如何通過代碼審計挖掘REDos漏洞


寫這篇文章的目的一是由於目前網上關於java代碼審計的資料實在是太少了,本人作為一個java代碼審計的新手,深知學習java代碼審計的難受之處,所以將自己學習過程中挖掘的一些漏洞寫成博客發出來希望可以給后人一些幫助,同時也可以記錄下自己的成長過程。目的二是由於這次挖掘的漏洞REDOS,個人覺得這個漏洞還是很有意思的,很早就知道這個漏洞了但是從來沒有挖到過,終於在一個月前挖到了,很開心,記錄一下。

先介紹下REDOS這個漏洞吧,可能很多人並不是很熟悉這個漏洞,一是由於這種漏洞造成的影響是拒絕服務,影響似乎不是很嚴重;二是這種漏洞一般需要通過白盒測試才能發現,通常的黑盒測試很難發現這種漏洞。

OWASP的對它的描述是:正則表達式拒絕服務(Regular expression Denial of Service-ReDoS)是一種拒絕服務攻擊(Denial of Service ),它利用了大多數正則表達式實現可能會導致極端情況的原因,這些極端情況導致它們工作得非常緩慢(與輸入大小呈指數關系),然后攻擊者可以使用正則表達式導致程序進入這些極端情況,然后掛起很長時間。

說白了就是,應用程序中經常會有使用正則表達式去匹配字符串的情況,(1)如果開發人員對正則表達式的原理並不是很熟悉寫出了類似“^(a+)+$”這樣的正則,(2)由於業務需求,正則是動態構造的,且受用戶控制。這兩種情況,程序都可能受到REDOS攻擊。大家可能第一眼看到“^(a+)+$”不會覺得這個正則有什么問題,這里也就是為什么說要懂原理才行。因為這里其實涉及到了編譯原理的知識,我盡量講清楚:

正則表達式引擎分成兩類,一類稱為DFA(確定性有限狀態自動機),另一類稱為NFA(非確定性有限狀態自動機)。兩類引擎要順利工作,都必須有一個正則式和一個文本串,一個捏在手里,一個吃下去。DFA捏着文本串去比較正則式,看到一個子正則式,就把可能的匹配串全標注出來,然后再看正則式的下一個部分,根據新的匹配結果更新標注。而NFA是捏着正則式去比文本,吃掉一個字符,就把它跟正則式比較,然后接着往下干。一旦不匹配,就把剛吃的這個字符吐出來,一個一個吐,直到回到上一次匹配的地方。也就是說對於同一個字符串中的每一個字符,DFA都只會去匹配一次,比較快,但特性較少,而NFA則會去匹配多次,速度相比會慢很多但是特性多啊,所以用NFA作為正則表達式引擎的會多些。現在我們再來看^(a+)+$,大家應該能看出些問題了,當我們用它去匹配“aaaax”這個字符串時,他需要嘗試2^4=16次才會失敗,當我們這個字符串再長一點時,需要嘗試的次數會成指數增長,aaaaaaaaaaX就要經歷2^10=1024次嘗試。

如下圖:

 

 

可以看到javaphppython都是用的NFAJS中好像也是),也就是說這些語言中用了正則的api都是可能存在問題的哦。比如java中比較常見的split,replaceAll等,這些方法中既可以接受字符串也可以接受正則表達式的。

說了這么多,准備進入正題吧,為什么一個月前就發現漏洞了,現在才寫博客呢,本來是想等作者修復漏洞后再寫博客的,但是因為github上的作者到現在都沒有回復我,似乎並不准備修復,而我也懶得等了,直接將漏洞披露出來吧,畢竟漏洞影響不大,並且漏洞和程序上下文的聯系也不大,復現的時候只需要將缺陷代碼單獨拿出來在本地復現即可。

 

 

這整個java文件是將文件轉化為pdf格式的一個工具類,在轉化文件格式的同時還需要 將文件名的后綴也修改下。如上兩個方法就是用來修改文件名后綴的。getOutputFilePath(String inputFilePath)用於將文件名的后綴修改為.pdf,而文件名后綴是通過getPostfix(String inputFilePath)這一方法截斷文件名中最后一個.后面的內容來獲取的。簡單介紹下String.replaceAll(a,b)的意思就是將String中的字符串a全部替換成字符串b。比如說”aabbcc”.replaceAll(“a”,”b”),輸出結果為“bbbbcc,上面也說了a可以是正則表達式 當”aabbcc”.replaceAll(“(a)+”,”b”),輸出結果為“fbbcc”。再回過頭看代碼,如果我們已知參數inputFilePath是可控的,怎樣設置這個值才能觸發REDOS攻擊呢?

首先參數inputFilePath的后綴必須控制成一個真正表達式的樣,我們可以先設置為.(a+)+,.(a+)+作為正則表達式去匹配inputFilePath時,inputFilePath的前半部分必須符合這個正則才不至於匹配沒開始就結束了,所以inputFilePath的前半部分也應該類似.aaaa這樣,再和后綴拼湊起來就是.aaa.(a+)+

Ok,本地搭建環境測試一下:

 

 

發現程序運行的很快,關鍵是成功替換了字符串。回顧之前的說的,之所以能造成REDOS攻擊是因為一直匹配失敗,引擎才會反復嘗試導致攻擊的。怎樣讓他匹配失敗呢又一直嘗試呢?只需要將后綴.(a+)+改為.(a+)+$。這個正則的意思就是字符串必須以a結尾才行。修改之后重新運行:

 

 

發現雖然運行很快但是不會替換字符串了,也就是說匹配失敗。

那我們再將inputFilePath改下,讓程序匹配的時間長一些,如下圖:

 

 

發現程序一直在運行,說明攻擊成功了。

總結一下,挖掘REDOS漏洞,一是需要對程序中用到了正則的api有些了解(replaceAll只是最為常見的,其實還有很多),后面有時間的話我也會對這些api做些整理;二是要對正則有一定的了解才方便構造poc


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM