Smart Contract Exploits Part 3 (Accounts)

More exciting twists in store as we explore fuzzy identity and account takeover.

by Caleb Lau


Contract Exploits Pt. 3 – Featuring Capture the Ether – Accounts

And now we have reached part 3! Both the Accounts and Miscellaneous challenges were written in part 3 initially; However does seem to be lengthy, reaching over 20 pages on the document I was working on, so there will be a part 4 for the Miscellaneous challenges section instead. The Accounts section centers on challenges surrounding Ethereum addresses and on blockchain use of Elliptic Curve cryptography.

For those who missed the first part: https://celebrusadvisory.com/smart-contract-exploits-part-1/
Whereas the second part is found here: https://celebrusadvisory.com/smart-contract-exploits-part-2/

The website where these challenges could be found: https://capturetheether.com/challenges/
And the author of these challenges is the very brilliant smarx, catch him on his twitter handle @smarx.

As before, this article will require some prior knowledge with Solidity and its surrounding dev tools.

Without further ado – Huge spoilers ahead!

 

 

13. Fuzzy Identity

 

Source code as below.

pragma solidity ^0.4.21;

interface IName {
    function name() external view returns (bytes32);
}

contract FuzzyIdentityChallenge {
    bool public isComplete;

    function authenticate() public {
        require(isSmarx(msg.sender));
        require(isBadCode(msg.sender));

        isComplete = true;
    }

    function isSmarx(address addr) internal view returns (bool) {
        return IName(addr).name() == bytes32("smarx");
    }

    function isBadCode(address _addr) internal pure returns (bool) {
        bytes20 addr = bytes20(_addr);
        bytes20 id = hex"000000000000000000000000000000000badc0de";
        bytes20 mask = hex"000000000000000000000000000000000fffffff";

        for (uint256 i = 0; i < 34; i++) {
            if (addr & mask == id) {
                return true;
            }
            mask <<= 4;
            id <<= 4;
        }

        return false;
    }
}

 

Here we are required to have isComplete set to true, by passing both isSmarx() and isBadCode() conditions. isSmarx() calls an interface, which requires the calling address to somehow return the value “smarx” in the context of a function name(), therefore will require a smart contract to call the authenticate function. isBadCode() on the other hand takes the calling address and checks if the address contains the bytes “badc0de”.

We need to do two things then: create a smart contract consisting of the function name() which returns “smarx”, and ensure the contract has “badc0de” as part of its address hex.

The first is simple enough – Prepare a contract which has implements the function name(). Don’t forget to also implement a call to the function authenticate() of the Fuzzy Identifier exploit contract, as the contract needs to be the msg.sender.

Implementing our exploit contract.

The second needs a bit of brute forcing and knowledge of how Ethereum generates contract addresses. As previously mentioned on part 2, contract addresses are generated deterministically in Ethereum with the rightmost 160 bits of the keccak256 result of the sender address and nonce in RLP encoding. We could quite quickly cook up a piece of Solidity code which does this, deploy using Nethereum with Ganache to randomly generate a bunch of addresses and test a couple of nonce to see which address + nonce fulfills our requirements. Don’t forget to save the private key of the address, and keep track of the nonce!

The piece of Solidity code basically runs keccak256 with the address and nonce in RLP encoding:

Generating contract address using Solidity.

And the VB.NET code, which deploys the contract, then starts generating random addresses and increment nonce, passing them to the contract and checking the returned results:

The code – increments nonce 10 times then moves to another address, repeating the same.

Run it for a bit… Yes, it’s brute force. Might take a while. Took me a couple of hours at about 25 contract addresses per second:

And we found our address!

Now that we have everything prepared, using the private key of the account we know will eventually create a contract address with the hex “badc0de”, we will load the account address with some ETH, increment the nonce of the account on Ropsten testnet, until it eventually reaches our desired nonce, where we will then deploy the contract we have first prepared.

Notice the “badc0de”?

From there, using our exploit contract we call authenticate() on the Fuzzy Identifier challenge contract, fulfilling all conditions and completing this challenge.

 

 

14. Public Key

 

Source code as below.

pragma solidity ^0.4.21;

