Polarisctf招新赛
pwn
ez-nc

nc连一下???


这是干什么

难道限制7byte,还会ban字符

出题人怎么这么坏啊
这到底在考什么
格式化字符串???

wc,我是天才
可以还原一下函数
char buf[8];
scanf(“%7s”, buf);
printf(buf);

出canary了


%i$s
这内存里也没flag啊

| 字节序列 | 含义 | 解释 |
|---|---|---|
**<font style="color:rgb(68, 71, 70);">\x7fELF</font>** |
Magic Number | 告诉系统:“我是一个 ELF 格式的可执行文件”。 |
**<font style="color:rgb(68, 71, 70);">\x02</font>** |
Class | 代表 64 位(如果是 <font style="color:rgb(68, 71, 70);">\x01</font>则代表 32 位)。 |
**<font style="color:rgb(68, 71, 70);">\x01</font>** |
Data | 代表 小端序(Little Endian),这是 x86 架构的标准。 |
**<font style="color:rgb(68, 71, 70);">\x01</font>** |
Version | ELF 文件的版本,通常固定为 <font style="color:rgb(68, 71, 70);">01</font>。 |
ez-nc的文件头


有点奇怪的题
ezheap


有seccomp

ban 了execve和execveat
用orw

c++编译

全开

我去,2.39

有backdoor function,那还用个蛋的orw


这笔记看着就高级
翻译一下
| 编号 | 选项名称 | 翻译 |
|---|---|---|
| [1] | Register model artifact | 注册模型产物 |
| [2] | Stream artifact chunk | 流式传输产物分片 |
| [3] | Bootstrap async scheduler | 引导异步调度器 |
| [4] | Inspect scheduler queue | 检查调度队列 |
| [5] | Allocate session tensor | 分配会话张量 |
| [6] | Complete batch inference | 完成批处理推理 |
| [7] | Patch session metadata | 修复/更新会话元数据 |
| [8] | Provision worker profile | 配置工作节点配置文件 |
| [9] | Dispatch async task | 派遣异步任务。 |
| [10] | Runtime telemetry | 运行时遥测 |
| [11] | Operator handbook | 操作员手册 |
| [0] | Shutdown gateway | 关闭网关 |
根本看不懂
这出题人怎么这么坏啊
•᷄ࡇ•᷅

什么叫main函数里还有加密???
不对,不是加密,这是自校验

底下这一块才是要看的
兮,这c++反汇编我还不太看的懂

慢慢看了


先清零buf 376+16字节
malloc 了0x200,512bytes
v23 = begin , v29=the chunk’s end
*(__m128i *)&buf[376] = _mm_unpacklo_epi64((__m128i)v23, (__m128i)v23);
将两个 64 位整数交叉组合成一个 128 位的向量
两个参数都是v23,故高64低64都是v23
sub_8A60 和 sub_3570:传入了 buf 的起始地址
先看看8a60

控制台这一块

ban execve

buf共用

用来int (str)的

8a60结束后free buf的,6720也是
看看case1

calloc

打印calloc的返回地址

单个输入 v9 (items) 和 v42 (stride) 被限制在 32 位以内
n32_1 = (std::ios_base *)(v42 * v9);
乘积被存入了 64 位的 n32_1 中
n32 = (unsignedint)n32_1;
强制将 64 位的 n32_1 转换为 32 位的 unsigned int
v13 = calloc(n32, 1uLL);
使用截断后的 32 位小数值去分配堆内存
存在 Integer Truncation (高位砍掉)
这样就可以伪造所记录的calloc的chunk大小
看看case2

1 | |
看看case3
什么叫**引导异步调度器**

启动后台的任务分发系统
创建总控台(Scheduler Control Plane):分配一个全局的调度器控制块(queue_ctrl),里面记录了调度器的状态,并且初始化了安全策略(**strict_policy**** = 1)**
创建任务槽(Task Descriptors):循环 8 次,创建 8 个任务结构体(Task 0 到 Task 7)。每个结构体里预先填好了默认的回调函数指针(handler)和参数(context)
1 | |

可以通过case2的溢出覆盖v10
看看case4

4可以泄露addr

看看case5
1 | |
case6

UAF
看看case7

可以用它改fd
这就是edit
看看case8
Case 8 表面上是一个配置节点的业务功能,但在 Pwn 攻击链中,它是一个无损的 64 位任意内存内容填充器
| 物理内存偏移 | Case 8 以为它在写什么? (Worker Profile) | 实际传入的 Payload | Case 9 以为它在读什么? (Task Descriptor) |
|---|---|---|---|
<font style="color:rgb(68, 71, 70);">+0x00</font> |
<font style="color:rgb(68, 71, 70);">cpu_quota</font> |
<font style="color:rgb(68, 71, 70);">0</font> |
<font style="color:rgb(68, 71, 70);">task_id</font> |
<font style="color:rgb(68, 71, 70);">+0x08</font> |
<font style="color:rgb(68, 71, 70);">mem_quota</font> |
<font style="color:rgb(68, 71, 70);">0</font> |
状态字段 / 占位 |
<font style="color:rgb(68, 71, 70);">+0x10</font> |
<font style="color:rgb(68, 71, 70);">io_weight</font> |
<font style="color:rgb(68, 71, 70);">0</font> |
状态字段 / 占位 |
**<font style="color:rgb(68, 71, 70);">+0x18</font>** |
**<font style="color:rgb(68, 71, 70);">latency_slo</font>** |
**<font style="color:rgb(68, 71, 70);">backdoor_addr</font>** |
**<font style="color:rgb(68, 71, 70);">handler</font>**(后门函数指针) |
**<font style="color:rgb(68, 71, 70);">+0x20</font>** |
**<font style="color:rgb(68, 71, 70);">replicas</font>** |
**<font style="color:rgb(68, 71, 70);">task_0_addr + 40</font>** |
**<font style="color:rgb(68, 71, 70);">context</font>**(传给后门函数的参数指针!) |
**<font style="color:rgb(68, 71, 70);">+0x28</font>** |
**<font style="color:rgb(68, 71, 70);">memo</font>** |
**<font style="color:rgb(68, 71, 70);">b"\x00"*8</font>** |
**<font style="color:rgb(68, 71, 70);">tag</font>**字符串 (随便) |

