“
閱讀本文大概需要 3 分鐘,
”大家好,我是崔慶才,
前幾天看到了一個開源專案中的遠程執行漏洞,開發者通過 HTTP 介面暴露了一個引數,接收引數之后,代碼中將該引數拼接了一個命令,然后把這個命令使用 Python 中的 Popen 執行了,然后把對應的輸出結果回傳到了 HTTP Response 中,
這開發者都這么不注意的嗎?這誰干的好事啊?
沒錯,那個開發者就是我,,,
是的,我在我的開源專案 Gerapy 里面寫出來了這個漏洞,然后喜提 CVE:https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-43857,
這個漏洞的安全級別設定為高,披露文章見:注意!Gerapy 作業系統命令注入漏洞
不過還好,我還加了一層防護,那就是登錄驗證,所以如果不登錄是沒法利用的,所以影響還好,所以大家如果 Gerapy 有設定好密碼,密碼不被爆破出來的話,那就問題不大,
現在問題已經修復,大家可以升級到 Gerapy 最新版本,目前是 0.9.9 版本,
但總之,這個程序我學到了很多東西,這里來記錄下這個漏洞是怎么產生的以及怎么規避,
由于 Gerapy 里面的那個邏輯相對復雜,為了便于說明,這里我來試著用一個例子復現一下這個漏洞,并說明下這個漏洞的修正方法,
示例復現
這個漏洞主要涉及到 Python 中 Popen 的使用,
比如,這里我定義一個方法,接收一個引數 host,然后拼接了一個 dig 命令,用于查找這個 host 的決議地址,代碼如下:
from subprocess import Popen, PIPE
def execute(host):
cmd = f'dig {host}'
p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
stdout, stderr = p.stdout.read(), p.stderr.read()
return stdout if not stderr else stderr
result = execute('www.baidu.com')
print(result)
拼接完命令之后,我呼叫了 Popen 執行了這條命令,并且捕獲輸出結果和錯誤,如果沒有錯誤就回傳輸出結果,如果有錯就回傳錯誤,
然后我呼叫了 execute 方法,并傳入了 www.baidu,com,一切都看起來很自然對不對?
運行結果類似如下:
; <<>> DiG 9.10.6 <<>> www.baidu.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45141
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.baidu.com. IN A
;; ANSWER SECTION:
www.baidu.com. 253 IN CNAME www.a.shifen.com.
www.a.shifen.com. 261 IN CNAME www.wshifen.com.
www.wshifen.com. 300 IN A 119.63.197.151
www.wshifen.com. 300 IN A 119.63.197.139
;; Query time: 148 msec
;; SERVER: 2001:4898::1050:5050#53(2001:4898::1050:5050)
;; WHEN: Wed Dec 29 01:25:39 CST 2021
;; MSG SIZE rcvd: 127
沒啥問題對不對?傳入了一個域名,輸出了 dig 命令之后域名的決議結果,
可是,如果這時候攻擊者傳入的命令是非法的,比如這樣呼叫:
result = execute('www.baidu.com; cat /etc/passwd')
結果會怎樣?!它在后面額外又拼接了一條 cat /etc/passwd ,這是非常危險的命令,直接將機器的相關用戶資訊輸出出來,
運行結果就會變成這樣子:
; <<>> DiG 9.10.6 <<>> www.baidu.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45141
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.baidu.com. IN A
;; ANSWER SECTION:
www.baidu.com. 253 IN CNAME www.a.shifen.com.
www.a.shifen.com. 261 IN CNAME www.wshifen.com.
www.wshifen.com. 300 IN A 119.63.197.151
www.wshifen.com. 300 IN A 119.63.197.139
;; Query time: 148 msec
;; SERVER: 2001:4898::1050:5050#53(2001:4898::1050:5050)
;; WHEN: Wed Dec 29 01:25:39 CST 2021
;; MSG SIZE rcvd: 127
##
# User Database
#
# Note that this file is consulted directly only when the system is running
# in single-user mode. At other times this information is provided by
# Open Directory.
#
# See the opendirectoryd(8) man page for additional information about
# Open Directory.
##
nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false
root:*:0:0:System Administrator:/var/root:/bin/sh
daemon:*:1:1:System Services:/var/root:/usr/bin/false
...
_oahd:*:441:441:OAH Daemon:/var/empty:/usr/bin/false
是的,cat /etc/passwd 的結果也被輸出出來了!
這樣是不是很可怕?
想想看,這樣的話,命令換一換,攻擊者幾乎可以做任何的事情,比如執行點 rm -rf 都不在話下,
我當時就是使用了 Popen 方法拼接了一個 spider_name 的名稱,但是沒對 spider_name 做校驗,導致出現了問題,
另外,這種直接拼接命令列構造并執行的方法千萬別用,會造成很大的安全隱患,
有朋友就會說了,如果我把里面所有的輸入引數做個安全檢查不就好了嗎?比如檢測到有非法字符或命令就回傳不執行,
行倒是行,但問題在于你一定能想全所有的可能情況嗎?萬一漏掉一個那也非常危險,而且萬一有效字符也被過濾了咋辦?另外也非常費時費力,得不償失,
解決方案
那咋辦?那這種命令列執行的方式難道一定會造成安全性問題嗎?
其實辦法是有的,我們只需要修改下命令列的構造方式就可以了,上面的安全性問題可以通過這樣的修改來修復:
from subprocess import Popen, PIPE
def execute(host):
cmd = ['dig', host]
p = Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE)
stdout, stderr = p.stdout.read(), p.stderr.read()
return stdout if not stderr else stderr
這里我們將命令修改成了一個串列表示的內容,每個空格間隔都是串列的一個元素,使用 Popen 執行的時候執行 shell 引數為 False 即可,
這樣的話,每個串列的元素都會被看作成一個字串,被當成一個整體來看待,而不是單純的拼接,
比如如果這時候 host 我們再傳入 www.baidu.com; cat /etc/passwd,這時候這個引數就會被看作一個整體,相當于又加了一層引號,相當于被轉義了一樣,這樣 dig 執行的物件就是 www.baidu.com; cat /etc/passwd 這個整體了,所以就不會出現之前的安全性問題了,
同樣的呼叫:
result = execute('www.baidu.com; cat /etc/passwd')
print(result)
運行結果如下:
; <<>> DiG 9.10.6 <<>> www.baidu.com;cat /etc/passwd
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 2626
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.baidu.com\;cat\032/etc/passwd. IN A
;; AUTHORITY SECTION:
. 900 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2021122801 1800 900 604800 86400
;; Query time: 92 msec
;; SERVER: 2001:4898::1050:5050#53(2001:4898::1050:5050)
;; WHEN: Wed Dec 29 01:38:41 CST 2021
;; MSG SIZE rcvd: 133
這樣就不會輸出后面的 /etc/passwd 的內容了,
所以,大家看到這里,想必也能理解為什么有時候我們會看到一些命令用串列來表示,而不是單純的命令來表示了,
比如 Docker 構建鏡像的最后一條命令,類似如下:
ENTRYPOINT ["/bin/chamber", "exec", "production", "--"]
CMD ["/bin/service", "-d"]
這也是為了安全考慮的,
所以,通過這個事件,真的安全性問題要重視起來,而且尤其我作為開源專案的作者,我也有必要好好地處理好安全性問題,不然大家用了我專案,但是出現了問題,我還是難辭其咎的,
以后我會多加注意,謝謝大家的支持,

End
崔慶才的新書《Python3網路爬蟲開發實戰(第二版)》已經正式上市了!書中詳細介紹了零基礎用 Python 開發爬蟲的各方面知識,同時相比第一版新增了 JavaScript 逆向、Android 逆向、異步爬蟲、深度學習、Kubernetes 相關內容,?同時本書已經獲得 Python 之父 Guido 的推薦,目前本書正在七折促銷中!
內容介紹:《Python3網路爬蟲開發實戰(第二版)》內容介紹

掃碼購買


點個在看你最好看

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/398602.html
標籤:其他