contract PublicKeyChallenge {
    address owner = 0x92b28647ae1f3264661f72fb2eb9625a89d88a31;
    bool public isComplete;

    function authenticate(bytes publicKey) public {
        require(address(keccak256(publicKey)) == owner);

        isComplete = true;
    }
}

 

This isn’t a smart contract exploit, rather tests one’s understanding of how a blockchain works, and how transactions are signed on a blockchain. When a transaction is signed, a signed transaction is generated, two ECDSA signature outputs are produced (r, s), and the recovery id (v). All these could be used to recover a public key of an address should there been a transaction sent from the address we would like to recover from.

Fortunately, the ethereumjs-tx library make it easy for us to do this without worrying about the underlying intricacies. We just need to get the raw transaction hex, which we could also easily get from Etherscan, so we do not need to attempt reconstructing the transaction from scratch.

Checking the address 0x92b28647ae1f3264661f72fb2eb9625a89d88a31, we indeed see a transaction was sent previously:

A previous sent transaction from 0x92b28647ae1f3264661f72fb2eb9625a89d88a31

Going to the top right corner of the Transaction Information section, click on Tools & Utilities, then click on “Get Raw Transaction Hex”. We are then presented with:

0xf87080843b9aca0083015f90946b477781b0e68031109f21887e6b5afeaaeb002b808c5468616e6b732c206d616e2129a0a5522718c0f95dde27f0827f55de836342ceda594d20458523dd71a539d52ad7a05710e64311d481764b5ae8ca691b05d14054782c7d489f3511a7abf2f5078962

 

The rest is straightforward, using ethereumjs-tx’s getSenderPublicKey function, we could easily retrieve the public key:

Using ethereumjs-tx library to obtain the public key.

Use the public key as an input parameter for the function authenticate, and we are done!

Or are we? Well this challenge is 750 points, thought I’d write a little bit more about it. What if Etherscan is momentarily down, and we desperately need to have the raw transaction hex? 😛

One of the ways is to use Geth synced with Ropsten, which exposes a method which we can call – eth.getRawTransaction – With the transaction hash as the input parameter, and we can get the raw transaction hex:

An example done through Geth console, not the actual hash – I don’t usually sync Ropsten.

But if we don’t want to sync Geth, sure, another way is to get the transaction details:

Using eth.getTransaction.

Reconstruct the raw transaction, and serialise the output in hex:

Or if you’re real hard to please, you could concatenate the transaction data together yourself. 🙂

From there the same takes place – Get the raw transaction hex string and pass it through getSenderPublicKey(), and we will get the same public key.

 

 

Account Takeover

 

Source code as below.

pragma solidity ^0.4.21;

contract AccountTakeoverChallenge {
    address owner = 0x6B477781b0e68031109f21887e6B5afEAaEB002b;
    bool public isComplete;

    function authenticate() public {
        require(msg.sender == owner);

        isComplete = true;
    }
}

 

Love this one. Again, this challenge isn’t exactly a smart contract exploit challenge, and I can imagine how some would find this challenge harder than the Fifty Years challenge, despite providing 500 points lesser. Everything which can be seen here in the contract is indeed fine, the address and its corresponding private key is properly generated, so we will need to further expand our scope and search for clues elsewhere.

We pivot towards searching for clues from the transactions sent: https://ropsten.etherscan.io/txs?a=0x6B477781b0e68031109f21887e6B5afEAaEB002b&p=2

Again, on the surface, nothing looks wrong. However, if we start digging into how the transactions are constructed:

Getting the transaction details with eth.getTransaction

