¿Cómo se creó la recursividad que condujo al hackeo de DAO?

Entiendo que si el contrato DAO tiene una función de retiro que envía dinero al contrato X, el contrato X puede ser malicioso y usar la función alternativa para volver a llamar a la función de retiro. Sin embargo, en el caso del contrato DAO, X era otra copia de DAO, por lo que no tuvo este comportamiento malicioso.

Entonces, ¿cómo fue exactamente posible el envío recursivo?

Era la splitfunción que se explotaba.
Este artículo tiene una explicación de cómo funciona el truco de reingreso vessenes.com/…

Respuestas (2)

Esta es la línea de código problemática en la withdrawRewardForfunción:

    if (!rewardAccount.payOut(_account, reward)) <-- reentrant exploit
         throw;
    paidOut[_account] += reward;

El payOut llamará a la payoutfunción del destinatario:

function payOut(address _recipient, uint _amount) returns (bool) {
    ..
    if (_recipient.call.value(_amount)()) {
    ..
}

Si _recipientes un contrato, esto llamará a la función de respaldo del contrato . Este destinatario, a su vez, puede volver a llamar al contrato TheDao. Como withdrawRewardForno estaba protegido contra la reentrada, se permitió de nuevo. Esto significaba un pago continuo antes de que se permitiera que el código continuara paidOut[_account] += reward;y más allá.

El artículo More Ethereum Attacks: Race-To-Empty is the Real Deal , que fue escrito una semana antes del ataque, tiene un análisis más detallado de este tipo de pirateo y soluciones propuestas.

Si sucedió, ¿cómo se rompió esta recursividad antes del retiro total del saldo?
@AK La cantidad máxima de gas permitida limita la cantidad de veces que se puede llamar el contrato, consulte vessenes.com/deconstructing-thedao-attack-a-brief-code-tour para obtener más información

Para responder a su pregunta, debe diferenciar entre Contratos y Propuestas.

El DAO es un Contrato que codifica reglas para poner fondos a disposición a través de Propuestas .

Una propuesta puede ser una solicitud de inversión o una solicitud para retirar sus propios fondos (esto se denomina Propuesta dividida ). Cuando se aprueba una propuesta dividida, llama a la API splitDAO para crear un DAO secundario (contrato secundario). De hecho, el código de este contrato secundario es idéntico al DAO original.

La función splitDAO transfiere los fondos solicitados al DAO secundario. Pero también llama a una función predeterminada proporcionada por la propuesta , aparentemente para manejar los fondos que se transfieren. Y esta función se llama justo después de la operación de transferencia de fondos, pero antes de que los fondos se deduzcan del saldo total de DAO y antes de que el saldo del usuario se establezca en cero .

El exploit recursivo se incrustó en esta función predeterminada. Simplemente llamó a splitDAO de nuevo.

En realidad, la transferencia de fondos, la reducción del saldo total y la puesta a cero del saldo del usuario debería haber sido una operación atómica. También se podrían haber implementado otros métodos (bloqueos, exclusión mutua) para garantizar que toda la operación de división se complete antes de que se ejecute cualquier código externo proporcionado por el usuario.