看看case9
1 | |
可以通过case2把strict_policy改成0
看看case10
1 | |
看看case11

原来是凑数的

2.32以上就有safe-linking了
有UAF时就可以直接得到heap_base_addr,pwn.college里写过太多了
1 | |


这题的阅读实在有点困难
•᷄ࡇ•᷅
treasure



伪c可比伪cpp清秀多了

这开了pie就几乎不太可能猜对了


看来是要覆盖got表拿shell

我去,有负index



但直接覆盖got的话后续的call function会error
可以只覆盖低一字节,libc.sys一下就行
然后得到leak_addr,算出libc_base_addr

这里用0xef52b,
通过input name可以将rbp-0x78,rbp-0x50覆盖为0





1 | |
offset那块要注意treasure连的libc.so.6是哪个

这才是正经题
˃̣̣̥᷄⌓˂̣̣̥᷅
httpd


看着像服务器的题

9999

甚至还会显示sig,也是提示fork爆canary了



子父进程共享mmap的内存

应该就是要搞这个函数

malloc了一块s
get和post请求

看看这个

http协议解析器
s1大概长这样
| 偏移 (Offset) | 字段名 | 长度/类型 | 说明 |
|---|---|---|---|
<font style="color:rgb(68, 71, 70);">+0</font> |
<font style="color:rgb(68, 71, 70);">Method</font> |
16 bytes | 如 “GET” 或 “POST” |
<font style="color:rgb(68, 71, 70);">+16</font> |
<font style="color:rgb(68, 71, 70);">Path</font> |
256 bytes | 请求路径 |
<font style="color:rgb(68, 71, 70);">+272</font> |
<font style="color:rgb(68, 71, 70);">Proto</font> |
16 bytes | 协议版本 (如 “HTTP/1.1”) |
**<font style="color:rgb(68, 71, 70);">+288</font>** |
<font style="color:rgb(68, 71, 70);">Header_Count</font> |
4 bytes (int) | 当前解析到的 Header 数量 |
<font style="color:rgb(68, 71, 70);">+292</font> |
<font style="color:rgb(68, 71, 70);">Headers</font> |
32 * 320 bytes | Header 数组 (每个 Key 64B, Value 256B) |
<font style="color:rgb(68, 71, 70);">+10532</font> |
<font style="color:rgb(68, 71, 70);">Token_Key</font> |
64 bytes | Cookie 中的 token 键名 |
<font style="color:rgb(68, 71, 70);">+10596</font> |
<font style="color:rgb(68, 71, 70);">Token_Value</font> |
256 bytes | Cookie 中的 token 值 |
**<font style="color:rgb(68, 71, 70);">+10856</font>** |
<font style="color:rgb(68, 71, 70);">Body_Ptr</font> |
8 bytes (QWORD) | 指向 POST Body 的堆指针 |
**<font style="color:rgb(68, 71, 70);">+10864</font>** |
<font style="color:rgb(68, 71, 70);">Content_Length</font> |
8 bytes (QWORD) | 解析出的正文长度 |
1 | |

看看get
多少有点煎熬了


提取url路径
这LABEL_17怎么这么长•᷄ࡇ•᷅


检验token

char s[256]=0


检查admin token,if not goto start

这个if没有检验,直接进入

生成admin token

if a3(s),send admin token
进入/getCookie 时不为0

这样就得到了admin token
再看看post

这个是校验正常登入

检查admin token 后可以resetpasswd


通过token检验后用4035a0函数处理

canary is here
dest_3[5]=rbp-0x40+40bytes=rbp-0x18

寻找 “gateway” 对应的结构体,把指针赋给 a3
dest_3 只有56bytes,但memcpy的字节是post上去的payload决定
显然可以打rop
get,host请求+canary-fork泄露+stack-pivot+reset register+ret2libc
canary-leak

leak read_real_addr
这里要add rax,防止write不出来,write的值return 进rax里

没PIE真是件美事啊

就你了

就你了



打ret2libc

加个ret

这里没反应

shell怎么跑这里了

我查一查
ok了,是 Shell 默认的标准输入 (stdin, fd=0) 和 **标准输出 (stdout, fd=1)**问题
Socket和弹出来的 Shell 是互相隔离的
可以用rop.dup2( 4, 0)重定向stdin/out


试试

成了
1 | |
ok了

这是爆的是真的慢
运气挺好,附件是没libc的,看来远程的libc.so.6跟我的一样,不过就算不一样也能用libsearch