第一天打得头疼眼睛疼,我以为是被脑洞给干烂了,结果是tm感冒了,回去吃个药秒好了,但是脑洞太难蚌了,第二天打舞萌去了,没解后面的题了,不过幸好没看,后面有些题还真不大好写。
# Web:
# 老爷爷的金块
现在不知不觉 2025 年了,曾经在 4399 里遨游的小孩儿也变成大人了… 重新看了一遍 4399 的经典游戏,想起了这个努力挖矿的老爷爷。 下载附件,打开 exe,重温童年的乐趣!
提示 11. 请注意获得的 flag 第六段前面有一个空格哦~
打开是个游戏,打完就有 flag,当然分数要过 1000

另外一个想法,就是,浏览文件,发现 bk_flag.png 等多个图片,winhex 打开,搜索 flag,不知道为啥 IDA 里没有,找到了这个:

把这个交上去看看,成功。
# PHPGift
ctrl+u 看到:
1 | <!-- hhhhhh!!!! where is xxx.php --> |
提示是三字文件,flag 上面有个 ser 是唯一三字符的读读看:

1 |
|
pop 链。
链子思路大概是 Logger 中的 __invoke ,触发之后调用 log 进行任意文件写,在这里写入木马即可,那么调用 __invoke 的点可以是 User 中的 call_user_func , __toString 可以由 FileHandler 中的 echo $this->fileName; 触发,再向上就是 __destruct ,其他杂七杂八的类和方法太多了,看上去这个题难,结果一看好像不难,生成 payload 如下:
1 |
|
之后访问 upload.php 拿到 shell,传参是 1。

base64 解码得到: HECTF{c0ngr4ts_l1ttl3_h4ck3r_y0u_f0und_my_53cr3t_g1ft}
# 像素勇者和神秘宝藏
三个门,有用代码如下:
1 | <script> |
第一个门就正常按按鼠标就开了,多点很多次而已,第二个门是只有 vip 勇者能进,点击获取令牌,然后抓包发现 Cookie:
1 | role=user; token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoicGxheWVyIiwiYmxlc3NlZCI6ZmFsc2UsImV4cCI6MTc2NjIyNTY0OH0.H10-DibPmALQ0RyNSp08JlQlL4Rmc7wo8cOd1kau1-U |
看起来是 Jwt 解密,role 也是 user,姑且把 user 换成 vip(这里是小写,因为大写绕不过)绕过:

VIP 通道畅通!但宝藏仍被封印……
说明得开第三门,Jwt 解密看看:

根据这个猜测把 blessed 换成 true 即可,那么就是猜测密钥的问题了,根据上面注释:
1 | <!-- |
先说了 HECTF 是大写还是小写,然后后面又说了 这是秘密,草,坑在这里啊,HECTF 就是密钥,吗?那么密钥的大写和小写就是问题了, 直接爆破,下面这个代码直接运行就能爆破出 flag:
1 | import jwt |
# 谁家 blog 里有这功能?
Bob 在宿舍里闲的 damn 疼,不知道干什么,他的朋友 Alice 最近整了个个人博客主页,最近几天天 天在他面前炫耀,他觉得心理很不爽,有种被朋友内卷自己却在摆烂而感觉到了背叛的感觉,于是 他费尽心力终于捣鼓出来了一个,但是他搓出来的网站有一些其他博客作者不会有的功能…
注意:由于出题人学艺不精的问题,在做题过程中可能会出现网站无法访问的情况,可能是 exit 把 整个程序停掉了,请等待三十秒,三十秒后应该会重新启动,若三十秒后没有启动的,可以重启环 境。
一般类似的 CMS 的题直接登后台爆破弱口令碰碰运气,不过这个有可能爆不出来,但存在忘记密码的接口,这个是很多漏洞的可以利用的点,所以这里可以赌一把试试看:

