CarManager
1 | GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31. |
1 | CarManager: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=5e78faed20a68cb3ad600697b0d036baefa646e8, for GNU/Linux 3.2.0, stripped |
- 64位,dynamically,Full RELRO,NX,PIE
漏洞分析
1 | void __fastcall write_s(const char *introduction_data) |
UAF 漏洞:
1 | if ( check() ) |
堆溢出漏洞:
1 | if ( nameg->descript ) |
- 这里使用
strlen获取当前 chunk 的 size,在写满的情况下可能会将 next chunk->size 也计算在内,导致几字节的堆溢出 - PS:遇到这种不记录 size 而是使用
strlen计算 size 的题目,一定要检查有没有堆溢出
入侵思路 - 格式化字符串
使用格式化字符串可以泄露 stack_addr 和 libc_base:
接下来就是通过覆盖返回地址打 one_gadget:
1 | 0xe3afe execve("/bin/sh", r15, r12) |
libc-2.31 的 one_gadget 条件比较高,断点到 ret 并查看此时的寄存器:
1 | *RDX 0xffffffffffffff9c |
- 使用一个
pop_rdx_ret就可以将 RDX 置空,在栈上有预留的 “0”
1 | 00:0000│ rsp 0x7fffffffdc08 —▸ 0x55555555551a ◂— jmp 0x55555555553f |
搭建 ROP,利用预留的 “0” 置空 RDX,最后写上 one_gadget 就可以了
非完整 exp 如下:(没有写有关逆向的部分)
1 | # -*- coding:utf-8 -*- |
入侵思路 - UAF+堆溢出
利用 UAF 也可以完成泄露,但是需要注意技巧:
1 | for ( i = 0; size > i; ++i ) |
所有的输入函数都会对末尾的 \n 进行置空,而所有的输出都会被 \x00 截断,此时遗留在堆上的地址就无法被打印
可以用如下技巧绕过这个限制:(输入刚好合适的 size,使 \n 读取不进来)
1 | Register("name",0x50,"1"*0x10) |
利用堆溢出我们可以修改 next chunk->size,将其释放就可以实现堆重叠,重新申请回来就可以修改 tcache 了
非完整 exp 如下:(没有写有关逆向的部分)
1 | # -*- coding:utf-8 -*- |
codelog
1 | GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31. |
1 | codelog: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=dd0a2bd738926ee059e1aceb6873084dcf353a1b, for GNU/Linux 3.2.0, stripped |
- 64位,dynamically,Full RELRO,Canary,NX
漏洞分析
程序只置空了指针而没有置空 size:
1 | if ( chunk_list[num].ptr ) |
堆溢出漏洞:(scanf 没有限制输入值的长度)
1 | printf("char: "); |
- PS:遇到
scanf需要注意是否有堆溢出
入侵思路
简单搭建一下堆风水就可以完成泄露:
1 | init(0x48,0x60,0x60) |
利用 scanf 的堆溢出可以修改 tcache,从而劫持 free_hook
完整 exp 如下:
1 | # -*- coding:utf-8 -*- |
rpg
1 | GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31. |
1 | rpg: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=b69646b934160ce4f2f19a27b4d4637e7cd180f8, stripped |
- 64位,dynamically,Partial RELRO,NX,PIE
flatbuffers 逆向分析
在开始分析本题目前,需要先了解 flatbuffers 的相关知识,可以在官网了解其基础用法:
经过长时间的逆向分析,发现程序会大量使用如下结构去索引数据:
1 | __int64 __fastcall sub_1BE0(char *a1) |
1 | __int64 __fastcall sub_2E70(__int64 a1, unsigned __int16 a2) |
1 | __int64 __fastcall sub_2EA0(__int64 a1, unsigned __int16 a2) |
1 | __int64 __fastcall sub_2A30(__int64 input, unsigned __int16 num) |
从 flatbuffers 官方测试样例中编译了几个二进制文件,找到类似的结构:(反序列化部分)
1 | const MyGame::Sample::Vec3 *__cdecl MyGame::Sample::Monster::pos(const MyGame::Sample::Monster *const this) |
1 | const MyGame::Sample::Vec3 *__cdecl flatbuffers::Table::GetStruct<MyGame::Sample::Vec3 const*>( |
1 | flatbuffers::voffset_t __cdecl flatbuffers::Table::GetOptionalFieldOffset( |
也就是说,题目设计了 flatbuffers 反序列化的部分,需要我们逆向出序列化逻辑并将序列化后数据作为程序的输入
flatbuffers 的反序列化需要定义 cft.fbs 文件(用于指定序列化格式),目前只能通过反序列化伪代码来猜测 cft.fbs 中可能的结构,我在题目中找到了如下的突破口:
1 | __int64 __fastcall Verify(char *this, _QWORD *verifier) |
Verifier 是 flatbuffers 的检查器:
VerifyString(verifier, key)暴露了cft.fbs的第1个条目是 stringVerifyField_int(this, verifier, 6u, 8LL)暴露了cft.fbs的第2,3个条目是 int64
而最后一个条目可以从如下伪代码中分析出来:
1 | v1 = get_index2((__int64)input); |
1 | __int64 __fastcall get_index(unsigned int *a1, unsigned int a2) |
cft.fbs的第4个条目是 Vector tables2- 近一步对比分析可以得知:tables2 存放有2个条目 - (int64,string)
最后设计出的 cft.fbs 如下:
1 | namespace Test.Sample; |
漏洞分析
程序只置空了 size,有 UAF 漏洞:
1 | free(datag.chunk_list[index]); |
入侵思路
先利用 UAF 进行泄露
1 | add(0,0x420) |
然后利用 UAF 打 double free 即可
完整 exp 如下:
1 | # -*- coding:utf-8 -*-s |
output
1 | Suppose the quantum circuit is named "qc", the quantum operations are as follows: |
附图:
这道题目的关键就是 matplotlib.pyplot 模块
有了该模块进行可视化,很快就可以求出答案,完整 exp 如下:
1 | from qiskit import * |
Glass
从32x32的像素点中选择10个,基数很小可以直接爆破