行业资讯
📅 2026/8/12 20:19:53
BugKuCTF-WEB超详细解题思路(11-20)
本文仅用于网络安全技术学习与授权测试交流。本文实验皆在靶场进行任何未经授权使用文中技术的行为均与作者无关请务必遵守法律法规获得许可后方可进行渗透测试。目录变量1本地管理员game1源代码网站被黑bp好像需要密码shelleval需要管理员变量1题目信息进入靶场发现一堆php代码?php ​ error_reporting(0); include flag1.php; highlight_file(__file__); if(isset($_GET[args])){ $args $_GET[args]; if(!preg_match(/^\w$/,$args)){ die(args error!); } eval(var_dump($$args);); } ?代码分析include flag1.php;引入了一个文件里面有 Flag但它的变量名我们不知道可能是$flag也可能是$FLAG、$f1ag等。preg_match(/^\w$/, $args)做了严格的输入限制你传入的args只能是大小写字母、数字、下划线不能有空格、括号、分号等特殊符号。这直接把常见的system()、exec()等命令执行方式堵死了。eval(var_dump($$args););这里是考点$$args可变变量。如果你传入argshello那么$$args就代表变量$hello并把它打印出来。因为 Flag 藏在某个全局变量里而且题目限制了字符我们要用全局变量数组来直接“掀桌子”找答案。直接在你的靶机地址后面加上?argsGLOBALS原理传入argsGLOBALS→ 触发eval(var_dump($GLOBALS););。$GLOBALS是 PHP 的超级全局变量它是一个数组包含了当前脚本里所有已定义的变量包括flag1.php里的那个隐藏变量。页面会输出一个巨大的数组结构你在里面找一个类似[flag flag{...}]或者[FLAG ...]的键值对直接就能看到 Flag。ok找到flag本地管理员题目信息打开靶场我去一堆n绝对有猫腻按f12看一下源代码发现个编码也不知道是啥子解一下呗发现用base64解出来个test123嗯这个test123是啥看靶场页面是个登录框一般来说用户名都是admin这个我估计是密码咱们试一试。好好好不让登ip还被记录牛的。直接用bp抓包气煞我也。在网页中测试登录用户名输入admin一般都是这个用户名密码输入test123右键发送到重放器然后自己手动添加一个请求头注意后台提示本地管理员登录那本地的 IP 是 127.0.0.1找到flag耶game1题目信息哦建房子游戏经过我不懈的努力竟然达成200大分发现当游戏结束的时候多出来一个用get方式传输的score.php的文件其中 signzMNTA sign是一个base64的加密解一下码呗又开了一把得了25分发现这个参数的开头都是zMD的组合 那就base64解码后面的参数可以正好是25这个题应该是修改游戏得分拿到flag直接修改得分修改游戏数据真带劲。开一把的同时抓个包要改就改大些改成99999然后base64编一下码把bp里的替换一下搞定找到flag源代码题目信息进入靶场让我看看源代码看看呗结果一大堆编码页面里的 JS 代码被恶意转成了URL 编码%开头的十六进制之后用eval(unescape(...))拼接执行。把那段被混淆的 JS 解码后它原本的代码其实是function checkSubmit(){ var a document.getElementById(password); if(undefined ! typeof a){ if(67d709b2b54aa2aa648cf6e87a7114f1 a.value) return !0; alert(Error); a.focus(); return !1; } } document.getElementById(levelQuest).onsubmit checkSubmit;它的意思是当用户提交表单时会检查页面上 ID 为password的输入框的值。如果这个值等于67d709b2b54aa2aa648cf6e87a7114f1验证就通过。直接填入67d709b2b54aa2aa648cf6e87a7114f1成功搞到flag网站被黑题目信息打开靶场好炫酷的界面上面提示网站有漏洞咱们检查一下检查一下源代码等并无异常用dirsearch扫描网站目录试试看看能找到什么扫到了一个可疑的shell.php文件咱们来访问一下访问之后有个输入框让我们输入密码但我们不知道密码那就用bp爆破一下吧随便输入个密码然后抓包选中要爆破的位置点击添加选中提前准备的弱口令爆破文件爆破之后发现长度不同的便是爆破出来的密码密码是hack让我们试一试输入hack得到flagbp题目信息打开靶场是个登录框但咱们也不知道密码呀哦能爆破呀上一题就是先随便填写密码然后抓包发到爆破模块添加爆破点下载个字典进行爆破发现长度都一样,嗯怎么回事来看看响应红框里的 JavaScript 翻译过来是这样的// 硬编码创建一个对象code 值固定为 bugku10000 var r { code: bugku10000 } ​ // 如果 r.code 等于 bugku10000就显示“密码错误” if(r.code bugku10000){ console.log(e); document.getElementById(d).innerHTML Wrong account or password ; } // 如果不等于就跳转到成功页面 else{ console.log(0); window.location.href success.php?coder.code;这段 JS 里面完全没有读取你输入的密码。无论你通过 POST 提交password12345还是passwordadmin服务器返回的 HTML 里都固定是这段代码。代码里写死了如果 code 等于 bugku10000就故意报错。这就是为什么你发什么都显示错误因为页面里的 JS必定会执行这个报错分支既然 JS 里判断通过就会跳转到success.php?code...不需要在控制台里绕直接在浏览器地址栏输入http://你的靶机IP/success.php?code123嗯也不行。我们继续找线索诶我们发现点不一样的 之前都是var r { code: bugku10000 };但是这一次返回的是var r { code: hacker1000 };正是这个hacker1000决定了后端到底认哪个密码。因为r.code hacker1000JS 判断if(hacker1000 bugku10000)会返回False所以代码会跳过报错直接执行else分支的跳转window.location.href success.php?code r.code; // 等价于跳转到 success.php?codehacker1000这解释了为什么之前访问success.php?code123会一直返回no!因为服务端真实期望接收到的code应该是hacker1000好让我们试一试hack1000找到flag这道题是用ai摸索出来的原理这道题的原理非常典型在 CTF 中通常被称为“动态渲染 JS 前端逻辑分流”或者“伪登录回显泄露”。简单来说这是一道考验你在乱码混淆中寻找真实钥匙并绕过前端拦截机制的题目。我帮你把这套原理拆解成3 层递进的逻辑你一看就全明白了第一层服务端的“见人下菜碟”动态渲染 JS这是这道题最核心的一步。 后端check.php接收你提交的密码password后它不会直接告诉你成功还是失败而是根据你输入的密码动态生成一段包含不同信息的 HTML 返回给你。当你输入错误密码如123456时服务端返回的 JS 是var r { code: bugku10000 };当你输入正确答案zxc123时服务端返回的 JS 立马变成了var r { code: hacker1000 };这就是为什么你用 Intruder 爆破时只有zxc123这个请求的响应里出现了hacker1000这是后端给你的一个隐藏暗号。第二层前端 JS 的“引诱陷阱”逻辑分流服务端把 JS 发给前端浏览器后前端 JS 会运行这一段代码if(r.code bugku10000){ // 显示错误提示 } else { // 浏览器自动跳转到success.php?code当前r.code的值 }注意这里的陷阱bugku10000这个值就是一个“死胡同”。如果服务器给你返回bugku10000浏览器就会永远卡在报错页面。只有当r.code是其他值比如hacker1000时浏览器才会自动帮你跳转到正确的页面。第三层后端防伪校验最后一道关卡当浏览器触发跳转到success.php?codehacker1000时服务端success.php会读取 URL 里的code参数进行二次校验。如果code bugku10000后端会返回no!你之前访问失败就是因为这个。只有code hacker1000后端才会返回最终的 Flag。好像需要密码题目信息打开靶场又是输入框要求输入密码这次我还不信了继续用bp爆破随便输入密码然后抓包添加爆破点导入爆破字典进行爆破。哦这回爆破出来了密码12468试一下找到flagshell题目信息打开之后靶场是空白的看一下题目提示$poc a#s#s#e#r#t; # 定义了一个名为POC的变量 $poc_1 explode(#, $poc); # 使用了中的ecplode函数作用是按照指定的分割符#对变量字符串进行分割分割后是一个数组 $poc_2 $poc_1[0].$poc_1[1].$poc_1[2].$poc_1[3].$poc_1[4].$poc_1[5]; # 将分割后的数组内容进行拼接显然是一个单词assert $poc_2($_GET[s])# 将拼接后的字符串 $poc_2 作为函数名也就是assert()函数 ​ $_GET[] 表示使用的是get请求传入一个名为s的参数然后将这个参数值作为assert函数的内容得到初步的POC:http://160.202.254.160:14479/?s然后简单测试一下system 函数进行输出http://160.202.254.160:14479/?ssystem(%22whoami%22)执行成功接下来找flag先遍历一下也就是ls 找到关于flag的txt文件直接访问flaga15808abee46a1d5.txt找到flageval题目信息打开靶场发现下面这段代码?php include flag.php; $a $_REQUEST[hello]; eval( var_dump($a);); show_source(__FILE__); ?include flag.php;把存有 Flag 的文件包含进来此时$flag变量已经存在于内存里了。$a $_REQUEST[hello];通过 GET 或 POST 获取用户传入的hello参数并赋值给$a。符号用来忽略没传参数时的报错。eval(var_dump($a););考点所在。eval()会把括号里的字符串当作 PHP 代码执行。这里的$a被直接拼接进了要执行的 PHP 代码中。show_source(__FILE__);把当前这份源码输出给用户看。因为$a是用户控制的而它被直接拼入了eval执行。只要把 URL 参数精心构造一下闭合掉前面的var_dump(, 然后塞入任意 PHP 代码最后注释掉后面的符号就能实现任意代码执行RCE。方法一直接读变量?hello$flag原理拼接后变为eval(var_dump($flag););直接把flag.php里包含进来的 Flag 变量打印出来。因为flag.php文件里的$flag变量被出题人故意赋值成了Too Young Too Simple这个提示语这是 BugkuCTF 非常经典的坑。 当你传hello$flag时后端拼接后执行的是eval(var_dump($flag););所以只会打印出这个提示语。真正的 Flag 并不在$flag变量里而是藏在flag.php的源码里先用方法一失败真正的 Flag 并不在$flag变量里而是藏在flag.php的源码里方法二读文件源码最直观?hello1);show_source(flag.php);//原理拼接后变成了eval(var_dump(1);show_source(flag.php);//);。前面的1);凑齐了var_dump(1);。中间的show_source(flag.php);直接读取并打印flag.php的源码。后面的//注释掉了结尾多余的一个);避免语法报错。找到flag需要管理员题目信息根据题目提示应该是需要管理员身份的。我们进去以后查看源码啥也没有dirsearch扫目录发现有个robots.txt可以访问我们直接访问。然后我们访问发现一个resusl.php访问resusl.php可恶又给我记录了它说我不是管理员并且记录了我的ip然后下面是它GET传了一个x参数看是不是等于password变量的值我们可以尝试传?xadminok我们真厉害找到flag啦