news 2026/10/10 6:44:42

攻防世界web2:PHP代码审计与逆向算法解密实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
攻防世界web2:PHP代码审计与逆向算法解密实战

做Web方向CTF的朋友,对“攻防世界”这个靶场应该不陌生。我是一路从新手题刷过来的,印象最深的倒不是那些一两步就能出flag的送分题,而是像web2这样“把源码甩在你脸上,让你逆向出flag”的题目。它不考SQL注入,也不考XSS,就考你面对一段陌生PHP代码时能不能沉住气做代码审计,能不能把加密逻辑一步步剥开。这篇文章就把我做攻防世界web2这道题的完整过程、踩过的坑以及背后的审计思路写下来,适合刚接触CTF、准备打比赛、或者想系统学PHP代码审计的朋友。

我当时看到题目页面的第一反应是:整个页面只有一个PHP文件,没有输入框,没有URL参数,没有任何传统意义上的漏洞点。页面顶部是一串长长的密文,下面是highlight_file(FILE)打印出来的源码,以及一行注释“逆向上述算法”。这种题目在CTF里非常典型,核心不是“找漏洞”,而是“读代码、逆算法”。把这一道吃透,后面再碰到给源码的加密题,基本都能举一反三。

1. 拿到题目后的第一眼:源码审计与考点拆解

1.1 题目给到的原始代码

我复刻的版本大致是这样:

<?php $miwen="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwWxLlwiE4pqJZiCsJ41gwLhQrB8YkyCCH5Gx0VvMxzG5VtXyFzN1c="; function encode($str){ $_o = strrev($str); for($_0=0; $_0<strlen($_o); $_0++){ $_c = substr($_o, $_0, 1); $__ = ord($_c) + 1; $_c = chr($__); $_ = $_c . $_; } return str_rot13(strrev(base64_encode($_))); } highlight_file(__FILE__); /* 逆向上述算法 */

不同平台的复刻版本在变量名上可能有出入,但算法骨架是一致的。页面顶部那一串密文放在$miwen变量里,它是我们最终要还原的目标;encode函数负责把任意字符串加密成密文;highlight_file(__FILE__)把当前文件源码直接输出到浏览器,所以整段逻辑在页面上暴露无遗;最后的注释说得很明白——要你把算法逆向回去。这种情况下,题目的信息量其实非常大:密文有了,算法有了,甚至连方向都告诉你了,剩下的就是阅读代码和推导逆运算。

这种给源码的题目,出题人通常不会故意把代码写得看不清,真正难的是阅读逻辑和还原编码链路。很多新手一看到str_rot13、ord这些函数就慌,其实拆开看,每个函数单独拎出来都很基础。关键在于能不能沉住气,把函数调用链一步步捋清楚。

1.2 逐行拆解encode函数

把encode函数拆开看,它是五个步骤的流水线。

第一步,strrev($str)把字符串反转。比如abc变cba。这一步很关键,因为后面所有处理都基于反转后的字符串,逆向的时候要特别注意把它还原。

第二步,进入for循环。循环从字符串索引0开始,逐个取出字符,用ord()取到ASCII码值,加1,再用chr()转回字符。这步的本质就是“原字符串每个字符的ASCII码加1”。可能有人会疑惑,循环里有个拼接操作$_ = $_c . $_,它把新字符放在已有字符串前面,这样顺序会不会乱?我用abc验证过:反转后是cba,字符加1后逐个往前拼,最后得到bcd,顺序恰好又回到了初始顺序。所以可以放心把它理解为逐字符加1。

第三步,对结果做base64_encode。得到一串包含大小写字母、数字、+、/和末尾可能带=的base64字符串。

第四步,再次strrev,把base64字符串整体反转。

第五步,执行str_rot13。这是ROT13加密,一种凯撒位移,英文字母位移13位。因为字母正好26个,ROT13自逆,连续执行两次等于没处理。它只影响字母,数字和符号保持不变。

整条链路就是:字符串逐字符ASCII加1,转base64,反转,再做ROT13。

1.3 这道题到底在考什么

有人可能会问,这道题里没有任何注入点,也没有疑似漏洞的函数,它想考什么?我整理了一张考点表。

考点考察目的
PHP字符串函数是否熟悉strrev、ord、chr、substr等基础函数的行为边界
编码算法识别能否识别出base64、ROT13、字符位移这些常见编码
逆向思维能否根据正向算法推导出完整的逆运算顺序
代码审计习惯面对陌生函数时,能不能逐行读、逐行验证,而不是只看个大概

这张表放在一起看就清楚了,这道题不是要你利用漏洞,而是要你具备“把代码当作数据处理流程”的能力。CTF的Web题目并不只有SQL注入和XSS,大量源码审计题都在考察这种基本功。刷题时如果只看现成exp而不自己走一遍,这道题的价值就浪费了一大半。

