2026年天府杯pwn的0解题目WP

admin 2026-02-08 01:47:44 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文档详细解析了2026年天府杯Pwn比赛的零解题Writeup。文章逆向了程序逻辑,分析了基于输入计算的堆分配机制,揭示了隐藏的Show后门功能及内存对齐漏洞。通过构造特定的堆块布局和伪造堆地址绕过非法指针检查,作者给出了最终的Exploit利用思路和脚本,展示了二进制漏洞挖掘与利用的高阶技巧。 综合评分: 88 文章分类: CTF,二进制安全,漏洞分析,逆向分析


cover_image

2026年天府杯pwn的0解题目WP

原创

teacher李 teacher李

由由学习吧

2026年2月5日 22:15 重庆

2026年天府杯pwn的0解题目WP

libc版本为2.27

整体代码逻辑如下:

extern FILE *stderr;

extern FILE *stdin;

extern FILE *stdout;

unsignedintmain(unsignedinta0, unsignedlonglonga1)

{

   unsignedlonglong v0;  // [bp-0x28]

   unsignedint v1;  // [bp-0x1c]

   char result;  // [bp-0x11]

   unsignedlonglong v3;  // [bp-0x10]

   v1= a0;

   v0= a1;

   setvbuf(stdout, NULL, 2, 0);

   setvbuf(stderr, NULL, 2, 0);

   setvbuf(stdin, NULL, 2, 0);

   result =0;

   while (true)

   {

       mean();

       v3 =input();

       if (v3 ==1)

       {

           add(sub_400b7f);

       }

       elseif (v3 >=1)

       {

           if (v3 ==2)

           {

               show(sub_400bea);

           }

           elseif (v3 ==1337)

           {

               if (result)

                   return0;

               show(sub_400ccd);

               result =1;

           }

           else

           {

               return0;

           }

       }

   }

}

intmean()

{

   puts(“1. malloc”);

   puts(“2. delete”);

   puts(“3. exit”);

   returnprintf(“>> “);

}

longlonginput()

{

   unsignedlonglong v0;  // [bp-0x30]

   char v1;  // [bp-0x28]

   v0=16;

   memset(&v1, 0, 16);

   shuru(&v1, 15);

   returnstrtol(&v1, NULL, 10);

}

longlongshuru(char*a0, longlonga1)

{

   char v0;  // [bp-0x21]

   void* i;  // [bp-0x20]

   unsignedlonglong count;  // [bp-0x18]

   for (i =0; i < a1; i +=1)

   {

       count =read(0, &v0, 1);

       if (count !=1)

           exit(-1); /* do not return */

       if (v0 ==10)

           return i;

       *((char*)(a0 + i)) = v0;

   }

   return i;

}

typedefstruct struct_0 {

   struct struct_0 *field_0;

} struct_0;

externunsignedlonglongg_602060[4];

externunsignedlonglong g_602160;

void*sub_400ec0(struct_0 **a0)

{

   char v0;  // [bp-0x29]

   void* idx;  // [bp-0x28]

   void* j;  // [bp-0x20]

   unsignedlonglong v3;  // [bp-0x18]

   unsignedlonglong v4;  // [bp-0x10]

   puts(“Length: “);

   v3=input();

   if (v3 >640)

       exit(-1); /* do not return */

   v4= v3 +15>>4;

   for (idx =0; idx <=31&&*((longlong*)(0x8* idx + (char*)&g_602060[0])); idx +=1);

   if (idx ==32)

       exit(-1); /* do not return */

   *((unsignedlonglong*)(0x8* idx + (char*)&g_602060[0])) =a0(v4);

   *((unsignedlonglong*)(0x8* idx + (char*)&g_602160)) = v4;

   puts(“Data: “);

   for (j =0; j < v4; j +=1)

   {

       v0 =sub_40108a(j *24+*((longlong*)(0x8* idx + (char*)&g_602060[0])));

       if (v0 !=1)

           return v0 ^1;

   }

   return j;

}

typedefstruct struct_0 {

   charpadding_0[16];

   unsignedlonglong field_10;

} struct_0;

unsignedlonglongsub_40108a(struct_0 *ptr)

{

   ptr->field_10=shuru(ptr, 16);

   returnptr->field_10&0xffffffffffffff00|ptr->field_10==16;

}

unsignedlonglongsub_400b7f(unsignedlonga0)

{

   void* i;  // [bp-0x18]

   unsignedlonglong ptr;  // [bp-0x10]

   ptr =malloc(a0 *24);

   for (i =0; i < a0; i +=1)

   {

       sub_400e4a(ptr + i *24);

   }

   return ptr;

}

typedefstruct struct_0 {

   charpadding_0[16];

   void* field_10;

} struct_0;

