复健。
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 * |