2. 逆推思路与准备工具

2.1 先画逆向流程,不要急着写脚本

我见过不少朋友拿到这类题第一反应就是找在线解密工具,把密文丢进去碰运气。我不推荐这么干,因为在线工具很难一次猜中编码组合。更稳妥的做法是把整条编码链画成一张流程表,然后从最后一个操作开始逐个逆推。

一个很合适的类比是穿衣服:早上穿衣服是从内到外,晚上脱衣服就得从外到内。编码也是这样,str_rot13是最后一步,逆向时就要最先处理。我习惯在纸上画两列,左边是正向顺序,右边是逆向顺序,中间用箭头连起来。画完之后思路会非常清晰,也不容易漏掉反转操作。

2.2 工具选择:Python脚本比PHP调试更顺手

解这类题,我推荐直接写Python脚本。第一,Python对base64、ROT13都有现成库支持,代码量很少;第二,Python交互式环境可以一行一行验证中间结果,比在PHP里反复跑函数方便得多。

当然,如果你手头有PHP环境,也可以把源码复制下来直接执行,尤其适合观察原函数每一步的输出。但最终逆向脚本我建议用Python,因为它的字符串和字节处理逻辑更直观,调试成本更低。在线站点最多作为辅助验证,不建议作为主要解法,尤其是密文可能被第三方获取,而且在线工具很难处理多步自定义算法。

2.3 用短字符串先验证逻辑

在真正解密之前,强烈建议先用短字符串把“正向加密-逆向解密”的闭环跑通。我用的是test:用题目的正向算法加密得到密文,再用自己写的逆向逻辑解密,如果最终回到test,说明逆向流程没搞反。

这一步很多人会省略,结果就是一解真实密文就乱码,然后怀疑人生。其实只要花一分钟先自测一把,顺序问题立刻暴露。我写脚本时先用这个测试用例跑通了,才去处理题目密文。这个习惯后来帮我解决了很多变体题,比盲目改参数高效得多。

3. 完整解密实操

3.1 逆运算的具体步骤

现在正式逆向。按照“后进先出”的原则,decode过程分四步:

  1. 执行str_rot13,还原到反转前的结果。ROT13是自逆算法,直接对密文操作即可。
  2. 执行strrev,把base64字符串的顺序恢复原样。
  3. 执行base64_decode,得到被ASCII+1处理过的那段字符串。
  4. 遍历每个字符,把ASCII码值减1,还原原始明文。

这里唯一的坑是很多人容易纠结第一步和第二步之间要不要先反转。我的判断方法很简单:看正向算法最后一个操作是str_rot13,那逆向第一步就是str_rot13;正向倒数第二步是strrev,那逆向第二步就是strrev。逐个对位,很难出错。

3.2 一跑到底的Python脚本

下面是我实际使用的解密脚本:

import base64 import codecs miwen = "a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwWxLlwiE4pqJZiCsJ41gwLhQrB8YkyCCH5Gx0VvMxzG5VtXyFzN1c=" # 第一步:ROT13解码 tmp = codecs.decode(miwen, 'rot13') # 第二步:反转 tmp = tmp[::-1] # 第三步:base64解码 tmp = base64.b64decode(tmp).decode() # 第四步:每个字符ASCII码减1 flag = ''.join(chr(ord(ch) - 1) for ch in tmp) print(flag)

脚本逻辑很直白:先做codecs.decode(miwen, 'rot13')完成ROT13解码,再反转,再base64解码,最后逐字符减1。要注意base64.b64decode返回的是bytes对象,所以加.decode()转成字符串方便遍历。

把题目里的密文填进miwen变量,运行脚本后,终端直接输出原始flag,常见格式是cyberpeace{...}。我在自己的环境里跑完直接拿到了flag,整个过程不超过一分钟。如果读者在其他复现站点里密文不同,把变量替换掉就行。

3.3 分步打印与中间结果验证

为了让过程更直观,我把同样的逻辑分步打印出来,这里用测试明文test做演示:

import base64 import codecs def encode(s): tmp = s[::-1] res = '' for ch in tmp: res = chr(ord(ch) + 1) + res res = base64.b64encode(res.encode()).decode() return codecs.encode(res[::-1], 'rot13') def decode(s): tmp = codecs.decode(s, 'rot13') tmp = tmp[::-1] tmp = base64.b64decode(tmp).decode() return ''.join(chr(ord(ch) - 1) for ch in tmp) plain = "test" secret = encode(plain) print("加密结果:", secret) print("解密结果:", decode(secret)) print("是否还原:", decode(secret) == plain)

