forked from paradigmxyz/paradigm-ctf-2021
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit c84e730
Showing
169 changed files
with
8,509 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__pycache__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# CTF Data | ||
|
||
## Installing | ||
|
||
### Prerequisites | ||
|
||
* Docker | ||
* [solc-select](https://github.com/crytic/solc-select) | ||
* [mpwn](https://github.com/lunixbochs/mpwn) | ||
* Python 3 | ||
|
||
### Configuration | ||
|
||
You'll need to update the following environment variables: | ||
* `RPC_URL` to a valid Ethereum JSON-RPC endpoint | ||
* `PYTHONPATH` to point to mpwn | ||
|
||
You'll also need to manually install the following: | ||
* `solc-select install 0.4.16 0.4.24 0.5.12 0.6.12 0.7.0 0.7.6 0.8.0` | ||
* `pip install ecdsa sha3` | ||
|
||
### Build everything | ||
|
||
```bash | ||
./build | ||
``` | ||
|
||
### Run a challenge | ||
|
||
Running a challenge will open a port which users will `nc` to. For Ethereum related | ||
challenges, an additional port must be supplied so that users can connect to the Ethereum | ||
node (which forks from mainnet state) | ||
|
||
``` | ||
./run babycrypto 31337 | ||
``` | ||
|
||
On another terminal: | ||
|
||
``` | ||
nc localhost 31337 | ||
``` | ||
|
||
For ETH challenges: | ||
|
||
``` | ||
./run bank 31337 8545 | ||
``` | ||
|
||
## Running the autosolver | ||
|
||
```bash | ||
./solve | ||
``` | ||
|
||
## Add a new challenge | ||
|
||
1. Copy one of the existing challenge directories and rename it to your challenge's name | ||
1. Edit the `info.yaml` to add your details | ||
1. Add your contracts under the `public/contracts` directory | ||
1. Add any contracts which are supposed to be private, such as the source code for a rev challenge | ||
or a challenge solution under the `contracts/private` directory | ||
1. Add it to the build script with the dirname and compiler version | ||
1. (Optional) Allow it to be auto-solved: | ||
1. Do either of the following: | ||
* Add an `private/Exploit.sol` file with a `constructor(Setup setup)` constructor that solves the challenge | ||
* Add a `private/solve.py` if it requires additional actions to be executed (e.g. babycrypto, vault) | ||
1. Then add it to the `solve` script | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
name: babycrypto | ||
author: samczsun | ||
flag: PCTF{5H0uldve_Re4d_rfC_6979} | ||
tags: ["crypto"] | ||
description: | | ||
I've written a super simple program to sign some data. Hopefully I didn't mess anything up! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
from mp import * | ||
|
||
from ecdsa import ecdsa | ||
from ecdsa.numbertheory import inverse_mod | ||
import sha3 | ||
import binascii | ||
|
||
def hash_message(msg: str) -> int: | ||
""" | ||
hash the message using keccak256, truncate if necessary | ||
""" | ||
k = sha3.keccak_256() | ||
k.update(msg.encode('utf8')) | ||
d = k.digest() | ||
n = int(binascii.hexlify(d), 16) | ||
olen = ecdsa.generator_secp256k1.order().bit_length() or 1 | ||
dlen = len(d) | ||
n >>= max(0, dlen - olen) | ||
return n | ||
|
||
REMOTE_IP = os.getenv("REMOTE_IP") | ||
REMOTE_PORT = os.getenv("REMOTE_PORT") | ||
|
||
p = remote(REMOTE_IP, int(REMOTE_PORT)) | ||
|
||
# p = process('python3', 'chal.py') | ||
|
||
p >> 'message? ' << 'message1\n' >> 'r=' | ||
r1 = int(p.recvline(), 16) | ||
p >> 's=' | ||
s1 = int(p.recvline(), 16) | ||
|
||
p >> 'message? ' << 'message2\n' >> 'r=' | ||
r2 = int(p.recvline(), 16) | ||
p >> 's=' | ||
s2 = int(p.recvline(), 16) | ||
|
||
p << 'a\nb\n' >> "test=" | ||
|
||
test = int(p.recvline(), 16) | ||
print("test", hex(test)) | ||
print("s1", hex(s1)) | ||
print("s2", hex(s2)) | ||
|
||
assert r1 == r2 | ||
|
||
h1 = hash_message("message1") | ||
h2 = hash_message("message2") | ||
|
||
for v in ( | ||
s1 - s2, | ||
s1 + s2, | ||
-s1 - s2, | ||
-s1 + s2 | ||
): | ||
k = inverse_mod(v, ecdsa.generator_secp256k1.order()) * (h1 - h2) % ecdsa.generator_secp256k1.order() | ||
d = inverse_mod(r1, ecdsa.generator_secp256k1.order()) * (k*s1 - h1) % ecdsa.generator_secp256k1.order() | ||
|
||
g = ecdsa.generator_secp256k1 | ||
pub = ecdsa.Public_key(g, g * d) | ||
priv = ecdsa.Private_key(pub, d) | ||
if pub.verifies(h1, ecdsa.Signature(r1, s1)): | ||
break | ||
|
||
sig = priv.sign(test, 1) | ||
p >> 'r? ' << hex(sig.r) << '\n' | ||
p >> 's? ' << hex(sig.s) << '\n' | ||
|
||
flag = p.recvline().decode('utf8') | ||
print("got flag", str(flag)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
FROM ctf/challenge_base:latest | ||
|
||
COPY deploy/ /home/ctf/ | ||
|
||
RUN python3 -m pip install -r /home/ctf/requirements.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
from random import SystemRandom | ||
from ecdsa import ecdsa | ||
import sha3 | ||
import binascii | ||
from typing import Tuple | ||
import uuid | ||
import os | ||
|
||
|
||
def gen_keypair() -> Tuple[ecdsa.Private_key, ecdsa.Public_key]: | ||
""" | ||
generate a new ecdsa keypair | ||
""" | ||
g = ecdsa.generator_secp256k1 | ||
d = SystemRandom().randrange(1, g.order()) | ||
pub = ecdsa.Public_key(g, g * d) | ||
priv = ecdsa.Private_key(pub, d) | ||
return priv, pub | ||
|
||
|
||
def gen_session_secret() -> int: | ||
""" | ||
generate a random 32 byte session secret | ||
""" | ||
with open("/dev/urandom", "rb") as rnd: | ||
seed1 = int(binascii.hexlify(rnd.read(32)), 16) | ||
seed2 = int(binascii.hexlify(rnd.read(32)), 16) | ||
return seed1 ^ seed2 | ||
|
||
|
||
def hash_message(msg: str) -> int: | ||
""" | ||
hash the message using keccak256, truncate if necessary | ||
""" | ||
k = sha3.keccak_256() | ||
k.update(msg.encode("utf8")) | ||
d = k.digest() | ||
n = int(binascii.hexlify(d), 16) | ||
olen = ecdsa.generator_secp256k1.order().bit_length() or 1 | ||
dlen = len(d) | ||
n >>= max(0, dlen - olen) | ||
return n | ||
|
||
|
||
if __name__ == "__main__": | ||
flag = os.getenv("FLAG", "PCTF{placeholder}") | ||
|
||
priv, pub = gen_keypair() | ||
session_secret = gen_session_secret() | ||
|
||
for _ in range(4): | ||
message = input("message? ") | ||
hashed = hash_message(message) | ||
sig = priv.sign(hashed, session_secret) | ||
print(f"r=0x{sig.r:032x}") | ||
print(f"s=0x{sig.s:032x}") | ||
|
||
test = hash_message(uuid.uuid4().hex) | ||
print(f"test=0x{test:032x}") | ||
|
||
r = int(input("r? "), 16) | ||
s = int(input("s? "), 16) | ||
|
||
if not pub.verifies(test, ecdsa.Signature(r, s)): | ||
print("better luck next time") | ||
exit(1) | ||
|
||
print(flag) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
ecdsa==0.16.1 | ||
pysha3==1.0.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
name: babyrev | ||
author: samczsun | ||
flag: PCTF{7URn5_0u7_i7_w45n7_53cur3_4F73R_4ll} | ||
tags: ["rev"] | ||
description: | | ||
If I don't verify my source code, then hackers can't exploit my contract, right? |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
pragma solidity 0.4.24; | ||
|
||
contract Challenge { | ||
bytes public constant encrypted = "\x31\x1d\xfa\x54\x51\x96\x3f\x33\xb1\x6e\x63\xf0\xc6\x22\x78\xc9\xb9\x07\xe4\x3d\x19\x61\xcd\xf9\xf5\x90\xa0\xc3\xb3\x51\xc0\x40\x19\xcc\xcb\x83\x14\x03"; | ||
bytes public constant decrypted = "PCTF{v32y_53cu23_3nc2yp710n_4190217hm}"; | ||
|
||
bool public solved; | ||
|
||
function solve(uint256 seed) public { | ||
require(encrypted.length == decrypted.length); | ||
|
||
uint8[256] memory sbox = [ | ||
//0 1 2 3 4 5 6 7 8 9 A B C D E F | ||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, | ||
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, | ||
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, | ||
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, | ||
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, | ||
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, | ||
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, | ||
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, | ||
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, | ||
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, | ||
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, | ||
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, | ||
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, | ||
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, | ||
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, | ||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 | ||
]; | ||
|
||
bytes memory work = encrypted; | ||
for (uint i = 0; i < work.length; i++) { | ||
work[i] ^= byte(uint8(seed)); | ||
seed = rewriteSeed(sbox, seed); | ||
} | ||
|
||
require(keccak256(abi.encodePacked(work)) == keccak256(abi.encodePacked(decrypted)), "solve/not-solved"); | ||
|
||
solved = true; | ||
} | ||
|
||
function rewriteSeed(uint8[256] memory sbox, uint256 seed) private pure returns (uint256) { | ||
uint256 newSeed; | ||
|
||
for (uint i = 0; i < 256; i += 8) { | ||
newSeed |= uint256(sbox[((seed >> i) & 0xFF)]) << i; | ||
} | ||
|
||
uint256 lastByte = newSeed & 0xff; | ||
newSeed >>= 8; | ||
newSeed |= lastByte << (256 - 8); | ||
|
||
return newSeed; | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
pragma solidity 0.4.24; | ||
|
||
import "public/Setup.sol"; | ||
|
||
contract ChallengeSolver { | ||
function solve(uint seed) public; | ||
} | ||
|
||
contract Exploit { | ||
constructor(Setup setup) public payable { | ||
ChallengeSolver chal = ChallengeSolver(address(setup.challenge())); | ||
|
||
chal.solve(0x1ca71c40cb573a1f120ec468c36dde2923591766033254683cab7d34575a9d61); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import os | ||
|
||
Sbox = ( | ||
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, | ||
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, | ||
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, | ||
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, | ||
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, | ||
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, | ||
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, | ||
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, | ||
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, | ||
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, | ||
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, | ||
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, | ||
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, | ||
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, | ||
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, | ||
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 | ||
) | ||
|
||
SEED_BYTES = 32 | ||
|
||
def next_seed(seed): | ||
new_seed = 0 | ||
for i in range(SEED_BYTES): | ||
off = i*8 | ||
new_seed |= Sbox[((seed >> off) & 0xFF)] << off | ||
|
||
first_byte = new_seed & 0xFF | ||
new_seed >>= 8 | ||
new_seed |= (first_byte << (SEED_BYTES*8 - 8)) | ||
|
||
return new_seed | ||
|
||
def transform(plaintext, seed): | ||
ciphertext = list(map(ord, plaintext)) | ||
for i in range(len(plaintext)): | ||
ciphertext[i] ^= (seed & 0xFF) | ||
seed = next_seed(seed) | ||
|
||
return ''.join(list(map(chr, ciphertext))) | ||
|
||
decrypted = "PCTF{v32y_53cu23_3nc2yp710n_4190217hm}" | ||
|
||
# generate it | ||
with open("/dev/urandom", "rb") as f: | ||
seed = int.from_bytes(f.read(SEED_BYTES), byteorder='big') | ||
|
||
encrypted = transform(decrypted, seed) | ||
|
||
print("".join([f"\\x{ord(c):02x}" for c in encrypted])) | ||
|
||
# solve it | ||
seed = 0 | ||
for off in range(SEED_BYTES): | ||
for i in range(0, 256): | ||
new_seed = seed | (i << off * 8) | ||
|
||
new_value = transform(encrypted, new_seed) | ||
if new_value[off] == decrypted[off]: | ||
seed = new_seed | ||
break | ||
|
||
print(hex(seed)) | ||
print(repr(transform(encrypted, seed))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
FROM ctf/eth_challenge_base:latest | ||
|
||
COPY deploy/ /home/ctf/ | ||
|
||
RUN python3 -m pip install -r /home/ctf/requirements.txt |
Oops, something went wrong.