atuo_coffee_sale_machine
1 | GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31. |
1 | pwn: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=b65a1033a56d36412b5e4993b0c7f4f4f2e685bf, for GNU/Linux 3.2.0, not stripped |
- 64位,dynamically,Partial RELRO,Canary,NX
漏洞分析
题目维护了两个数组 copy_left_coffee
和 left_coffee
,分别存放 user 和 root 状态下的数据
大多数函数在执行之前都会先根据当前状态进行切换,但 change_default
没有:
1 | show_list(); |
这意味着在 user 状态下释放 chunk 时,数据不会同步到 root 状态,这就造成了 UAF
入侵思路
程序中只有一个地方可以用来泄露:
1 | for ( i = 0; i <= 2; ++i ) |
劫持 tcachebin 为 coffee_list 就可以泄露 libc_base
最后劫持 tcachebin 为 free_hook,写入 system 即可
完整 exp 如下:
1 | # -*- coding:utf-8 -*- |
6502_proccessor
1 | GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.6) stable release version 2.27. |
1 | 6502_proccessor: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=6c78b755035efbfcec3230038685158aefa0d8cb, not stripped |
- 64位,dynamically,Partial RELRO,Canary,NX,PIE
程序分析
本题目实现了一个 6502 CPU 指令集的 VM
- 6502 CPU 指令集可以参考:6502 CPU汇编语言指令集 - WuSiYu Blog
6502 CPU 有3个8位寄存器:regA(累加器),regX(X变址寄存器),regY(Y变址寄存器)
题目中所有指令都由全局数组 lookup 进行管理,该数组的每个条目都被用于表示一个指令,其结构如下:
1 | 00000000 Node struc ; (sizeof=0x20, mappedto_18) |
每个指令结构体中都有两个函数,一个表示该指令的操作,另一个表示该指令的寻址方式
要利用的指令操作如下:
1 | __int64 LDA() /* 读取(从内存读到寄存器) */ |
1 | __int64 STA() /* 写入(从寄存器写入到内存) */ |
1 | __int64 ADC() /* 直接控制regA */ |
要利用的寻址操作如下:
1 | __int64 IZX() /* 基于regX的间接寻址 */ |
漏洞分析
函数 write_mem
有负数溢出漏洞:
1 | __int64 __fastcall write_mem(unsigned __int16 a1, char a2) |
- 注意
(__int16)(a1 - 512)
这段伪代码,这里有一个强制类型转换
虽然 IDA 分析 a1 是 unsigned __int16
,但从汇编指令来看可以分析出问题:
1 | .text:00000000000059E4 48 8B 15 0D 67 20 00 mov rdx, cs:mem_ptr |
- 实现强制类型转换的汇编代码为:
cwde cdqe
(符号扩展) - 因此 a1 应该是16位的有符号数
同样的漏洞也出现在 get_mem
中:
1 | unsigned __int8 __fastcall get_mem(unsigned __int16 cpu) |
入侵思路
本题目的核心点就是利用程序实现的 6502 CPU 指令来覆盖 puts_got 为 system
可以先使用 LDX 和 STX 往 regX 中写入 puts_got 的偏移,然后用 LDA 进行读取,计算步骤如下:
1 | pwndbg> distance $rebase(0x20A018) 0x559fe1e0c120 |
1 | In [5]: hex(0x10000-0x2108-0x206+0x200) |
测试样例如下:
1 | payload = b'' |
1 | ► 0x559fe1c05958 movzx eax, byte ptr [rdx + rax + 0x206] <puts@got.plt> |
读取 &puts 的值之后,我们可以使用 ADC 指令将 &puts 加为 &system:
1 | pwndbg> distance &puts &system |
最后用同样的方法往 regX 中写入 puts_got 的偏移,接着就可以使用 STA 覆盖 puts_got
每次覆盖1字节,连续执行3次后就可以将 puts 修改为 system
完整 exp 如下:
1 | # -*- coding:utf-8 -*- |
silent
1 | GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.5) stable release version 2.27. |
1 | silent: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=178287750053d8eedf914be6f97e8ab65e812b1b, not stripped |
- 64位,dynamically,Full RELRO,NX
1 | 0000: 0x20 0x00 0x00 0x00000004 A = arch |
漏洞分析
栈溢出:
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
入侵思路
首先我们需要一个 magic gadget:
1 | ➜ pwn2 ROPgadget --binary silent --depth 600 | grep "rbp - 0x3d" |
下面这段 gadget 是通过指令错位得来的:
1 | 0x00000000004007e8 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret |
- 将
[rbp - 0x3d]
中的数据加上ebx
- 由于我们可以控制
rbp
,因此这段 gadget 实现了 WAA
核心思路就是覆盖 stdout 上遗留的 libc_addr 为 puts
,完成泄露以后再覆盖回来写循环
下一次执行 main 就可以写入 ORW 链了
完整 exp:
1 | # -*- coding:utf-8 -*- |
babyheap
1 | GNU C Library (Ubuntu GLIBC 2.38-1ubuntu6) stable release version 2.38. |
1 | babyheap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=6b74874314aed94f7f0bb37f33a1aace975e2491, for GNU/Linux 3.2.0, stripped |
- 64位,dynamically,全开
漏洞分析
在 dele show edit
中的 chunk_list
和 size_list
有溢出:
1 | if ( index < 0x11 ) |
有 off-by-one 漏洞:
1 | for ( i = 0; i < size; ++i ) |
入侵思路
利用 off-by-one 可以打 unlink attack,进而泄露 heap_base 和 libc_base:
1 | ru("easier\n") |
接下来就可以劫持 tcache,进而劫持 IO_list_all
最后打 house of cat 就可以了(这里需要整理一下堆风水,以便 /bin/sh
的写入)
完整 exp 如下:
1 | # -*- coding:utf-8 -*- |