通過發(fā)現(xiàn)“異常詞匯(Bad Word)”頻率在代碼中查找漏洞
如何在代碼中查找漏洞是一個非常復雜的問題,如果高屋建瓴地看,這個查找漏洞的過程應該是以下這樣的:
- 找到危險的功能;
- 找到從你控制的輸入到危險功能的路徑;
- 向程序輸入錯誤的行為;
先讓我們看看如何找到危險的功能,根據(jù)經(jīng)驗,80%的錯誤都在大約20%的代碼中。由于通常需要徹底理解代碼才能發(fā)現(xiàn)新的漏洞,因此決定重點關(guān)注哪20%至關(guān)重要。根據(jù)我的經(jīng)驗,哪里的“異常詞匯(Bad Word)”頻率最多,哪里能找出的漏洞機會就越大。
本文我將通過一個簡短的故事向你展示尋找漏洞的思路。Terraform 是一個安全和高效的用來構(gòu)建、更改和合并基礎(chǔ)架構(gòu)的工具。采用 Go 語言開發(fā)。Terraform 可管理已有的流行的服務,并提供自定義解決方案。最近在一次滲透測試工作中,當我在查看一份大段的Terraform 材料時,無意中看到了這行代碼:
- driver.raw_exec.enable = 1
原本我認為在這段代碼中是沒有找到漏洞的可能,但當以上那行奇怪的代碼出現(xiàn)時。我不得不停下來弄清楚它們是什么意思負責什么操作。原來,它正在配置Hashicorp的一個名為Nomad的操作調(diào)度程序。HashiCorp是由Mitchell Hashimoto和Armon Dadgar聯(lián)合創(chuàng)辦,總部位于美國舊金山,致力于為企業(yè)提供服務,通過數(shù)據(jù)中心管理技術(shù)研發(fā),讓開發(fā)者通過工具構(gòu)建完整的開發(fā)環(huán)境,提高開發(fā)效率。Nomad 是一個集群管理器和調(diào)度器,專為微服務和批量處理工作流設(shè)計。
Nomad 是分布式,高可用,可擴展到跨數(shù)據(jù)中心和區(qū)域的數(shù)千個節(jié)點。Nomad 提供一個常規(guī)工作流跨基礎(chǔ)設(shè)施部署應用。開發(fā)者使用一個聲明式操作規(guī)范來定義應用該如何部署,資源有什么要求(CPU,內(nèi)存,硬盤)。Nomad 接收這些操作,查找可用的資源來運行應用。調(diào)度算法確保所有的約束都滿足,盡量在一個主機部署盡可能多的應用,優(yōu)化資源利用。
和其他滲透測試人員一樣,我立即對Nomad進行了了解。果然,我查出了這行代碼的蹊蹺之處,官方文檔是這樣解釋其作用的:“這使你可以無隔離地運行操作,出于安全原因,默認情況下將其禁用。”
當我迅速完成我可以構(gòu)建的最簡單的Nomad模塊時,我高興壞了,幾分鐘后,我就可以以root身份訪問該模塊,并且獲取大量憑證。在此之前,我從來沒有聽說過Nomad,更不用說這個配置選項了,那么是什么讓我想要深入挖掘呢?它是徘徊在同一段代碼上的兩個安全異常詞匯的組合,即“raw”和“exec”。這些異常詞匯可以幫助你找到代碼中最關(guān)鍵的安全部分,這樣你就可以將你的注意力放在最重要的地方了。
常見的異常詞匯
1. raw
Raw意味著你正在訪問較低級別的抽象,當你的安全控制在更高的級別執(zhí)行時,允許此“raw”界面的用戶繞過它們,這就會成為一個漏洞。
示例:
- CAP_NET_RAW是一種Linux功能,允許你創(chuàng)建原始套接字,并使用它們來繞過典型的進程隔離限制。
- Nomad中的raw_exec驅(qū)動程序使你可以創(chuàng)建具有Nomad代理權(quán)限的,在容器外部運行的操作。
- 許多ORM都具備rawQuery或rawSQL方法,可讓你直接執(zhí)行查詢。 ORM生成的查詢通常是不可注入的,但是在使用“raw” 界面時,如何組織SQLi由用戶決定。
2. eval | exec | run
將用戶輸入與用動態(tài)語言(Javascript、SQL、bash等)編寫的代碼相結(jié)合通常會導致注入攻擊,攻擊者可以提交代碼作為輸入內(nèi)容,從而導致解釋器的行為異常。運行此代碼通常被稱為“執(zhí)行”,“評估”或“運行”。
示例:
- 無隔離狀態(tài)下,raw_exec運行一個Nomad操作;
- execute(sql)在許多python數(shù)據(jù)庫驅(qū)動程序中運行sql查詢;
- exec(code)是一個python方法,它運行傳遞給它的代碼;
- eval(code)是許多動態(tài)語言提供的函數(shù),比如運行傳遞給它的代碼的Javascript,Python也有一個eval函數(shù),但它只用于表達式;
這個函數(shù)將返回大量的假陽性結(jié)果,因為正如Steve Yegge預測的那樣,似乎每個動詞都通過run()、execute()或justDoIt()方法變成了名詞。
3. process | system | popen | exec | spawn
這些詞可以指示子進程的創(chuàng)建,如果子進程生成了shell,則可以注入shell命令。即使它直接調(diào)用execve系統(tǒng)調(diào)用,你仍然可以向程序添加或修改參數(shù)。
示例:
- python中的子進程模塊;
- 節(jié)點中的child_process模塊;
- golang中的os / exec程序包;
- python中的os.system方法;
- ruby中的popen模塊;
4. privilege | permission | capability | role | rbac | policy | authorization | claims
這些詞將幫助你找到負責向用戶、容器、進程、文件、EC2實例等授予特權(quán)的代碼,使用任何高度特權(quán)的對象來操作你的命令,甚至完全繞過authz。
示例:
- docker --privileged標志為主機提供了容器功能的root特權(quán);
- linux內(nèi)核將root用戶權(quán)限劃分為“功能”,你可以將其分配給程序,從而允許它執(zhí)行創(chuàng)建原始套接字,調(diào)試你不擁有的進程或繞過文件ACL之類的操作。
- Kubernetes使用一個稱為RBAC(基于角色的訪問控制)的api擴展來授權(quán)對k8s資源的訪問,k8s全稱kubernetes,這個名字大家應該都不陌生,k8s是為容器服務而生的一個可移植容器的編排管理工具,越來越多的公司正在擁抱k8s,并且當前k8s已經(jīng)主導了云業(yè)務流程,推動了微服務架構(gòu)等熱門技術(shù)的普及和落地,正在如火如荼的發(fā)展。
- 許多云提供商使用術(shù)語“role binding”向主體授予一組權(quán)限;
- JWT具有向用戶告知用戶特權(quán)的“claims”,并且用戶使用jwt.ParseWithClaims之類的功能對其進行驗證。
5. reflect | klass | constantize | forName
許多編程語言都允許你通過函數(shù)名稱,類,方法,變量等來查找它們(甚至實例化/調(diào)用它們),這通常稱為“反射”。如果用戶可以控制要調(diào)用的方法的名稱或要返回的變量的名稱,則可能會導致程序行為異常。
示例:
- Javascript中的Reflect對象;
- ruby String#constantize方法;
- java Class.forName方法;
- klass是通過反射查找的類的常見變量名(因為“class”往往是一個保留詞匯);
6. pickle | yaml | serialize | marshal | objectinput
這些詞表示程序可能正在使用支持復雜對象的格式對數(shù)據(jù)進行反序列化,這可能使攻擊者可以讀取文件、發(fā)送HTTP請求甚至執(zhí)行任意代碼,具體取決于序列化格式以及運行時可用的對象(JVM類路徑上的類,python中sys.path上的包等)。
示例:
- python的pickle格式;
- node-serialize包;
- 大多數(shù)YAML解析器;
- Java的ObjectInputStream ;
- php的反序列化功能;
7. parse | open | request
這些詞匯之所以有趣,原因與eval()及其類似的函數(shù)一樣,攻擊者可以輸入解析器所識別的元字符,以改變解析器的行為。主要區(qū)別在于,你不是用動態(tài)語言運行代碼,而是利用解析器訪問文件或URL等資源。
示例:
- 控制URL解析器的輸入可能會導致SSRF、繞過代理限制、非斜杠格式等等;
- 控制文件路徑解析器的輸入可能導致LFI,RFI和本地文件讀/寫;
8. unsafe | insecure | dangerous
有時候,API開發(fā)人員喜歡通過在名稱中包含“insecure” 或“unsafe”來提醒人們注意危險的API。
示例:
- Rust中unsafe {};
- Go語言編寫的TLS套件中的InsecureSkipVerify;
- React中dangerouslySetInnerHtml(),React主要用于構(gòu)建UI。你可以在React里傳遞多種類型的參數(shù),如聲明代碼,幫助你渲染出UI、也可以是靜態(tài)的HTML DOM元素、也可以傳遞動態(tài)變量、甚至是可交互的應用組件;
- Go中不安全的程序包;
9. todo | fixme | xxx
隨著代碼的發(fā)展,開發(fā)人員會添加注釋,以提醒自己實現(xiàn)功能、修復漏洞或清理一些自己不喜歡的代碼。有時,這些注釋可能會導致你發(fā)現(xiàn)重要的漏洞、丟失的功能等,你可以利用它們。
示例:
- 有一次,我在Apache服務器的Web根目錄中找到一個todos.txt文件,它包含了一長串未修補的安全漏洞。
- 還有一次,我發(fā)現(xiàn)一個FIXME注釋提到了一個功能問題。事實證明,這是一個非常難以發(fā)現(xiàn)的漏洞,但利用ReDoS漏洞卻是微不足道的。
10. merge | clone
這些詞通常表示一個object, dict, map等正在與另一個對象合并或克隆到新對象中,這可能會導致一些有趣的安全問題,例如Javascript原型污染漏洞、大規(guī)模分配漏洞等。
示例:
- LoDash中的_.merge;
- LoDash中的_.clone;
11. alloc | free
這是一個很好的線索,說明正在進行手動內(nèi)存管理。眾所周知,這很難解決,并且可能導致諸如緩沖區(qū)溢出、釋放后使用、雙重釋放等漏洞。
示例:
- malloc ();
- free ();
- Objective C中的[object alloc] 消息;
12. AES | RSA | DSA | DES | CBC | ECB | HMAC | GCM
這些是加密原語,可以表明作者在使用他們自己的加密系統(tǒng),而不是使用更高級別的抽象。有許多微妙的方法可以不安全地使用它們,所以請仔細閱讀并咨詢密碼學家。
示例:
- aes.NewCipher(密鑰);
- 新的RSAPrivateKey(keyBytes);
- HMAC.new(secret,digestmod = SHA256);
13. JWT | JKS | JWK | JKU …
JSON Web令牌是安全傳輸數(shù)據(jù)的標準,在現(xiàn)代應用程序堆棧中非常常用,有很多不安全地使用它們的方法,因此值得關(guān)注處理JWT的代碼。
常見的JWT問題:
- 無算法;
- 操縱alg標頭;
- 不驗證aud或 iss claims;
- 未驗證有效期(exp和nbf claims)
- 簽名但不加密敏感數(shù)據(jù);
示例:
- JWTVerifier;
- jwt.ParseWithClaims;
- jwt.verify;
14. password | private | token | secret | key | Authorization
這些詞匯很好地說明了你可能有一些硬編碼到存儲庫中的密鑰,如API密鑰、數(shù)據(jù)庫密碼、加密密鑰等。
示例:
- BEGIN RSA PRIVATE KEY;
- AWS’s “secret access key”;
- Django的SECRET_KEY設(shè)置;
15. validate | verify
這些詞匯通常表示正在執(zhí)行業(yè)務/安全規(guī)則,不過請仔細檢查這些內(nèi)容,以獲取通過驗證的輸入內(nèi)容,因為這也可能導致漏洞。他們試圖禁止的輸入類型也可以為你提供有關(guān)潛在漏洞的線索。
示例:
16. XML | xerces | SAX | etree | xpath | DocumentBuilder
解析攻擊者控制的XML可能會導致諸如本地文件讀取以及拒絕服務攻擊等一系列安全問題。
示例:
- DocumentBuilderFactory.newInstance();
- SAXParserFactory.newInstance();
- xml.etree.elementtree;