To answer your question, you need to differentiate between Contracts and Proposals.
The DAO is a Contract that codifies rules to make funds available via Proposals.
A proposal could be a request for investment, or a request to withdraw your own funds (this is called Split Proposal). When a split proposal is approved, it calls splitDAO API to create a child DAO (child contract). The code of this child contract is indeed identical to the original DAO.
The splitDAO function transfers the requested funds to the child DAO. But it also calls a default function provided by the proposal, ostensibly to handle the funds being transferred. And this function is called right after the fund-transfer operation, but before the funds are deducted from the total balance of DAO and before user balance is set to zero.
The recursive exploit was embedded in this default function. It simply called splitDAO again.
In reality, the transfer of funds, reduction of total balance and setting user balance to zero should have been an atomic operation. Other methods (locks, mutexes) could also have been implemented to ensure the entire splitting operation is completed before any external, user-provided code is executed.
split
function that was exploited.