我本地跑出来的输出很有意思:test被加密成==Dq0MJq,再解密回去仍然得到test,是否还原显示True。这就证明了整套逆运算顺序没有错。如果中间任何一步处理顺序反了,这个测试会直接给出乱码。所以以后遇到类似题目,完全可以把这段测试脚本当模板,先自测再解真实密文。

3.4 用PHP再跑一遍确认

虽然Python脚本已经够用,但有的朋友觉得PHP环境更顺手,毕竟源码本身就是PHP。我用PHP也写了一个版本:

<?php $miwen = "a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwWxLlwiE4pqJZiCsJ41gwLhQrB8YkyCCH5Gx0VvMxzG5VtXyFzN1c="; $tmp = str_rot13($miwen); $tmp = strrev($tmp); $tmp = base64_decode($tmp); $flag = ''; for ($i = 0; $i < strlen($tmp); $i++) { $flag .= chr(ord($tmp[$i]) - 1); } echo $flag; ?>

这个脚本和Python版本完全等价。base64_decode在PHP里直接返回字符串,不需要额外转码。用PHP执行一次,输出结果和Python版本一致。两个脚本我都跑过,可以互相验证,也算是一种交叉确认。尤其是当你对某个平台的执行结果存疑时,换个语言跑一遍往往能更快定位问题。

4. 常见问题与排查技巧实录

4.1 解密顺序搞反怎么办

我踩过最深的坑就是把反转和base64解码的顺序搞反。当时我拿到密文直接先base64解码,得到一串看起来有规律的字母,再反转再减1,结果全是乱码。后来我用test做测试,把正向加密每一步的中间结果都打印出来,才发现base64解码必须紧跟在反转之后。

如果遇到乱码,不要改参数瞎试,先回到测试明文验证。用assert decode(encode(明文)) == 明文这种方式跑一遍,如果返回False,说明逆向顺序有问题。剩下的就是逐个调整步骤顺序,直到断言通过为止。这是效率最高的排查方式,比盯着密文猜测强太多。

4.2 base64解码报错或输出bytes

Python的base64.b64decode返回bytes对象,直接按字符遍历会得到数字,所以必须加.decode()。但解密过程中如果出现不可见字符,直接用utf-8解码可能抛异常。这时候可以改用latin1解码,或者干脆不转字符串,直接遍历bytes做ASCII减1操作。

raw = base64.b64decode(tmp) flag = bytes([b - 1 for b in raw]).decode()

这种做法更健壮,因为bytes本身就是字节数组,每个元素对应一个ASCII码值,减1之后用bytes()重新组装,最后再统一解码。我后来处理带特殊字符的密文时优先用这个写法,基本不会翻车。如果密文里本身包含非ASCII字符,按字节处理还能避免编码猜测带来的偏差,可控性会好很多。

4.3 密文变量从哪里拿

不同版本的web2题目,密文位置可能不太一样。有的放在页面顶部,有的放在PHP注释里,还有的需要打开浏览器开发者工具,在源码中找$miwen的赋值位置。如果页面引入了外部文件,比如include了某个配置脚本,密文也可能藏在那个文件里。

遇到这种情况,别只看当前文件,要多点几步把PHP文件都下载下来,搜索miwen或者相似关键词。我在刷题时习惯给每道题建立单独的目录,遇到关键源码就留档,方便复盘和对比版本差异。这个习惯看起来简单,等你要写题解或者复现旧题时就知道多省事了。

4.4 其他类型的web2变体

攻防世界平台上的web2并不只有我上面讲的一种,不同赛季、不同复刻站里可能被改成别的考点。我见过比较常见的有:把ROT13换成凯撒位移、把ASCII+1换成一串自定义字符替换表、在base64之前混入URL编码、甚至把明文拆成两段分别处理再拼接。

不管变体怎么改,解题流程都一样:先把源码里每一步操作画出来,标注输入输出格式;从最后一步逆向推导到第一步;用短字符串做闭环验证。这四步走完,绝大多数变形都能解。如果题目涉及自定义字符替换,无非是在逆运算里加一张反向映射表而已,思路并没有变化。

5. 从web2延伸出来的Web审计能力

5.1 给源码题为什么高频出现

不少新手以为CTF Web就是注入和XSS,实际上源码审计题在各大平台里占比非常高。原因很简单:真实业务里的安全问题,大多藏在一堆业务代码里,不会像靶场那样给你一个明显的注入参数。出题人用web2这种“给源码、逆向算法”的形式,训练的就是选手面对未知代码时的处理能力。

这种能力到了复杂题目里依然有用。比如攻防世界后续的web3、web4中,经常出现加密混在业务逻辑里的情况,你不把算法读明白,连参数传什么都搞不清楚。所以web2虽然难度不大,但它建立的读码方法,是可以直接迁移到更高级别题目上的。

5.2 相关考点延伸与自查清单

刷完web2之后,建议按这张清单自查:

