blindless
1 | GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31. |
1 | main: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=8edd548324d994d7d9ceaba18aea6a9f0daf7c7c, for GNU/Linux 3.2.0, with debug_info, not stripped |
- 64位,dynamically,全开
提示文件如下:
1 | 0x5625ff3e2000 0x5625ff3e3000 r--p 1000 0 /home/ctf/pwn |
漏洞分析
局部写漏洞:
1 | else if ( c.key == '.' ) |
入侵思路
申请一片大空间,使得 malloc 调用 mmap:
1 | data = (char *)malloc(sizea); |
计算偏移使得 data 指向 exit_hook
1 | if ( c.key == '@' ) |
利用局部写漏洞覆盖 exit_hook 低位为 one_gadget,爆破3位出 flag(概率为 1/4096)
完整 exp 如下:
1 | # -*- coding:utf-8 -*- |
jit
1 | jit: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=7062b1b0edff968d09e012e0905c2e5b7276cbd4, for GNU/Linux 3.2.0, stripped |
- 64位,dynamically,全开
程序分析
本题目实现了一个虚拟机,在地址为 0x4240 的地方开始加载字节码 ,支持4种内置的函数调用,大致格式如下:
1 | [opcode][原寄存器的值][目标寄存器的值][data len][data] |
程序在 0x31B0 的代码会对之前部署的字节码进行解析,并将其转化为 x86 能理解的二进制代码并写入一片由 mmap 申请的内存空间
下面就是 mov 指令的实现:(包括直接寻址和间接寻址)
1 | case 0xB7: |
1 | case 0xBF: |
经过调试测试,各个寄存器对应的数值如下:
reg | num |
---|---|
rax | 0 |
rdi | 1 |
rsi | 2 |
rdx | 3 |
r9 | 4 |
r8 | 5 |
rbx | 6 |
r13 | 7 |
r14 | 8 |
r15 | 9 |
- PS:使用 opcode 决定该寄存器的占位大小(byte,word,dword,qword)
入侵思路
本题目完全是依靠程序本身的功能进行入侵
可以构建合适的汇编代码来获取 [rdi+0x58]
处的 libc 地址,计算偏移将其改为 system
1 | def cmd(opcode,reg_op2,reg_op1,data1,data2): |
经调试发现,程序的“内置函数”就记录在 heap 中
比赛时的入侵思路就是往“内置函数”中写入 system(程序通过 offset 来决定具体调用哪个“内置函数”,但并没有对 offset 进行限制)
1 | 2b:0158│ 0x55e26ce6ba58 ◂— 0x211 |
最后发现写入的 system 不起作用,不管是添加到 func_list 后还是覆盖已有的值都没用
可能是 func_list 的位置没有找对,因为在 search 时发现多个地址都存在 func_list:
1 | pwndbg> search -t qword 0x55c891fd0710 |
可以选择去挨个覆盖这些地址,也可以直接去覆盖 free_hook,这里采用了后者
完整 exp 如下:
1 | # -*- coding:utf-8 -*- |