Rating:
# Table of contents
- ## [Crackme](#challenge-name-crackme)
---
# Notes
### For some of the challenges, a rinkeby testnet wallet account is required. Make sure to get one before doing.
### I'm using Ubuntu 20.04 as my environment.
---
# Challenge name: Crackme
![Question screenshot](../images/crackme.png)
## Information
Contract address: ```0xDb2F21c03Efb692b65feE7c4B5D7614531DC45BE```
> crackme.sol
```
pragma solidity ^0.6.0;
contract crack_me{
function gib_flag(uint arg1, string memory arg2, uint arg3) public view returns (uint[]){
//arg3 is a overflow
require(arg3 > 0, "positive nums only baby");
if ((arg1 ^ 0x70) == 20) {
if(keccak256(bytes(decrypt(arg2))) == keccak256(bytes("offshift ftw"))) {
uint256 check3 = arg3 + 1;
if( check3< 1) {
return flag;
}
}
}
return "you lost babe";
}
function decrypt(string memory encrypted_text) private pure returns (string memory){
uint256 length = bytes(encrypted_text).length;
for (uint i = 0; i < length; i++) {
byte char = bytes(encrypted_text)[i];
assembly {
char := byte(0,char)
if and(gt(char,0x60), lt(char,0x6E))
{ char:= add(0x7B, sub(char,0x61)) }
if iszero(eq(char, 0x20))
{mstore8(add(add(encrypted_text,0x20), mul(i,1)), sub(char,16))}
}
}
return encrypted_text;
}
}
```
Personally i think this is similar to a reverse-engineer challenge. I need to call the contract's ```gib_flag()``` function to get the flag.
---
gib_flag | arg1 | arg2 | arg3
---------|------|------|-----
condition | user_input ^ 0x70 == 20 |decrypt(user_input) == "offshift ftw"| user_input + 1 < 0
correct input|Dec: 100 or Hex: 0x64|"evvixyvj vjm"|2**256 - 1 (Will cause overflow after += 1)
---
## My solution
1. using python's brownie library
2. run ```brownie init crackme_ctf```
3. copy ```chall.sol``` into the generated ```contracts/``` folder
4. do some modification to the ```chall.sol``` to make it compilable
```
> crackme_ctf/contracts/chall.sol
pragma solidity ^0.6.0;
contract crack_me{
string flag = "yeah"; // <- i added this line
function gib_flag(uint arg1, string memory arg2, uint arg3) public view returns (string memory){
//arg3 is a overflow
require(arg3 > 0, "positive nums only baby");
.
.
.
```
5. copy the entire code from ```chall.sol``` to another ```test.sol``` file in ```contracts/```
> crackme_ctf/contracts/test.sol
```
pragma solidity ^0.6.0;
contract hello{ // <- remember to change the contract name
function gib_flag(uint arg1, string memory arg2, uint arg3) public view returns (string memory){
//arg3 is a overflow
require(arg3 > 0, "positive nums only baby");
if ((arg1 ^ 0x70) == 20) {
if(keccak256(bytes(decrypt(arg2))) == keccak256(bytes("offshift ftw"))) {
uint256 check3 = arg3 + 1;
if( check3< 1) {
return "yeah you reversed it"; // <- replaced the flag variable with a string
}
}
}
return "you lost babe";
}
// *** remember to change this function to public
function decrypt(string memory encrypted_text) public pure returns (string memory){
uint256 length = bytes(encrypted_text).length;
for (uint i = 0; i < length; i++) {
byte char = bytes(encrypted_text)[i];
assembly {
char := byte(0,char)
if and(gt(char,0x60), lt(char,0x6E))
{ char:= add(0x7B, sub(char,0x61)) }
if iszero(eq(char, 0x20))
{mstore8(add(add(encrypted_text,0x20), mul(i,1)), sub(char,16))}
}
}
return encrypted_text;
}
}
```
6. write a script to bruteforce the value for arg3
> crackme_ctf/scripts/test.py
```
from brownie import *
def main():
accounts.add() # Create a new local wallet account
d = hello.deploy({"from":accounts[0]})
target = "offshift ftw"
stored = ""
# Valid ascii characters
for _ in range(32,128):
stored += str(chr(_))
ans = ""
for x in range(len(target)):
for c in range(128):
tmp = d.decrypt(stored[c])
if(tmp == target[x]):
ans += stored[c]
break
print(ans)
```
```
Output of: $ brownie run scripts/test.py
Brownie v1.13.0 - Python development framework for Ethereum
CrackmeCtfProject is the active project.
Launching 'ganache-cli --port 8545 --gasLimit 12000000 --accounts 10 --hardfork istanbul --mnemonic brownie'...
Running 'scripts/test.py::main'...
mnemonic: 'piece multiply pet next panel off special sketch dose illness domain naive'
Transaction sent: 0x128e7a11fc664a3592afdb4db2550d3b368ccc2a95b8f0f6bea8a8442350eb64
Gas price: 0.0 gwei Gas limit: 12000000 Nonce: 0
hello.constructor confirmed - Block: 1 Gas used: 287601 (2.40%)
hello deployed at: 0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87
evvixyvj vjm <- user_input for arg3
Terminating local RPC client...
```
7. ```$ brownie console --network rinkeby```
```
Brownie v1.13.0 - Python development framework for Ethereum
CrackmeCtfProject is the active project.
Brownie environment is ready.
>>> myWallet = accounts.add(YOUR_OWN_RINKEBY_NETWORK_WALLET_PRIVATE_KEY)
>>> d = web3.eth.contract(
... address="0xDb2F21c03Efb692b65feE7c4B5D7614531DC45BE",
... abi=crack_me.abi
... )
>>> d.functions.gib_flag(100,"evvixyvj vjm",2**256-1).call()
```
8. Process the error manually
```
File "<console>", line 1, in <module>
File "web3/contract.py", line 954, in call
return call_contract_function(
File "web3/contract.py", line 1529, in call_contract_function
raise BadFunctionCallOutput(msg) from e
BadFunctionCallOutput: Could not decode contract function call gib_flag return data b'\x00\x00\x00.......\x00\x00\x004' for output_types ['string']
>>> return_data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0
0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00n\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00g\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x
00\x00\x00\x00\x00\x00\x00\x00\x00r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0
0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x007\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
x005\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00_\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x
00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x0
0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00u\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
x00\x00\x00\x00\x00_\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x
00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00R\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x0
0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
x00\x00\x00\x00\x00\x00\x00\x00\x00K\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x
00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00_\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00m\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x008\x00\x00\x00\x00\x00\x00\x
00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x004"
>>> for a in return_data:
... if(a != '\x00'):
... print(a,end='')
...
C0ngr@75_Y0u_CR@CK3D_m3854>>>
```
## Flag: flag{C0ngr@75_Y0u_CR@CK3D_m3854}
---