news 2026/8/12 20:19:47

BugKuCTF-WEB超详细解题思路(11-20)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
BugKuCTF-WEB超详细解题思路(11-20)

本文仅用于网络安全技术学习与授权测试交流。本文实验皆在靶场进行,任何未经授权使用文中技术的行为均与作者无关,请务必遵守法律法规,获得许可后方可进行渗透测试。

目录

变量1

本地管理员

game1

源代码

网站被黑

bp

好像需要密码

shell

eval

需要管理员


变量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);"); } ?>

代码分析:

  1. include "flag1.php";引入了一个文件,里面有 Flag,但它的变量名我们不知道(可能是$flag,也可能是$FLAG$f1ag等)。

  2. preg_match("/^\w+$/", $args):做了严格的输入限制,你传入的args只能是大小写字母、数字、下划线,不能有空格、括号、分号等特殊符号。这直接把常见的system()exec()等命令执行方式堵死了。

  3. eval("var_dump($$args);");:这里是考点$$args(可变变量)。如果你传入args=hello,那么$$args就代表变量$hello,并把它打印出来。

因为 Flag 藏在某个全局变量里,而且题目限制了字符,我们要用全局变量数组来直接“掀桌子”找答案。

直接在你的靶机地址后面加上:

?args=GLOBALS

原理:

  1. 传入args=GLOBALS→ 触发eval("var_dump($GLOBALS);");

  2. $GLOBALS是 PHP 的超级全局变量,它是一个数组,包含了当前脚本里所有已定义的变量(包括flag1.php里的那个隐藏变量)。

  3. 页面会输出一个巨大的数组结构,你在里面找一个类似["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的文件

其中: sign=zMNTA=== 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,得到flag!

bp

题目信息

打开靶场是个登录框,但咱们也不知道密码呀,哦?能爆破呀,上一题就是

先随便填写密码,然后抓包

发到爆破模块,添加爆破点

下载个字典进行爆破

发现长度都一样,嗯?怎么回事,来看看响应

红框里的 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?code='+r.code;
  1. 这段 JS 里面完全没有读取你输入的密码。无论你通过 POST 提交password=12345还是password=admin,服务器返回的 HTML 里都固定是这段代码。

  2. 代码里写死了:如果 code 等于 bugku10000,就故意报错。这就是为什么你发什么都显示错误,因为页面里的 JS必定会执行这个报错分支!

既然 JS 里判断通过就会跳转到success.php?code=...,不需要在控制台里绕,直接在浏览器地址栏输入:

http://你的靶机IP/success.php?code=123

嗯?也不行。我们继续找线索

诶,我们发现点不一样的 之前都是var r = { code: 'bugku10000' };但是这一次返回的是var r = { code: 'hacker1000' };

正是这个hacker1000决定了后端到底认哪个密码。

因为r.code = 'hacker1000',JS 判断if('hacker1000' == 'bugku10000')会返回False,所以代码会跳过报错,直接执行else分支的跳转:

window.location.href = 'success.php?code=' + r.code; // 等价于跳转到 success.php?code=hacker1000

这解释了为什么之前访问success.php?code=123会一直返回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?code=hacker1000时,服务端(success.php)会读取 URL 里的code参数进行二次校验。

  • 如果code = bugku10000,后端会返回no!(你之前访问失败就是因为这个)。

  • 只有code = hacker1000,后端才会返回最终的 Flag

好像需要密码

题目信息

打开靶场,又是输入框要求输入密码,这次我还不信了,继续用bp爆破,随便输入密码,然后抓包

添加爆破点

导入爆破字典,进行爆破。哦!这回爆破出来了,密码12468

试一下,找到flag!

shell

题目信息

打开之后,靶场是空白的

看一下题目提示

$poc = "a#s#s#e#r#t"; # 定义了一个名为POC的变量 $poc_1 = explode("#", $poc); # 使用了PHP中的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/?s=system(%22whoami%22)

执行成功,接下来找flag

先遍历一下,也就是ls ,找到关于flag的txt文件

直接访问flaga15808abee46a1d5.txt,找到flag!

eval

题目信息

打开靶场发现下面这段代码

<?php include "flag.php"; $a = @$_REQUEST['hello']; eval( "var_dump($a);"); show_source(__FILE__); ?>
  1. include "flag.php";:把存有 Flag 的文件包含进来(此时$flag变量已经存在于内存里了)。

  2. $a = @$_REQUEST['hello'];:通过 GET 或 POST 获取用户传入的hello参数,并赋值给$a@符号用来忽略没传参数时的报错。

  3. eval("var_dump($a);");考点所在eval()会把括号里的字符串当作 PHP 代码执行。这里的$a被直接拼接进了要执行的 PHP 代码中。

  4. 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的源码里!

方法二(读文件源码,最直观):

?hello=1);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变量的值,我们可以尝试传?x=admin