这里看 url 似乎有可以利用的点,测测看是否存在这种逻辑漏洞(前端对后端响应进行验证,然后跳转到第二个重置密码的页面,以此类推,直到最后的一个页面,当后端认为前两次验证已经完成了,不需要进行后续验证,之类可能会存在任意密码修改的洞,不过实际情况下应该大部分都修了,我这里只是模拟这个洞的点,不会根实际情况一样),这里抓一下响应包看看:

相应包之类可以修改 "success":false 为 "success":true ,成功跳转到第二次验证:

之后随便输,抓包看看:

原本的 step1 变成了 step2,可以猜猜看最后的一步可以是 step3 或者 step4,不过这里还是老老实实一步步下去,老样子,抓相应包,改参数:

成功跳转到这一步,那就修改密码为 123,之后抓包看看:

之后抓相应包看看:

1117210622168.png&pos_id=img-A0YJbOft-1767957868072)
这里我没改,证明修改密码成功了,之后就可以去登录了,成功进入后台:

往下面翻发现了这里有个超链接,似乎可以点,点击之后发现是下载博客的地方:

抓包之后发现了文件下载接口,似乎可以任意文件读:

试着读取根目录下的 flag 文件失败了,被 forbidden 了,不过可以读取源码,直接下载 app.py 得到源码,源码里有用的地方在这儿:
1 | from flask import Flask, request, jsonify, render_template, session, redirect, url_for, flash,send_file, abort |
这是一个沙箱,可以执行代码,但是,存在三重检验,一个是源码层面的检验 source_simple_check ,需要代码里不存在这些黑名单关键词,第二层是字节码检验,第三层是运行时检验,也就是常说的 addaudithook ,主要需要绕过的是第一层和第三层,可以先从沙箱入手,想办法绕过沙箱,不过,沙箱中的 "__builtins__": safe_builtins ,可知内建函数中没有可以让我们导入的东西,那么可以想办法逃逸出沙箱,到更外层可能会有可以使用的点,所以用栈帧逃逸逃到外界即可。
首先是想办法逃逸到外界,我们可以确定的是,外界的程序里肯定是有 "__builtins__" 的,那么就可以通过如下方式进行绕过:
1 | def waff(): |
可见,这里存在 "__builtins__" ,可惜是个函数,那么,看看这一层是哪一层:
1 | def waff(): |
先来做个 test 文件测试下,将这两个沙箱函数稍微改改,本地调试下:
1 | def source_opcode_checker(code_obj): |
可以简单整改下源码任期能够在本地运行,之后就是调试:
1 | def waff(): |
这里的 "__builtins__" 是模块,但是因为第一层 waf 过滤太多关键字了,所以根据模块的调用方式这个似乎没法进行绕过,那么再回溯一层再看看:
1 | def waff(): |
这里就成了字典,那么就可以进行绕过了,这里成功获取了 "__builtins__" 的字典,那么在题目里测试下看看:
1 | def waff(): |
证明是成功了,但是,这里需要注意,如果直接输出 b 会导致程序 crash 掉,尽可能别输出,这似乎是别的什么问题导致的报错。
既然有了 "__builtins__" 之后,那么就可以考虑直接把 os._exit(0) 给杨掉,这样就算执行了 system 之类的函数也不会报错,直接扔下 payload:
1 | def waff(): |
这里通过了 __builtins__ 中的 setattr 修改了 os 中的 _exit 为 print ,那么就算被钩子检测到了,也不用担心被直接退出了,之后就是直接 RCE 了:
1 | def waff(): |
因为不能使用 read() (在关键词上被禁用了),所以这里只有通过这种方式进行绕过了,通过 getattr 获取 popen 执行过后的对象中的 _proc 之后用 stdout 进行输出
先贴一个 payload,最后 payload 如下:
1 | def waff(): |
这里就对这个 payload 就行下讲解就行了,就不一步步进行调试了:
首先,三个 waf 函数里,第二个 waf 作用不是很大,但是会导致 #错误: 执行错误: Disallowed global access: q ,不过像之后那样就不会出现这个报错。第一个主要是杨掉了常见的字段,第三个则是沙箱的核心点,虽然看上去第三个 waf 防的很死,但是用处不大,因为是用的 os._exit() 直接 exit 掉了,所以这里直接给 os 里的这个方法给杨掉就行了,不需要在意,所以这里的 payload 就是:
1 | def waff(): |
这里贴一下 k13in 大佬的 poc:
1 | def f(): |
# ez_include(复现)
不太一样的文件包含
贴个源码:
1 |
|
存在 $filterPrefix = 'php://filter/string.strip_tags/resource='; ,依稀记得 BUUOJ-[NPUCTF2020] ezinclude 1 这个题里有一个这个伪协议,有个特性:
在上传文件时,如果出现
Segment Fault,那么上传的临时文件不会被删除。这里的上传文件需要说明一下,一般认为,上传文件需要对应的功能点,但实际上,无论是否有文件上传的功能点,只要 HTTP 请求中存在文件,那么就会被保存为临时文件,当前 HTTP 请求处理完成后,垃圾回收机制会自动删除临时文件。使
php陷入死循环直,产生Segment Fault的方法:(具体原理未找到,如果有大佬清楚,请告知,感谢。)
- 使用
1 >php://filter/string.strip_tags/resource=文件
- 使用
1 >php://filter/convert.quoted-printable-encode/resource=文件
- 函数要求
filefile_get_contentsreadfile
所以直接 PHP LFI 包含临时文件+string.strip_tags 过滤器导致出现 php segment fault
1 | import requests |
之后用 ?file=tmp ,这里功能是扫描临时文件目录,输出 DAyj :
1 | foreach ($phpFiles as $name) { |
根据这里,可以知道,当输出长度高于 4 的时候输出后四位,那么,这里应该是两位是未知的 php??DAyj ,那么就得爆破
最后爆破出来:
1 | import requests |
1 | [!] 爆破成功!正确组合: jL |
成功 RCE,之后就是 RCE 读取文件了:
# 红宝石的恶作剧(复现,先记录下来,细看)
ez_ssti
WP 没看懂,先记录下来,之后研究下
输入 1+1 返回 2,但是输入 {{1*1}} 就报错了,wp 里说这个是 Ruby 环境,存在 ERB 模板注入漏洞,所以用 File.read('/flag'),返回fakeflag 读取 flag,但是返回了个假的 flag: Hello, HECTF{TH1S_lS_a_FAKE_flag}! ,过滤的东西通过动态常量绕过, Object.const_get("File").read("/flag") 。
# 数据管理系统(复现)
开发小哥为了下班粗心 buff 叠满,够领导拿着板子追着揍半条街。
提示 11.file 参数存在日志泄露
提示 22. 图片包含
Hint1 里说的是 file 参数存在日志泄露,结合主页的这里,推测可以通过这个读取到日志文件,可以先去读一下日志文件:

中间件是 Nginx,然后随便登录下发现登录方式是 GET 并且没加密: /?username=admin&password=asd ,可以读取下 Nginx 的日志文件,来获取账号密码,先用 GET 方式读下: ?file=/var/log/nginx/access.log ,并搜搜下 = admin:

账号密码有了,但是可能有干扰,得一个个试试,最后试出来这个对了 admin/bdsfasuaosdah42134223829@#! 。
在个人资料那里找到了文件上传的点,做一个图片马,整一个比较小的 png 图片,然后用 winhex 修改里面的一些内容的十六进制为木马的文本对应的十六进制,能绕过 getimagesize 校验:

在解压调试界面,分析前端 html,有个这个:

解压调试下面有个地方写了个 png 文件,那个就是我们传上去的木马文件,不过为啥最后一步的目录是这样的不知道,后面环境关了没来得及写,晚上宿舍断电,复现一半电脑没电关机了,第二天起来环境没了,所以贴一个 wp 里的截图:

最后尝试包含文件,getshell

# Pwn:
# nc 一下~
小明从系统后台中发现了一段有问题的日志,你能从中找到奇怪点并且消除吗?
nc 一下得到日志:
1 | 192.168.220.1 - - [02/Jul/2024:18:53:29 +0800] "GET /01 HTTP/1.1" 301 578 "http://192.168.220.132/"" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:144.0) Gecko/20100101 Firefox/144.0" |
经过分析,发现文件上传漏洞,且相关日志是:
1 | 192.168.220.1 - - [02/Jul/2024:18:53:57 +0800] "GET /01/data/upload/ HTTP/1.1" 200 1747 "http://192.168.220.132/01/index.php"" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:144.0) Gecko/20100101 Firefox/144.0" |
第二条很显然,POST 传输文件,第三条直接出现了访问木马的情况,那么第三条就是木马(后面全是 logout 和 login 操作,所以估计就是这两条)那么输入 02/Jul/2024:18:54:14+upd0te.php 通过第一关,第二关是个游戏,没搞懂什么 sum 逻辑,但还是不小心过了:
1 | 输入正确!恭喜来到病毒的世界,通过数字对战游戏战胜病毒即可消除它... |
# shop
1 | __int64 record_purchase() |
这里存在栈溢出漏洞,checksec 一下看看没有保护,nx 都没开,不需要尝试泄露 elf_base,逆向到密码: shopadmin123 ,这个函数里存在整数溢出漏洞:
1 | int manage_inventory() |
最后 exp:
1 | from pwn import * |
# Crypto
# 下个棋吧
先别做题了,flag 给你,过来陪我下把棋,对了,别忘了 flag 要大写,RERBVkFGR0RBWHtWR1ZHWEFYRFZHWEFYRFZWVkZWR1ZYVkdYQX0=
base64 解密得到 DDAVAFGDAX{VGVGXAXDVGXAXDVVVFVGVXVGXA} ,根据下棋内容搜索到是棋盘密码,得到 flag hectf{1145145201314} ,棋盘类型是 ADFGVX。最后 flag 为: HECTF{1145145201314}
# simple_math
一道普通的数学题,我会做数学题,你会做数学题吗?
1 | from Crypto.Util.number import * |
有 n,有 c,试着分解 n:
1 | from Crypto.Util.number import * |
有 p,q,n,c,e,但是 e 和 phi 不互素,rabin 进行三次二次开方,可以解:
1 | import gmpy2 |
# MISC
# 签到
关注凌武科技微信公众号,关注公众号后发送 “2025HECTF,启动!!!”,获得小惊喜!!!
做法就是题目描述字面意思。
# Check_In
🎵 🍑🎲⚽🍉 🚃
1 | ctf i love u -> 🎹🏀🌺 🎵 🍑🎲⚽🍉 🚃 |
这几个是一一对应的,hectf 就是🌹🍉🎹🏀🌺,可以得到一一对应的关系,之后得到:
1 | hectf{🚇elco⚾e_to_hectf_ho🏉e_💎ou_c🏓🌾_e🌾🍇o💎_it} |
看起来这个 flag 是可读的,找 ai 看着来来回回脑洞推测可能是 welcome to hectf hope you can enjoy it,最后的 flag:
1 | HECTF{welcome_to_hectf_hope_you_can_enjoy_it} |
# Reverse
# easyree
反编译之后,三个函数,先看第一个:
1 | __int64 __fastcall sub_1389(__int64 a1, __int64 a2, __int64 a3) |
一个异或,提取数据:
1 | unsigned char ida_chars[] = |
第二个函数,也是异或:
1 | __int64 __fastcall sub_143F(__int64 a1) |
提取数据:
1 | unsigned char ida_chars2[] = |
解密脚本:
1 |
|
输出结果:
1 | base64表异或后:ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9876543210+/ |
根据输出结果看出来是 base64 变表,解码得到 flag:
1 | HECTF{welc0m3_t0_rev3r3e_w0r1d_x1x1} |