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