ninipwn - 新手
key 是 8 字节的,但是读入了 10 字节,可以溢出到 text_length ,导致 text_length 超过 buf 长度,能够栈溢出,并且存在格式化字符串漏洞。
1.通过格式化字符串漏洞泄露 canary 和 libc_base
2.通过栈溢出进行 ROP
3.注意 ROP payload 需要经过 encrypt 处理,其实就是一个简单的异或算法
exp
1 | from pwn import * |
buggypaint - 简单
select 功能可以保存 create 功能创建的堆块指针,但是 delete 功能释放堆块时候不会清空 select 保存的堆块指针,这就导致了 UAF
可以很简单利用 tcache bin attack 进行任意读写,这里是通过 environ 泄露栈之后进行 ROP
exp
1 | from pwn import * |
protector - 简单
一个开启了沙箱的栈溢出程序,明显需要 orw ,但是其中的 generate_directory_tree.py 会将 flag 藏入随机生成的 0x100 个文件中的一个。
出题人也留了 getdents 系统调用用来读取文件夹下的目录,可以用 mprotect ,直接用 shellcode 更加方便,直接让 gpt 写一个例子
poc
1 | #include <stdio.h> |
这里显然需要知道 d_name 和 d_reclen 的偏移是多少,才能搓汇编
IDA 直接反汇编
可以看到, bpos + 0x12 是文件名,bpos + 0x10 是下个文件结构体的基址,于是根据这个信息就可以手搓汇编了,先写出遍历所有文件的 shellcode,再补充 orw 进去。
需要注意的是,getdents 要读 0x100 个文件的信息,所以我把信息保存到 libc_base + 0x21c000 中
exp
1 | from pwn import * |
u2s 偏难
这道题偏难的原因是该题是一个 c 语言实现的 lua 虚拟机,如果不熟悉 lua 语言会比较难做。
题目给了 diff 文件,其中重点关注的是
1 | diff --git a/src/lvm.h b/src/lvm.h |
luaV_fastgeti 定义在查阅资料后发现是用于获取 Lua 表(table)中整数键(integer keys)对应的值,那么说明这个 patch 会导致 oob,还有其它 patch 是限制了读文件、命令执行等,就不详细说。
一开始做这个题目,想着用下面的 poc
1 | local ptr_a = {1, 2, 3 ,4 ,5} |
结果读出来的都是 nil,就比较怀疑是不是 oob 了
接着测试下面的 poc
1 | local ptr_a = {"stopstop", 2, 3 ,4 ,5} |
通过打断点到 write
可以直接跑到 print 函数的底层 write 处,通过栈回溯
可以判断出 luaB_print 是用来输出数组元素的
其中的 luaL_tolstring 函数就是用来在列表中找值的,不过直接调试,发现 luaL_tolstring 是没有越界的。
接着测试和数组相关的代码,继续调试的话,会发现
1 | local ptr_a = {0x11223344, 2, 3 ,4 ,5, 6} |
上面的 poc 存在负向溢出写
调试 gdb 配置如下
1 | set args ./poc |
这样就可以在进入 luaB_print 和 exit 时候各看一次数组的变化,来判断出存在什么漏洞
直接数据搜索可以看到列表是存在于堆块上的,接下来继续走会 crash
并且被写上去了,所以到这里就确定漏洞是数组的负向溢出写
由于一些功能被禁用了,所以没有办法直接泄露出地址,如果要通过堆,那么就需要一个稳定的堆布局
需要把堆都申请下,这里如果调试会发现,字符串也是放在堆块上的,并且申请的堆块大小是 字符串大小 + 0x28
有了稳定的堆布局之后,就可以进行堆地址泄露
1 | local ptr_a = string.rep('e', 0x100) |
我一开始用两个列表,但是 print 时候一直报错
这样就获得了程序基址,接着就可以直接调用了
这是一个直接调用的 poc
1 | local ptr_a = {} |
由于某些功能被禁用了,所以要调用 system 的话,得通过负向溢出调用伪造的数组
调试发现的话,print 是 函数地址 + p8(0X16) , 而单纯数据的话,就是 value + p8(0x3)
exp
1 | function intToHex(number) |