本文仅用于网络安全技术学习与授权测试交流。本文实验皆在靶场进行,任何未经授权使用文中技术的行为均与作者无关,请务必遵守法律法规,获得许可后方可进行渗透测试。
目录
变量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(可变变量)。如果你传入args=hello,那么$$args就代表变量$hello,并把它打印出来。
因为 Flag 藏在某个全局变量里,而且题目限制了字符,我们要用全局变量数组来直接"掀桌子"找答案。
直接在你的靶机地址后面加上:
?args=GLOBALS
原理:
-
传入
args=GLOBALS→ 触发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的文件
其中: 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;
-
这段 JS 里面完全没有读取你输入的密码 。无论你通过 POST 提交
password=12345还是password=admin,服务器返回的 HTML 里都固定是这段代码。 -
代码里写死了:
如果 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__); ?>
-
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 的源码里!

方法二(读文件源码,最直观):
?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啦!
