CTF PWN
调试时去除alarm函数:使用16进制编辑器,将所有的alarm改成 isnan
或者使用命令
1 | sed -i s/alarm/isnan/g <二进制文件> |
slient
开启PIE和NX保护,漏洞点是一个栈溢出。
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
没有puts,write等可以进行泄露的函数。思路为将某些函数的got表读入bss段,使用ret2csu。
- 读got表需要gadget至少存在可以修改地址内容片段,形如
mov [xxx], xxx
。用于取值,而且需要我们可以控制寄存器的内容
1 | $ ROPgadget --binary silent |
- 栈溢出,使用ret2csu,向bss段读入ROP chain。
- 栈迁移,转到bss段的ROPchain执行
- 使用magic 修改read_got 表内容为syscall
- 泄露出libc_base
- 继续read ROPchian,栈迁移执行ROPchain
需要注意
- 取magic gadget中的ebx时,如果ebx的值为正,则直接取,如果为负,加0x100000000取补码。
- 控制rax,使用函数返回值是rax来控制
- 栈迁移的重点是控制rsp,也可以使用 pop_rsp 直接控制。
最终的exp
- 调试二三十次才明白,
但是很快就会忘。
1 | #!/usr/bin/python3 |
atuo_coffee_sale_machine
coffee_list: 存储咖啡信息。
两个coffee_left数组,分别是 user 和 admin,是一个 3*7
数组,id和position
user
- 购买,输入id,按照pos顺序进行free
- 查看,打印出 coffee_list 信息
admin
- replenish:先更新admin coffee_list,然后更新 user coffee_left
- change_default,输入id和pos更新内容,然后更新user coffee_left
存在两个问题,都可以进行泄露和 get shell
- admin在 change_default 使,没有先进行update,直接read会导致uaf问题
- 数组underflow,因为读入的id, pos 没有判断是否小于0。
exp如下
- 由于change_default存在更新,容易导致double free 和 无法 replenish 的错误,我们需要中途更新一下admin coffee_left。(
菜鸡的眼泪
1 | #!/usr/bin/python3 |
babyheap
出题人很好心的给出了一个堆地址,这样就可以在堆合并时,绕过unlink_chunk的assert
1 | if (__builtin_expect(fd->bk != p || bk->fd != p, 0)) |
问题出现在 读取数据中,存在一个堆溢出写0
1 | unsigned __int64 __fastcall read_con(char *ptr, int a2) |
我们size的限制,这个大小的bin为 tcache, unsorted, large bin
1 | size > 0x3FF && size <= 0x500 |
泄露出地址,large bin attack
,使用house of apple 手段。
- a-b-c,在a伪造一个堆,但是因为使用puts函数进行show,但是会存在
\x00
截断问题,可以将a伪造成一个 free_chunk,并且会因为arena地址无法leak成功。因此需要堆风水一下,让large bin arena最后一字节不为0。 - free b, a-b 合并,malloc d, a & d指向同一个chunk,就可以有类似uaf的效果。
- large bin attack 的手段: free a, a放入largebin 里,利用d修改a的
bk_nextsize
为io_list_all-0x20
,释放一个比a
size小的chunke
,将e
放入large bin里。这样io_list_all => heap a
- house_of_apple:利用d修改a内容,伪造一个
fake_io
- 退出,IO流,并且此题没有沙箱
house of apple 的exp如下
- 但是这是在我kali的glibc 2.37 下进行的,在libc2.38 patch后会因为arena最后一个字节为0而无法成功,😫,不想风水了
- 最后凑一个
rdi/flag
。
1 | #!/usr/bin/python3 |
主要利用large bin leak,但是因为其可以使用tcache bin,看到了其他的利用手段
- 最终tcache修改TLS,通过__call_tls_dtors函数实现system(“/bin/sh”)
- IO leak 栈地址,然后跳转到栈上进行指向函数。
tls_dtor
一种比较简单的利用手段,正常情况下存在如下的调用链
1 | exit |
其函数实现如下
- 一个全局变量是否存在
- 找到其函数指针
PTR_DEMANGLE
计算函数地址- 调用函数
1 | void |
其结构体如下
1 | typedef void (*dtor_func) (void *); |
PTR_DEMANGLE 这个宏计算函数地址
- 循环右移0x11位
- 与 pointer_guard 进行异或
1 |
正常情况下,这个值为0,不会调用如下的函数
1 | pwndbg> p tls_dtor_list |
因为这里没有什么检查,因此我们可以攻击这个值,让其指向我们伪造的一个 dtor_list
结构体。
- func 首先获得地址,先于 pointer_guard 进行异或,然后在进行循环左移11位
- obj 作为函数参数指针。
在汇编中
- tls_dtor_list 也在 fs 附近
1 | pwndbg> set tls_dtor_list=1 # 进入循环,寻找偏移 |
我们需要将伪造的结构体中函数地址进行一个移位运算
1 | def rol(addr): |
一个小demo,来自 glibc2.35-通过tls_dtor_list劫持exit执行流程
1 |
|
在高版本 的 tcache bin attack 里也可以使用这个。
- tcache fd 加密问题。
如下为 chunk 放入 tcache bin 相关的函数。
- 因为 header 的存在,第一步将 chunk 转化成 tcache_entry
- key 为一个随机数
- next指针计算,next指针的地址右移12位,然后与
prev tcache bin
指针进行xor
运算。
1 |
|
可以做一个测试,查看tcache 的next指针。
1 | pwndbg> x/20xg 0x555555559290 |
因此我们伪造fd。
1 | (addr >> 12) ^ pos |