2023 TPCTF pwn wp
o4赢但我爆零的一集,主题:洞呢???为什么找不到洞啊!!!
httpd
逆向
http要求👇
Content-Length:指定data长度,POST方法必须有
Stdout:可以指定输出的fd
GET的可选url
init
初始化root用户的passwd,新建一个user_note并插入
test
输出test123123
setlocale
调用setlocale
register
新建一个user_note,需要username,passwd,uid和lens
logoff
需要username和passwd,删除对应user_note
show
需要username和passw,输出对应user_note的内容
poweroff
退出
POST可以输入note内容,需要usename和passwd
利用
使用show功能输出user_note内容时最终会调用bad_400(输出400 BAD REQUEST的函数),将note->buffer中内容复制到s数组中
register可以申请的最大大小是0x400,s的大小是0x408,但root的note大小是0x4f8,可以溢出
root的密码是伪随机,可以预测
编一个这样的程序,需要的时候调用
show和更改note内容需要uid == 0,还限制了uid != 0
但uid是根据 : 的个数判断的,我们可以输入形如 a:b:0 的username,这样会把uid判定为0
setlocale有cve(第一次在pwn题见利用cve,孤陋寡闻了(lll¬ω¬))
从poc可以看出在setlocale之后使用对齐有两字节的溢出
1
2
3
4
5
6
7
8
9
10
11
12#include <stdio.h>
#include <locale.h>
int main (void)
{
if (setlocale (LC_ALL, ""))
{
printf ("1234567890123:\n");
printf ("%0+ -'13ld:\n", 1234567L);
}
return 0;
}理论上应该输出👇
1
2
3
4
51234567890123:
+1,234,567 :
1234567890123:
+1,234,567 :但实际上👆
register中就使用了对齐
user_note结构体的布局👇
1
2
3
4
5
6
7
8struct user_note
{
struct user_note *prev;
struct user_note *next;
char name[32];
char *buffer;
size_t buffer_size;
}name理论上最多31字节,溢出两字节,只要能溢出一个’\x00’到buffer指针就能修改别的堆块或者泄露地址(实测可以溢出一个空字节)
是否能触发似乎与要对齐的字符串长度有关
还要注意每次循环都会close(fd[0]),所以stdout和stderr最多泄露两次,不需要泄露的时候要通过Stdout重置fd
exp
改buffer指针到栈上写ROP链mprotect,改栈的权限为7
写shellcode进行connect后orw,另起一个nc监听端口
1 |
|
tpgc
个人经验,c++不是用来逆的也不是用来看的(ˉ▽ˉ;),主打一个盲猜
思路
一个菜单,有ruby,rod和weapon三个栈,功能
- add
- take_ruby:add,push ruby,可以输入name
- take_rod:add,push rod,可以输入name
- fuse:pop ruby,pop rod add,push weapon,可以输入name
- delete
- drop_ruby:pop,delete ruby
- drop_rod:pop,delete rod
- drop:pop,delete weapon,push ruby,push rod
根据两个wp缩过的poc
1 |
|
- 好像是把ruby和rod拼接后drop_rod寄了,先拼的ruby再拼的rod,实际执行的就是 (*ruby_name)(ruby_name),令ruby_name为printf的got表地址就能泄露libc基址了
- 没开pie+aslr部分开启,堆基址是不变的,可以先把one_gadget的地址写到堆上,再令ruby_name为这个堆地址
ps:比赛的时候我没有试过drop之后drop_rod,也没有试过长输入(lll¬ω¬),记住了
exp
1 |
|
mte notebook
调试起来好慢/(ㄒoㄒ)/~~
arm64 mte
一些个人理解
flag1可以帮助理解一下mte
将指针的最高字节作为tag使用,如果指针的tag和之前打的tag不符则报错
比如malloc一块内存,返回的指针最高字节就是打上的tag,使用指针的时候应该使用 0x600ffff9bc768b0 而不是 0xffff9bc768b0
打tag通过软件进行,tag的验证通过硬件进行
tag的验证猜测通过在内存访问(ldr / str)时,同步检测tag是否匹配
可以通过 STG 指令打tag(tag的粒度为16字节),flag1中有这个过程
flag1的前16字节的tag是flag1的第一个字节
接下来的tag分别是flag1的第4,8,12字节的低4bit
可以通过以上tag的指针获取flag1
搜索STG指令可以看到一个专门用于打tag的函数
在malloc和free中都调用了这个函数
- malloc会给返回的指针打上tag
- free后会重新打tag,所以使用free后的内存会报错
思路
由于调试太慢失去耐心所以没自己搓exp,直接调试一遍星盟的exp
利用的根据
- malloc的内存都打了tag,利用overlap会使用未打tag的内存,报错
- free后会重新打tag,uaf之后使用的指针的tag是malloc的tag,报错
- 堆以外的地址(比如main_arena)没打tag可以使用,之前泄露了栈地址,可以到栈上写rop链
两个结构体
1 |
|
10个note
先drop掉0~7的note,填满tcache,把note[7]放进unsorted bin
把note[7]申请回来作为page,note是0x90,page是0x80,但如果从0x90中分割0x80那么剩下0x10不足0x10,所以会直接返回0x90的note
由于未初始化漏洞unsorted chunk的bk就是struct page的next,伪造需要写0x18字节,所以前移3页
伪造完的page
一样的步骤,这次把note[8]释放掉,写rop链,overwrite_page结束后开始rop链
rop链就是利用 svc #0 进行系统调用
exp
1 |
|