复健。
WEEK1
pwn
easy_overflow
确实easy,直接构造栈溢出即可,要注意后门函数地址+1。
exp如下:
1 | from pwn import * |
得到shell后进行一波重定位即可:
1 | exec 1>&2 |
choose_the_seat
典型的负整数越界利用,由未检查下界导致。
- 漏洞允许我们更改下标指向
0x10内容,且程序可劫持got表。
1 | Arch: amd64-64-little |
- 第一步,劫持
exit为vuln函数。 - 第二步,泄露任意一处
got表地址,即泄露libc。 - 第三步,劫持
puts为system,顺便写入/bin/sh。
exp如下:
1 | from pwn import * |
orw
蛮有意思的一道orw题,巩固了一些知识点。
简单的栈溢出,开了沙箱,禁了system:
1 | line CODE JT JF K |
- 首先泄露
libc。 - 由于溢出的并不多,因此得想办法增强溢出能力。一开始想着走一步返回一步,但溢出的数量仍不足以一次性完成如
read(3,bss,0x30)这一系列操作。该题比较巧妙的一点是由于是read造成的溢出,我们其实无需控制rdi和rsi,只需增大rdx,并再次调用read即可。 - ps. 看了下官方
wp使用的是栈迁移和mprotect,感觉不如本方法简单。
1 | from pwn import * |
还需注意的点是:
open和open64是一个东西,当时libc里找半天,发现open64里有这么一句注释:
1 | Alternative name is '__open' |
- 使用
remote远程端口时,read的rdx参数有多大传多少,否则会导致下面send的数据被之前的read吃掉。
simple_shellcode
这种shellcode题目除了掌握基本的orw_shellcode外,要时刻关注执行shellcode前后的寄存器变化,根据寄存器写shellcode。
- 首先
shellcode有16个字节大小的限制,这也就意味着接下来我们要用16个字节大小限制的shellcode扩大漏洞利用能力。
- 调试并根据寄存器编写一下
shellcode,目标是写入更多数据到0xcafe0000这片空间并执行:
1 | shellcode1=asm(''' |
- 而后就是编写常规的
orw的shellcode,注意开头需要多写一些nop,否则写出的就不是目标flag,猜测是控制相关问题:
1 | shellcode2=asm(''' |
- 总的
exp如下:
1 | rom pwn import * |
re
easyasm
根据汇编找逻辑,分析完发现只是简单的异或:
1 | ; void __cdecl enc(char *p) |
exp如下:
1 | data=[0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e] |
easyenc
简单的加密手法,找到you are right简单分析即可,如果不是exe文件的话还想试试angr一把梭:
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
exp如下:
1 | data=[0x04, 0xFF, 0xFD, 0x09, 0x01, 0xF3, 0xB0, 0x00, |
encode
将一个数分开二次加密记录,一个计算%16的余数,另一个计算/16的整数。
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
exp如下:
1 | result=[8, 6, 7, 6, 1, 6, 13, 6, 5, 6, |
WEEK2
pwn
YukkuriSay
考察栈外的格式化漏洞,且read次数只有一次,说明一次只能修改一个指定地址。
- 第一次需要用来泄露+返回,将
buf充满,会通过print_str泄露出一个残留的栈地址:
- 不要
n,继续在buf上布置栈返回地址,使其返回到_start(返回main和vuln会报错),顺便泄露出libc。 - 由于
printf(str)最稳妥的修改方法是一个地址中的1字节+2字节(2+2需要爆破),因此不难想到劫持程序执行流为one_gadget,而返回地址__libc_start_main+???作为libc地址显然和one_gadget具有更好的相似性,在返回到__libc_start_main+???时可以观察到rdx=0和r15=0:
1 | 0xe3afe execve("/bin/sh", r15, r12) |
- 不难想到用第二个
gadget。 exp如下:
1 | from pwn import * |
editable_note
libc2.31下常规的uaf漏洞。
- add:
1 | unsigned __int64 add_note() |
- delete中出现
uaf漏洞:
1 | unsigned __int64 __fastcall delete_note(const char *a1) |
- edit:
1 | unsigned __int64 __fastcall edit_note(const char *a1) |
- show:
1 | unsigned __int64 __fastcall show_note(const char *a1) |
- 申请9个
unsortedbin大小的堆,释放前8个,其中7个进入tcachebin,1个进入unsortedbin,第9个堆用于避免被topchunk吸收。 - 打印得到
libc地址,计算__free_hook和system。 - 修改第6个堆的
fd指针指向__free_hook。 - 再申请2个堆,劫持
__free_hook为system。 exp如下:
1 | from pwn import * |
fast_note
libc2.23下的uaf漏洞,无edit功能,其他功能和上述一样,因此可以构造double free。
- 申请两个堆,第一个有
unsortedbin大小,释放第一个,泄露libc。 - 再申请两个数据大小为
0x60的堆,释放其中第一个,再释放第二个,再释放第一个,绕过检查,达成double free。 - 再申请一个数据大小为
0x60的堆,并修改其fd指向__malloc_hook-0x23处。 - 将
__malloc_hook-0x23处的堆块申请过来,修改__malloc_hook为one_gadget,必要时用realloc进行调整。 exp如下:
1 | from pwn import * |
new_fast_note
libc2.31下的uaf漏洞,无edit功能,其他功能和上述一样。由于引入了tcache,这里学习了一种新的double free利用手法house of botcake。参考文章
- 分配9个
unsortedbin大小的堆块,释放掉前7个,再申请一个保护堆块防止合并。
1 | pwndbg> bins |
- 释放掉第9个堆块。
1 | pwndbg> bins |
- 释放掉第8个堆块,触发
unsortedbin consolidate合并为一个大堆块。
1 | pwndbg> bins |
- 申请一个堆块(Tcache优先),再释放掉第9个堆块,此时两个指针分别位于
unsortedbin中和tcache中,完成了double free。
1 | pwndbg> bins |
- 只需申请一个比第9个堆块大的堆块,便能切割
unsortedbin,在第9个堆块处的fd指针留下__free_hook即可。
1 | tcachebins |
- 申请到
__free_hook为system,释放数据为/bin/sh的堆块即可。 exp如下:
1 | from pwn import * |
WEEK3
pwn
libc-2.32预备知识
- 引入了
-safe-linking(异或加密)机制,其核心思想是:将指针的地址右移12位再和指针本身异或,该操作在堆块进入和退出tcache bin时进行:
1 |
chunk放入tcache bin时,其fd指针会进行异或加密,其bk指针会放入tcache,以防double free。chunk从tcache bin被取出时,其fd指针进行反异或操作,其bk指针清零。- 以下图为例了解
-safe-linking:
- 当第一个
chunk进入tcache bin时,由于tcache此时为空,故fd = (0x559f965652a0 >> 12) ^ 0。 - 当第二个
chunk进入tcache bin时,fd = (0x559f965652a0 >> 12) ^ 0x559f965652a0。 - 第三个同理:
1 | hex((0x559f965652a0 >> 12) ^ 0) |
- 不难想到
(0x559f965652a0 >> 12)就是第一次free chunk的fd指针,一直参与异或,这也是有些师傅们常说的key。
safe_note
libc-2.32环境下带有edit功能的uaf漏洞题目,除了环境不同,其余功能均与WEEK2-pwn-editable_note一致。
- 申请9个
0x80大小堆块,释放7个装满tcache bin,再释放一个进入unsorted bin,泄露libc_base即可。注:puts函数可能被低位‘\x00’截断,可以用edit进行调整。 - 泄露第一个
free chunk的fd指针即key,将key与__free_hook异或,加密结果在取出时便会得到真实的__free_hook地址。 - 修改第七个
free_chunk的fd指针为__free_hook加密结果,再申请两次堆块得到__free_hook地址的控制权,写入system即可。 exp如下:
1 | from pwn import * |
large_note
uaf题型,涉及libc2.32下的house of corruption利用,主要适用于申请largebin大小的chunk题型,利用house of corruption可以改大一个已知地址。
该题中add堆块的范围在0x500 ~ 0x900之间,释放的堆块无法进入tcache:
用于利用实现house of corruption的代码如下:
1 | if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk)){ |
- 申请一个
0x520大小的堆块,再申请一个0x500大小的堆块避免合并,再申请一个0x510大小的堆块用于后续触发上述代码(需小于第一个堆块)。 - 释放掉第一个堆块并泄露
libc基址,其进入unsortedbin,如果下一个申请的堆块大于0x520,则位于unsortedbin中的free chunk进入largebin,否则被切割利用。
- 因此申请一个
0x600(必须大于0x520)的堆块,使位于unsortedbin中的free chunk进入largebin。
- 修改
largebin chunk的bk->nextsize为目标地址target-0x20,此处我们的target选择mp_+80即存储tcachebin数量的地址,初试为64,涵盖0x10 ~ 0x400,我们要将其改成一个大值。
- 释放第三个堆块,其进入
unsortedbin,故技重施再申请一个0x600的堆块,使位于unsortedbin中的free chunk进入largebin,触发house of corruption。
- 此时再释放的堆块都会进入
tcachebin,利用double free完成对__free_hook的劫持即可。
exp如下:
1 | from pwn import * |
note_context
相比于上题开了沙箱黑名单,禁用了execve。rdi转rdx后setcontext+61一把梭:
1 | // rdi2rdx |
1 | // setcontext+61 |
- 需要注意的是,若调用
open函数报错,可以尝试pop_rax_ret+syscall进行系统调用。 exp如下:
1 | from pwn import * |