自查点具体内容
PHP字符串函数能否准确说出strrev、substr、ord、chr的行为
base64特征能否一眼看出末尾=和字符集特征
ROT13特征能否识别只有字母变化、符号不变的加密结果
逆向顺序能否画出正向和逆向的完整流程图
脚本验证能否用短字符串闭环测试,确保脚本逻辑正确

这五点都能做到,基本就合格了。如果还有不熟的地方,可以自己改造encode函数,比如把ROT13改成ROT5、把ASCII+1改成ASCII-1,再写一遍解密脚本,练到完全流畅为止。这种主动给自己出变体的练习方式,比刷十道重复题都管用。

5.3 刷题建议与脚本库沉淀

最后给正在刷题的朋友一个很实用的建议:把常用解码脚本沉淀成本地工具库。我会在本地维护一个solve.py,里面包含base64、URL编码、ROT13、十六进制、字符位移等常见编解码的封装函数,每次遇到编码题直接调用,省去重复造轮子。

做web2这道题时,我的脚本库已经写好了base64和rot13函数,所以整个解密脚本只有十几行。如果刚开始刷题,不要急着追求效率,先把每个函数的实现原理搞清楚,再逐步积累。刷到二三十道Web题之后,你会发现很多题目都是在组合使用同一个工具箱,到时候速度自然就上来了。

最后说一点个人体会。做完web2之后,我给自己定了一个规矩:凡是看到“给源码,求flag”的题,先在纸上把函数调用链画出来,标清楚哪一步是正向、哪一步要逆向,再用短字符串验证一遍。这个习惯确实是靠这道题养成的,后来刷攻防世界的后续Web题,遇到变体就没再在顺序上栽过跟头。希望这篇记录能帮你少走几步弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 6:44:28

Cursor额度不够用?Kiro 550配额实测与迁移指南

最近一个月&#xff0c;我的 Cursor 额度又双叒见底了。作为一个每天要在编辑器里泡十几个小时的人&#xff0c;AI 补全对我来说已经是某种“生理依赖”&#xff0c;额度一断&#xff0c;写代码的速度直接腰斩&#xff0c;那种“每次回车前都要想一下这行值不值得让 AI 补”的感…

作者头像 李华
网站建设 2026/10/10 6:44:25

Linux下gcc/g++编译原理与静态库、动态库实战详解

如果你刚接触Linux&#xff0c;大概率见过这样一个画面&#xff1a;在终端里敲下gcc hello.c -o hello&#xff0c;回车&#xff0c;程序就跑起来了。很多人折腾了一上午编辑器快捷键&#xff0c;最后才意识到&#xff0c;真正把代码变成程序的&#xff0c;其实是这么一条命令。…

作者头像 李华
网站建设 2026/10/10 6:42:47

模板代码的性能体检:从GB/T 39788到JMH的实战指南

1. 模板代码的性能黑洞&#xff1a;藏在最不起眼的地方1.1 我遇到的一次线上P99翻车上个月我们做大促前的容量摸底&#xff0c;有个查询接口的P99延迟从平时的80毫秒直接飙到350毫秒。第一反应是数据库慢查询&#xff0c;DBA把慢日志拉出来看了半天&#xff0c;SQL都很正常。后…

作者头像 李华
网站建设 2026/10/10 6:42:36

运维必备:100条CMD命令实战指南,覆盖网络诊断到批处理自动化

干了十几年运维&#xff0c;我有个特别深的感触&#xff1a;不管图形界面做得怎么花哨&#xff0c;遇到系统抽风的时候&#xff0c;第一个能救场的永远是命令行。Windows 系统里&#xff0c;CMD 命令行到现在依然是我日常排查故障、批量处理终端的核心工具。今天我要分享的这份…

作者头像 李华
网站建设 2026/10/10 6:42:23

开源智能体OpenClaw养成指南:记忆、知识库与反馈调优实战

“越养越聪明”这句话听上去像营销话术&#xff0c;但我把 OpenClaw 从零配起来、连续用了两个多月之后&#xff0c;发现它还真不是玄学。OpenClaw 本质上是一套开源智能体框架&#xff0c;底层接大模型&#xff0c;外面套了记忆、知识库、工具调用和反馈修正这几层。它和普通聊…

作者头像 李华
网站建设 2026/10/10 6:41:47

从作业5看项目式作业的完整交付流程:需求到复盘的实战指南

作业这东西&#xff0c;看着是交差&#xff0c;其实是最划算的练习机会。很多同学拿到作业5这种题目&#xff0c;第一反应是赶紧做完拉倒&#xff0c;但我这几年带项目、自己也反复改方案&#xff0c;发现一份作业的完成质量&#xff0c;直接暴露了你对整块知识的掌握程度。今天…

作者头像 李华