ok,我们真厉害,找到flag啦!

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

豆顶顶 GEO:助力太仓本地企业挖掘自然搜索线索

先问个问题&#xff1a;你在豆包、DeepSeek里搜过“太仓精密加工厂家推荐”或者“太仓汽车零部件供应商哪家好”吗&#xff1f; 太仓一个做精密注塑的老周试了一下&#xff0c;回来有点纳闷。他在本地干了十几年&#xff0c;设备、品控、客户口碑都在线&#xff0c;结果AI推荐了…

作者头像 李华
网站建设 2026/8/12 20:14:39

prysm处理相位图

文章目录加载数据孔径内分析加载数据 prysm提供了干涉仪类&#xff0c;用相位图的后处理。在其sample_data中&#xff0c;贴心地提供了Zygo干涉仪的测量数据&#xff0c;其地址为&#xff1a;https://github.com/brandondube/prysm/raw/master/sample_files/&#xff0c;其中的…

作者头像 李华
网站建设 2026/8/12 20:14:23

嵌入式Linux设备树详解:从原理到RK3568实战应用

1. 从“硬编码”到“软配置”&#xff1a;为什么我们需要设备树&#xff1f; 如果你是从单片机或者早期的嵌入式Linux开发转过来的&#xff0c;一定对这样的场景不陌生&#xff1a;为了点亮一块新的开发板&#xff0c;你需要一头扎进内核源码的 arch/arm/mach-xxx/ 目录下&am…

作者头像 李华
网站建设 2026/8/12 20:14:14

Python自动化水文年鉴数据处理:从PDF/Excel到结构化数据的实战方法

1. 项目缘起&#xff1a;水文年鉴数据处理中的“脏活累活”干了这么多年水文数据分析&#xff0c;最头疼的环节之一&#xff0c;就是从那些五花八门、格式各异的水文年鉴里把数据“抠”出来。这活儿&#xff0c;说难吧&#xff0c;原理不复杂&#xff1b;说简单吧&#xff0c;真…

作者头像 李华
网站建设 2026/8/12 20:14:12

Evaluation of Large Language Models for Numeric Anomaly Detection in Power Systems

文章主要内容与创新点总结 一、主要内容 本文聚焦大型语言模型(LLMs)在电力系统数值异常检测(AD)中的应用,针对现有研究中LLMs在结构化数值遥测数据处理方面的不足,以GPT-OSS-20B为代表模型,在IEEE 14节点系统上开展了全面评估。 研究背景:LLMs在自然语言处理、代码生…

作者头像 李华
网站建设 2026/8/12 20:13:27

Java图书管理系统实战:从JDBC到三层架构的完整项目构建

1. 项目概述&#xff1a;从零构建一个扎实的Java图书管理系统 最近在整理自己的项目仓库&#xff0c;翻出了这个几年前写的图书管理系统。它不是什么惊世骇俗的架构&#xff0c;也没有用到当下最时髦的微服务或云原生&#xff0c;但恰恰是这样一个“经典”的课程设计级项目&…

作者头像 李华