Wait. Why is value r, an ECDSA signature output, which is supposed to be unique with each transaction reused here? This will be our exploit vector, and in fact was noticed in the wild as far back as 2013 (https://bitcointalk.to/index.php?topic=271486.0) in the blockchain space. Basically, r-values are to be generated using a random nonce k; However, there were mobile wallets using Android cryptographic libraries which ended up reusing k-values, in turn generating the same r-values across different transactions. This allows the private keys of the addresses to be derived from the signature outputs, resulting in stolen funds.

A bit of a trivia – Reusing the nonce k landed Sony into trouble during 2010: https://www.bbc.co.uk/news/technology-12116051. RFC6979 was later introduced during 2013 to allow deterministic generation of nonce k (hence helping with testing and environments with potentially unreliable PRNG).

Anyway, digression aside, let’s try to figure out how to get our private key for address 0x6B477781b0e68031109f21887e6B5afEAaEB002b.

We have the below values:

For txid 1: 0x061bf0b4b5fdb64ac475795e9bc5a3978f985919ce6747ce2cfbbcaccaf51009
s1 = 0x2bbd9c2a6285c2b43e728b17bda36a81653dd5f4612a2e0aefdb48043c5108de
z1 = 0x4f6a8370a435a27724bbc163419042d71b6dcbeb61c060cc6816cda93f57860c

For txid 2: 0xd79fc80e7b787802602f3317b7fe67765c14a7d40c3e0dcb266e63657f881396
s2 = 0x7724cedeb923f374bef4e05c97426a918123cc4fec7b07903839f12517e1b3c8
z2 = 0x350f3ee8007d817fbd7349c477507f923c4682b3e69bd1df5fbb93b39beb1e04

Common r value: 0x69a726edfb4b802cbf267d5fd1dabcea39d3d7b4bf62b9eeaeba387606167166

There is a method to derive private keys where the nonce k is reused with the values above, using the Python script uploaded here: https://gist.github.com/Enigmatic331/6dc71d727bf3ecfe6b425c5c1d298807

Script output.

Testing the private keys, we will find one of them allowing us access to address 0x6B477781b0e68031109f21887e6B5afEAaEB002b. From there, we call function authenticate() using the private keys to the aforementioned address, setting isComplete to true and completing the challenge.

Confirming the private key to the address using MyCrypto’s desktop app

But since this is 1500 points, it is only right if I write a little more. Perhaps a bit of insight to how the Python script is implemented. There is a real nice thread on StackExchange covering this as well: https://bitcoin.stackexchange.com/questions/37760/converting-ruby-script-into-python-recovering-private-key-when-someone-uses-th/37762#37762

An ECDSA signature s is computed using the below equation:

Where k is the nonce, z is the message digest, r is the ECSDA signature, privKey is the private key, and p is the prime order of the secp256k1 curve (being 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141).

From here we can derive for s1 and s2:

Remember all operations here are done modulo prime p, therefore (s1-s2)^(-1) is a modulo multiplicative inverse of prime p, which we could calculate using an implementation of Fermat’s Little Theorem as p is prime for this case, or alternatively the extended Euclidean algorithm.

Once we have k, we could then try to find privKey:

And the private key privKey should also be the same when derived from s2 and z2.

Calculating for the case of s1-s2 isn’t sufficient (though for this challenge we are fortunate enough to not have a flipped signature), as we could also use a flipped s signature -s (mod p), which would still be a valid signature output. Therefore preemptively, we would evaluate multiple k candidates (which there exists for each pair a negation of itself), so we could be looking at:

Where -s1-s2 and -s1+s2 we could derive using -k (mod p).

Now that we have all of these, we could implement these in code, plug in the values we have, and see what appears. Everything below will be implemented in Python, and since I am lazy let’s go with Fermat’s Little Theorem for the modulo multiplicative inverse (def inverse_mod):

Defining the r, s and z values, and the modulo inverse

Finding k:

Getting our k.

Deriving private key for k and -k (mod p).

Getting our private key.

Printing all k candidates and making sure the private keys matches:

Printing our private key.

We will also do the same for s1+s2, replacing the s1-s2 used to calculate the k candidate, and deriving the private keys in a similar fashion. So the whole code block will be:

Full Python code implementation.

Run the script, test which private key is the one to the address 0x6B477781b0e68031109f21887e6B5afEAaEB002b, and we can now solve this challenge.

 


 

For this part, instead of a conclusion, I will link straight to part 4 where we will complete the last two Capture the Ether challenges and wrap up: https://celebrusadvisory.com/smart-contract-exploits-part-4/

2018-09-23T12:01:47+00:00 September 22nd, 2018|Innovation|

Leave A Comment