fakeobj
题目描述
Dear MNGA,
send me your best fake snake object
Regards,
joseph
题目功能
1 | #!/usr/bin/env python3 |
题目给出了 obj 对象的地址和 system 函数的地址,然后读入 72 字节,写入 obj 对象的位置
利用思路
参考资料:Exploiting a Use-After-Free for code execution in every version of Python 3
从中可以知道 python 对象基本结构体如下:
1 | typedef struct _object { |
ob_refcnt 是引用计数,ob_type 指向对象所属的类型结构体 PyTypeObject
而 PyTypeObject 结构体中存在 vtable,有很多函数指针可以劫持
因为知道了 obj 的地址,所以可以在 object head 下面伪造一个 PyTypeObject 并让 ob_type 指向伪造的结构体
经过调试发现,print(obj) 时第一个调用的 vtable 的函数指针应该是 PyTypeObject->tp_str
这个函数指针在 PyTypeObject 中偏移 0x88 ,被函数 PyObject_Str 调用
1 | res = (*Py_TYPE(v)->tp_str)(v); |
题目限制只能读入 72 字节,所以 ob_type 设置为 &fake_PyTypeObject - 0x88,使得 tp_str 正好落在 obj+0x10 的位置,紧跟在 ob_type 后,设置为 system
因为调用这个函数指针的第一个参数是 _object 的地址,所以篡改 ob_refcnt 为 /bin/sh\x00,又因为引用计数会在运行时 +1 ,所以第一个字节要 -1 ,变成 ./bin/sh\x00
exp
1 | #!/usr/bin/env python |
Mysterious Vault
题目描述
Dear MNGA,
You’ve discovered a mysterious vault exposed on a server - surely, if you can break in, great rewards will follow…
Regards,
ItsIronicIInsist
题目功能
出题人好心的提供了源代码
主程序 mysterious_vault 先读入密码到共享内存,然后 fork 后 execve 启动两个子进程 password_handler_3000 和 password_handler_3001 ,启动前还分别为两个子进程 chroot 隔离环境,使得他们只能分别读取到 checker1-trusted-env/ 和 checker2-trusted-env/ 中的文件
3000 和 3001 都是由源码 password_handler_300X.c 静态编译而来,但 3000 是使用的 gcc,而 3001 使用的 clang
子进程先启动了沙盒

并且关掉了 标准输入、输出、错误
然后从共享内存中拷贝密码,对密码检查,不通过的话返回 1 错误码告知主进程,主进程失败结束。
如果两个 handler 的退出码为 0 即检查通过,mysterious_vault 会直接把共享内存当作密码拿出来进行检查(之前一直是共享内存 + 0x8 为密码),这次是和真正的密码字符串 memcmp,如果通过会调用 get_flag 打印出 flag。
漏洞点

密码长度最大为 0x200,而子程序的 password 仅有 200 字节大小,从共享内存拷贝时栈溢出。
handler 的检查可以用 ‘\x00’截断绕过
1 | if (strcspn(password, "password") != strlen(password)) { |
利用思路
exit 中会先 syscall exit_group ,这导致即使是正常情况下两个 handler 都会因为违反沙盒规则而 SIGSYS 异常终止
并且 3000 和 3001 的栈布局不同,返回地址的偏移 3001 在 3000 之前,gadget 也不同
而经过调试我发现 3001 中有 gadget 等效于 exit($rbp-4) 只要控制 *($rbp-4) 为 0即可
1 | .text:000000000041C598 mov esi, [rbp-4] |
而 3000 需要用 rop 链从 checker1-trusted-env/password 真密码文件中读取前 0x8 字节到共享内存的开头,把之前用作存储密码长度占用的 0x8 字节修复为真密码。
exp
1 | #!/usr/bin/env python |
BLAST
题目描述
Dear MNGA,
BEGIN TRANSMISSION
I HEARD ALL THE BEST MATRIX LIBRARIES WERE WRITTEN IN FORTRAN SO I TRIED MY
HAND AT IMPLEMENTING MY OWN. FOR SOME REASON IT KEEPS CRASHING THOUGH?
END TRANSMISSION
Regards,
wednesday
漏洞点
FORTRAN 语言的数组下标从 1 开始而非 0,而题目中数组操作都是从下标 0 开始遍历,导致可以泄露/写矩阵之前的内存。
flag 读取到 bss 了
功能1和3 配合能泄露 栈地址和 pie
功能2 栈溢出,刚好能控制 rbp 和返回地址
利用思路
位于 0x1DEE 的 gadget,可以泄露 $rbp-0x10-17 处的 16字节内存内容
1 | .text:0000000000001DEE lea rdx, p_n ; p_n |
通过这个 gadget,控制 $rbp-0x10-17 为 bss 上的 flag 来泄露flag
需要多次访问,每次泄露16字节的flag
EXP
Credit:
@LittFlower: 矩阵逆运算,泄露地址和达成栈溢出部分
1 | from pwn import * |
rw
题目描述
Dear MNGA,
a read and a write, what more could you ask for?
Regards,
joseph
题目功能
Ubuntu GLIBC 2.35-0ubuntu3.8
Python 3.10.12
1 | while True: |
利用思路
python list 结构体如下:
1 | typedef struct { |
这次没有直接泄露地址了,但可以任意 offset 写对象a的内存
首先得实现泄露地址,我的做法是部分写改其中一个 item 的类型为 PyUnicode_Type ,然后把字符串大小改大,利用 r 功能打印出字符串泄露地址
PyUnicode_Type 结构体如下:
1 | //https://github.com/python/cpython/blob/f575588ccf27d8d54a1e99cfda944f2614b3255c/Include/cpython/unicodeobject.h#L111 |
从字符串结构体可以看到有三种字符串类型,这里设置为 compact 类型直接泄露出结构体后面的内存。
经过调试发现 a 与 a[1] 的偏移固定,篡改 a[1] 泄露出 pie
然后将 a[1] 类型改为 PyBytesIter_Type 再打印可以泄露出当前对象也就是 a[1] 的地址
最后伪造 PyTypeObject 劫持 vtable 上的函数指针,和 fakeobj 题目一样
exp
1 | #!/usr/bin/env python |