longlongsub_400e4a(struct_0 *ptr)

{

   ptr->field_10=0;

   returnsub_401062(ptr);

}

longlongsub_401062(struct_0 *a0)

{

   returnmemset(a0, 0, a0->field_10);

}

typedefstruct struct_0 {

   struct struct_0 *field_0;

} struct_0;

externunsignedlonglongg_602060[4];

unsignedlonglongshow(struct_0 **a0)

{

   unsignedlonglong v0;  // [bp-0x10]

   puts(“Index: “);

   v0=input();

   if (v0 <=31)

       return (!g_602060[v0] ?puts(“Nope”) :a0(v0));

   exit(-1); /* do not return */

}

externvoid g_602060;

void*sub_400bea(unsignedlongidx)

{

   void* i;  // rbx

   if (*((longlong*)&(&g_602060)[8* idx]))

   {

       i =*((longlong*)&(&g_602060)[8* idx]) +*((longlong*)(*((longlong*)&(&g_602060)[8* idx]) -8)) *24;

       while (i !=*((longlong*)&(&g_602060)[8* idx]))

       {

           sub_400e72(i -24);

       }

       sub_400d45(*((longlong*)&(&g_602060)[8* idx]) -8);

   }

   *((unsignedlonglong*)&(&g_602060)[8* idx]) =0;

   return&g_602060;

}

longlongsub_400e72(void*a0)

{

   returnsub_401062(a0);

}

longlongsub_401062(struct_0 *a0)

{

   returnmemset(a0, 0, a0->field_10);

}

longlongsub_400d45(unsignedlonga0)

{

   return (unsignedlonglong)free(a0 +15&0xfffffffffffffff0);

}

externunsignedlonglongg_602060[4];

externunsignedlonglongg_602160[4];

intsub_400ccd(unsignedlonga0)

{

   void* i;  // [bp-0x10]

   for (i =0; i <g_602160[a0]; i +=1)

   {

       sub_400e8e(i *24+g_602060[a0]);

   }

   returnputchar(10);

}

typedefstruct struct_0 {

   charpadding_0[16];

   char field_10;

} struct_0;

extern FILE *stdout;

longlongsub_400e8e(struct_0 *a0)

{

   returnfwrite(a0, *((longlong*)&a0->field_10), 1, stdout);

}

保护机制、ROP链、gadget、sh:

详细分析:

这里需要注意绕过,我们输入的size经过了运算和移位

然后就是注意对齐,清空 field_10的最低字节,然后设置其值为 0x10

注意用户实际输入的计算:

1. 用户输入size = v3 (比如0x60 = 96)

2. 计算结构体数量:v4 = (v3 + 15) / 16

3. 实际malloc:malloc(v4 * 24)

举个例子:

– 用户输入:0x60 = 96字节

– 结构体数量:(96 + 15) / 16 = 111 / 16 = 6 (整数除法)

– 实际malloc:6 * 24 = 144字节

  • malloc对齐后:0x90字节,但堆头占0x10,所以用户看到0x80

但是也可以不用纠结这个问题,直接按照常规来写脚本就可以,就是注意分配的地址大小

然后有一个隐藏的show函数,当输入1337时候出现,但是只有一次机会

运行之后result = 1,就不能第二次进去了

然后是delete,就是free之后把指针清零(因为这个程序不是多线程程序所以不存在UAF,如果是多线程就得分情况导致潜在UAF漏洞的核心问题在于,释放内存和清空全局表指针的操作不是原子性的,在这之间存在一个时间窗口,并且代码逻辑可能创建了指向已释放内存的“悬垂指针”)

但是要注意delete内存对齐问题函数中存在堆操作越界 / 非法内存访问,正确的对齐计算:free((void*)((a0 + 15) & ~0xF)),否则,错误的地址导致破坏glibc的堆管理结构程序会退出

所以:先申请了 17 个堆块(索引0-16),free  10/7/6/2——这些索引在show()的检查范围内(v0<=31),且先通过add操作把g_602060的对应位置写入了合法的堆地址(而非随机值),避免了puts("Nope")后触发的非法访问。先大量add不同大小的堆块(0x70/0x10/0xb0/0xd0等),让g_602060中对应索引的位置被写入malloc返回的合法堆地址(而非越界的随机值),此时sub_400bea*((long long *)&(&g_602060)[8 * idx])是合法地址,暂时不会触发段错误。通过add(0x10, pld(0,0x441 + 0x20*3 + 0x60))等操作,伪造了a0的值,让a0+15&~0xf恰好等于一个合法的堆地址(而非野指针),规避了free非法地址触发的崩溃。

Exp攻击如下:


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:由由学习吧 teacher李 teacher李《2026年天府杯pwn的0解题目WP》

评论:0   参与:  0