一、背景說明
說實話自己是做安全的,平時總是給別人代碼找茬,但輪到自己寫代碼有時比開發還不注重安全,其中有安全腳本一般比較小考慮安全那么處理安全問題的代碼比重將會大大超過業務代碼的問題也有不是專職開發添加一項功能還沒開發那么熟練的問題。由於安全腳本一般不是對外開啟服務的,所以一般也不會暴出什么問題。
但前段時間被拉群通知說自己寫的一個腳本存在命令注入漏洞,開始很訝異,經溝通和分析之后發現是因為腳本有通過system調用一個二進制文件上報執行存在風險命令的操作,當構造一個命中風險判定規則且進一步構造注入語法的命令時,即能實現命令注入。
二、直接使用shlex.quote()進行修復
防范命令入注我們一般都會建議過濾\n$&;|'"()`等shell元字符,但自己實現一個個字符處理還是比較麻煩的,我們盡量尋求一種簡單的方法最好是現成的函數或者庫進行處理。
最后在這里看到說可以使用shlex.quote()進行處理:https://python-security.readthedocs.io/security.html#shell-command-injection
示例代碼如下:
import shlex filename_para = 'somefile' command = 'ls -l {}'.format(filename_para) print("except result command:") print("{}\n".format(command)) filename_para= 'somefile; cat /etc/passwd' command = 'ls -l {}'.format(filename_para) print("be injected result command:") print("{}\n".format(command)) filename_para = 'somefile; cat /etc/passwd' command = 'ls -l {}'.format(shlex.quote(filename_para)) print("be injected but escape result command:") print("{}\n".format(command))
運行結果如下:
三、自行寫代碼實現shlex.quote()進行修復
shlex.quote()是Python3.3之后才引入的,shlex.quote()代碼也很簡單,如果要兼容沒有該函數的Python版本可以自行實現該函數。
shlex.quote()源代碼鏈接:https://github.com/python/cpython/blob/master/Lib/shlex.py#L325
從原理上看,shlex.quote()所做的就是兩件事,一是在字符串最外層加上單引號使字符串只能做為一個單一體出現,二是將字符串內的單引號用雙引號引起來使其失去可能的閉合功能。
示例代碼如下:
import re def quote(s): """Return a shell-escaped version of the string *s*.""" # return if string in null if not s: return "''" # _find_unsafe = re.compile(r'[^\w@%+=:,./-]', re.ASCII).search # return if string have no those char. if re.search(r'[^\w@%+=:,./-]', s, re.ASCII) is None: return s # use single quotes, and put single quotes into double quotes # the string $'b is then quoted as '$'"'"'b' return "'" + s.replace("'", "'\"'\"'") + "'" if __name__ == "__main__": filename_para = 'somefile' command = 'ls -l {}'.format(filename_para) print("except result command:") print("{}\n".format(command)) filename_para= 'somefile; cat /etc/passwd' command = 'ls -l {}'.format(filename_para) print("be injected result command:") print("{}\n".format(command)) filename_para = 'somefile; cat /etc/passwd' command = 'ls -l {}'.format(quote(filename_para)) print("be injected but escape result command:") print("{}\n".format(command))
運行結果如下:
參考: