這篇文章將為大家詳細(xì)講解有關(guān)如何分析Java Web安全中的代碼審計(jì),文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。
調(diào)兵山網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)建站!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營維護(hù)。創(chuàng)新互聯(lián)建站從2013年創(chuàng)立到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)建站。
通俗的說Java代碼審計(jì)就是通過審計(jì)Java代碼來發(fā)現(xiàn)Java應(yīng)用程序自身中存在的安全問題,由于Java本身是編譯型語言,所以即便只有class文件的情況下我們依然可以對Java代碼進(jìn)行審計(jì)。對于未編譯的Java源代碼文件我們可以直接閱讀其源碼,而對于已編譯的class或者jar文件我們就需要進(jìn)行反編譯了。
Java代碼審計(jì)其本身并無多大難度,只要熟練掌握審計(jì)流程和常見的漏洞審計(jì)技巧就可比較輕松的完成代碼審計(jì)工作了。但是Java代碼審計(jì)的方式絕不僅僅是使用某款審計(jì)工具掃描一下整個(gè)Java項(xiàng)目代碼就可以完事了,一些業(yè)務(wù)邏輯和程序架構(gòu)復(fù)雜的系統(tǒng)代碼審計(jì)就非常需要審計(jì)者掌握一定的Java基礎(chǔ)并具有具有一定的審計(jì)經(jīng)驗(yàn)、技巧甚至是對Java架構(gòu)有較深入的理解和實(shí)踐才能更加深入的發(fā)現(xiàn)安全問題。
本文將分為多章節(jié)來講述Java代碼審計(jì)需要掌握的前置知識以及Java代碼審計(jì)的流程、技巧。
在開始Java代碼審計(jì)前請自行安裝好Java開發(fā)環(huán)境,建議使用MacOS、Ubuntu操作系統(tǒng)。
所謂“工欲善其事,必先利其器”,合理的使用一些輔助工具可以極大的提供我們的代碼審計(jì)的效率和質(zhì)量!
強(qiáng)烈推薦下列輔助工具:
1.Jetbrains IDEA(IDE)
2.Sublime text(文本編輯器)
3.JD-GUI(反編譯)
4.Fernflower(反編譯)
5.Bytecode-Viewer
6.Eclipse(IDE)
7.NetBeans(IDE)
在滲透測試的時(shí)候需要審計(jì)的代碼通常是class文件或者jar包,那么我們應(yīng)該如何審計(jì)呢?讓我們先來學(xué)習(xí)一下什么是Java源碼和字節(jié)碼。
簡單的說Java源碼就是未經(jīng)編譯的.java文件,我們可以很輕松的閱讀其中的代碼邏輯,而字節(jié)碼.class文件則是.java文件經(jīng)過編譯之后產(chǎn)生的字節(jié)碼文件,因?yàn)?class文件是編譯后的二進(jìn)制文件所以我們是無法直接閱讀的,只能通過反編譯工具將二進(jìn)制文件轉(zhuǎn)換成java代碼或者ASM代碼。
示例代碼Test.java:
/** * @author yz */ public class Test { public static void hello() { System.out.println("Hello~"); } public void world() { System.out.println("World!"); } public static void main(String[] args) { hello(); } }
Test.java編譯執(zhí)行流程:
Test.java 源碼、字節(jié)碼
由于class文件的可讀性較差,通常我們需要使用Java反編譯工具來反編譯代碼。我們通常會使用到JD-GUI、IDEA Fernflower插件、Bytecode-Viewer、Fernflower、JAD、JBE、JEB 等工具來反編譯class。
其中JD-GUI可能是目前反編譯中使用的最多的工具了,但是個(gè)人覺得JD-GUI的反編譯能力遠(yuǎn)不如經(jīng)過IDEA(IDEA應(yīng)該是使用的改版后的Fernflower),因?yàn)镮DEA默認(rèn)支持對jar和class的反編譯,所以我個(gè)人強(qiáng)烈推薦使用IDEA來反編譯class代碼。
當(dāng)然,反編譯工具很多時(shí)候也不是萬能的,JD-GUI經(jīng)常遇到無法反編譯或反編譯過程中程序直接崩潰的情況,遇到這類情況我們通??梢允褂肐DEA反編譯試試,如果IDEA也無法反編譯可以使用JBE來加載class文件讀取程序的字節(jié)碼,如果JBE仍無法讀取類信息還可以使用JDK自帶的javap命令來讀取class類字節(jié)碼,如果上訴所有的方法都無法反編譯,那么恐怕是這個(gè)類本身就存在無法編譯問題要么可能就是類文件被加密處理過??赡苣銜fjava編譯的class不是說不可以加密嗎?沒錯(cuò),這里所說的加密其實(shí)是為了保護(hù)編譯后的class代碼不可反編譯,通過實(shí)現(xiàn)自定義ClassLoader來loadClass加密后的類方式而已,這種加密方式曾在實(shí)戰(zhàn)中也有遇到。
通常我們在某些特殊的場景下拿到的只是jar文件,那么我們應(yīng)該如何反編譯整個(gè)jar包的class文件呢?
2.1. Fernflower
Fernflower可以很輕松的實(shí)現(xiàn)jar的完整反編譯,執(zhí)行如下命令即可: java -jar fernflower.jar jarToDecompile.jar decomp/ 其中jarToDecompile.jar是需要反編譯的jar文件,decomp是反編譯后的class文件所存放的目錄。需要注意的是Fernflower如遇無法反編譯的情況可能會生成空的java文件!
2.2. JD-GUI
JD-GUI是一個(gè)帶GUI的反編譯工具,在JD-GUI的菜單中點(diǎn)擊File-->Save All Sources即可反編譯jar。
2.3. IDEA
IDEA默認(rèn)就支持jar包反編譯,同時(shí)還支持class文件名(??F)、類方法名稱(??O)搜索。
2.4. Bytecode-Viewer
FernFlower提供了GUI版本Bytecode-Viewer,Bytecode-Viewer提供了直接反編譯的class、jar、zip、apk、dex功能,直接拖拽jar就可以直接對整個(gè)jar進(jìn)行反編譯了。
2.5. Find命令
find命令并不能支持Java反編譯,但是find命令可以非常方便的搜索經(jīng)過編譯后的二進(jìn)制文件中的內(nèi)容,所以有的時(shí)候使用find命令通常是最簡單實(shí)用的,直接解壓jar包然后使用find命令搜索: find ./ -type f -name “*.class” |xargs grep XXXX 即可搞定。
2.6 使用Find命令和Fernflower實(shí)現(xiàn)批量反編譯jar
當(dāng)我們只有項(xiàng)目war包且源碼經(jīng)過打包后發(fā)布到WEB-INF/lib的情況下,我們不得不去找出待審計(jì)源碼的具體jar文件并反編譯。遇到這種情況我們可以巧妙的使用find命令來反編譯所有目標(biāo)的jar包。
這里以jcms的一個(gè)非常老版本為例,jcms最終給客戶部署的war包中源碼并不是在WEB-INF/classes目錄下,而是將整個(gè)jcms系統(tǒng)按模塊打包成了多個(gè)jar包放在了WEB-INF/lib目錄下。我們可以通過搜索com.hanweb包名稱來找出所有jar中包含了jcms的文件并通過Fernflower來反編譯。
java -jar /Users/yz/Desktop/javaweb-decomplier/javaweb-decomplier.jar -dgs=1 $(find /Users/yz/Desktop/jcms/WEB-INF/lib/ -type f -name "*.jar" |xargs grep "com.hanweb" |awk '{print $3}') /Users/yz/jcms-decomplier
依賴的jar: javaweb-decomplier、Intellij java-decompiler。
執(zhí)行上面的命令后會在jcms-decomplier目錄下看到所有的jar已經(jīng)被Fernflower反編譯了。
IntelliJ IDEA是Jetbrains出品的一款非常強(qiáng)大的Java IDE,IDEA提供了強(qiáng)大的代碼搜索、近乎完美的反編譯、動(dòng)態(tài)調(diào)試等功能可以最大程度的輔助我們代碼審計(jì)。
不可以否認(rèn),與IDEA相比雖然Eclipse和Netbeans也有與之類似的功能,但是在真正的實(shí)戰(zhàn)體驗(yàn)中個(gè)人更傾向于使用IDEA,雖然曾經(jīng)的我也是一個(gè)重度Eclipse開發(fā)者。
IDEA的搜索快捷鍵是:??F,使用IDEA提供的搜索功能可以非常快速的定位漏洞點(diǎn)信息。
IDEA可以通過自定義搜索范圍來精確查找我們需要審計(jì)的代碼。默認(rèn)搜索的是所有的位置,不過我們可以點(diǎn)擊紅色箭頭指向的...按鈕來細(xì)化我們的搜索范圍。
自定義搜索范圍示例:
自定義搜索范圍后就可以在搜索時(shí)使用自定義的配置進(jìn)行范圍搜索了,有助于我們在挖漏洞的時(shí)候縮小代碼定位范圍。
搜索快捷鍵: ?O,標(biāo)記搜索支持類名、方法名搜索(包括class或jar文件中的方法也支持搜索)。
當(dāng)我們審計(jì)代碼的時(shí)候發(fā)現(xiàn)某個(gè)方法或類有漏洞時(shí)我們需要定位到漏洞的請求地址(觸發(fā)點(diǎn)),復(fù)雜業(yè)務(wù)系統(tǒng)往往會讓我們很難定位到漏洞的觸發(fā)點(diǎn)。借助IDEA的方法調(diào)用鏈搜索功能就可以很輕松的找出方法的調(diào)用鏈和觸發(fā)點(diǎn)。
選擇類或者方法名-->右鍵-->Find Useages或者使用快捷鍵?F7
為了更好的管理項(xiàng)目我們通常會采用分層架構(gòu)的方式來開發(fā)Java Web項(xiàng)目,分層設(shè)計(jì)的好處在于可以非常方便的分清楚包之間的業(yè)務(wù)邏輯關(guān)系。
常見的JavaWeb項(xiàng)目分層:
視圖層(View 視圖)
控制層(Controller、Action 控制層)
服務(wù)層(Service)
業(yè)務(wù)邏輯層BO(business object)
實(shí)體層(entity 實(shí)體對象、VO(value object) 值對象 、模型層(bean)。
持久層(dao- Data Access Object 數(shù)據(jù)訪問層、PO(persistant object) 持久對象)
基于Java分層架構(gòu)的示例項(xiàng)目:
如今的較為大型的Java Web項(xiàng)目通常都采用了模塊化方式開發(fā),借助于Maven、Gradle依賴管理工具,Java可以非常輕松的完成模塊化開發(fā)。除此之外使用OSGi(Open Service Gateway Initiative 可實(shí)現(xiàn)模塊熱部署)技術(shù)開發(fā)來Java動(dòng)態(tài)模塊化系統(tǒng)也是較為常見的。
采用模塊化開發(fā)也會給我們做代碼審計(jì)帶來一定的難度,因?yàn)樾枰诟嗟囊蕾噹熘腥ふ倚枰覀儗徲?jì)的代碼。
使用Maven開發(fā)的JavaWeb項(xiàng)目示例:
Servlet是在Java Web容器上運(yùn)行的小程序,通常我們用Servlet來處理一些較為復(fù)雜的服務(wù)器端的業(yè)務(wù)邏輯。值得注意的是在Servlet3.0之后(Tomcat7+)可以使用注解方式配置Servlet了。
基于注解的Servlet
Servlet3.0之前的版本都需要在web.xml中配置,Servlet是兩對標(biāo)簽,由<servlet>和<servlet-mapping>組成,Spring MVC框架就是基于Servlet技術(shù)實(shí)現(xiàn)的。
基于配置實(shí)現(xiàn)的Servlet
HttpServlet類
實(shí)現(xiàn)一個(gè)Servlet很簡單,只需要繼承javax.servlet.http.HttpServlet類并重寫doXXX方法或者service方法就可以了,其中需要注意的是重寫HttpServlet類的service方法可以獲取到上述七種Http請求方法的請求。
JSP、JSPX文件是可以直接被Java容器直接解析的動(dòng)態(tài)腳本,jsp和其他腳本語言無異,不但可以用于頁面數(shù)據(jù)展示,也可以用來處理后端業(yè)務(wù)邏輯。
從本質(zhì)上說JSP就是一個(gè)Servlet,因?yàn)閖sp文件最終會被編譯成class文件,而這個(gè)Class文件實(shí)際上就是一個(gè)特殊的Servlet。
JSP文件會被編譯成一個(gè)java類文件,如index.jsp在Tomcat中Jasper編譯后會生成index_jsp.java和index_jsp.class兩個(gè)文件。而index_jsp.java 繼承于HttpJspBase類,HttpJspBase是一個(gè)實(shí)現(xiàn)了HttpJspPage接口并繼承了HttpServlet的標(biāo)準(zhǔn)的Servlet,__jspService方法其實(shí)是HttpJspPage接口方法,類似于Servlet中的service方法,這里的__jspService方法其實(shí)就是HttpJspBase的service方法調(diào)用。
Filter是JavaWeb中的過濾器,用于過濾URL請求。通過Filter我們可以實(shí)現(xiàn)URL請求資源權(quán)限驗(yàn)證、用戶登陸檢測等功能。Filter是一個(gè)接口,實(shí)現(xiàn)一個(gè)Filter只需要重寫init、doFilter、destroy方法即可,其中過濾邏輯都在doFilter方法中實(shí)現(xiàn)。
Filter和Servlet一樣是Java Web中最為核心的部分,使用Servlet和Filter可以實(shí)現(xiàn)后端接口開發(fā)和權(quán)限控制,當(dāng)然使用Filter機(jī)制也可以實(shí)現(xiàn)MVC框架,Struts2實(shí)現(xiàn)機(jī)制就是使用的Filter。
Filter的配置類似于Servlet,由<filter>和<filter-mapping>兩組標(biāo)簽組成,如果Servlet版本大于3.0同樣可以使用注解的方式配置Filter。
對于基于Filter和Servlet實(shí)現(xiàn)的簡單架構(gòu)項(xiàng)目,代碼審計(jì)的重心集中于找出所有的Filter分析其過濾規(guī)則,找出是否有做全局的安全過濾、敏感的URL地址是否有做權(quán)限校驗(yàn)并嘗試?yán)@過Filter過濾。第二點(diǎn)則是找出所有的Servlet,分析Servlet的業(yè)務(wù)是否存在安全問題,如果存在安全問題是否可以利用?是否有權(quán)限訪問?利用時(shí)是否被Filter過濾等問題,切勿看到Servlet、JSP中的漏洞點(diǎn)就妄下定論,不要忘了Servlet前面很有可能存在一個(gè)全局安全過濾的Filter。
Filter和Servlet都是Java Web提供的API,簡單的總結(jié)了下有如下共同點(diǎn)。
1.Filter和Servlet都需要在web.xml或注解(@WebFilter、@WebServlet)中配置,而且配置方式是非常的相似的;
2.Filter和Servlet都可以處理來自Http請求的請求,兩者都有request、response對象;
3.Filter和Servlet基礎(chǔ)概念不一樣,Servlet定義是容器端小程序,用于直接處理后端業(yè)務(wù)邏輯,而Filter的思想則是實(shí)現(xiàn)對Java Web請求資源的攔截過濾;
4.Filter和Servlet雖然概念上不太一樣,但都可以處理Http請求,都可以用來實(shí)現(xiàn)MVC控制器(Struts2和Spring框架分別基于Filter和Servlet技術(shù)實(shí)現(xiàn)的);
5.一般來說Filter通常配置在MVC、Servlet和JSP請求前面,常用于后端權(quán)限控制、統(tǒng)一的Http請求參數(shù)過濾(統(tǒng)一的XSS、SQL注入、Struts2命令執(zhí)行等攻擊檢測處理)處理,其核心主要體現(xiàn)在請求過濾上,而Servlet更多的是用來處理后端業(yè)務(wù)請求上
傳統(tǒng)的開發(fā)存在結(jié)構(gòu)混亂易用性差耦合度高可維護(hù)性差等多種問題,為了解決這些毛病分層思想和MVC框架就出現(xiàn)了。MVC即模型(Model)、視圖(View)、控制器(Controller), MVC模式的目的就是實(shí)現(xiàn)Web系統(tǒng)的職能分工。
截至2018年底,絕大多數(shù)的新項(xiàng)目都已然改為了基于Spring Boot的Spring MVC實(shí)現(xiàn),也就是說曾經(jīng)站在JavaWeb MVC最巔峰的Struts2框架已經(jīng)逐漸隕落。
7.1 Spring MVC 控制器
在Spring進(jìn)入了3.0時(shí)代,使用Java注解的方式也逐漸的流行了起來,曾經(jīng)寫一個(gè)Spring的控制器我們通常要在xml中聲明Spring bean并配置處理的URL,而在新時(shí)代的Spring項(xiàng)目中我們通常用Spring MVC注解就可以輕松完成Spring MVC的配置了。
一個(gè)基于Spring 注解配置的控制器:
package org.javaweb.codereview.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class IndexController { @RequestMapping("/index.php") public String index() { return "/index.html"; } }
Spring Controller注解:
@Controller
@RestController
@RepositoryRestController
Spring MVC請求配置注解:
@RequestMapping
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
Spring MVC除了上述6種Http請求處理注解以外還有Spring Data JPA Rest提供的特殊的@RepositoryRestResource注解,@RepositoryRestResource是基于Spring Data JPA REST庫實(shí)現(xiàn)的,Spring Data JPA REST提供的API可支持通過JPA查詢數(shù)據(jù)并處理Http請求服務(wù)。
基于XML配置的Spring MVC
對于一些老舊的項(xiàng)目可能還保留了一些基于xml配置的方式Spring MVC項(xiàng)目,這里只簡單的介紹下如何配置不做過多的描述?;谂渲梅绞降目刂破饕话闶窃贑ontroller類中實(shí)現(xiàn)了Spring的org.springframework.web.servlet.mvc.Controller接口的handleRequest方法(當(dāng)然還有其他途徑,如:AbstractCommandController和SimpleFormController但都已經(jīng)過時(shí)了)。
TestController.java示例代碼:
package org.javaweb.codereview.controller; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author yz */ public class TestController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView mv = new ModelAndView(); mv.setViewName("index"); return mv; } }
XML配置具體的bean
<bean name="/test.do" class="org.javaweb.codereview.controller.TestController"/>
7.2 Struts2控制器
Struts2主要的開發(fā)模式是基于xml配置,在struts.xml中配置Action地址和對應(yīng)的處理類。
不過Struts2(2.1.6版本開始)也可以使用struts2-convention-plugin插件來實(shí)現(xiàn)基于注解方式的配置。
需要注意的是Struts2的參數(shù)是可以通過get/set方法傳入的,如上圖TestActionAnnotation類的username變量是可以直接在Http請求中的URL傳入的。
7.3 快速找出Http請求請求URL
代碼審計(jì)中我們可以選擇優(yōu)先從Controller、Servlet和JSP中入手,也可以選擇從漏洞點(diǎn)反向推出Http請求的入口地址,這里將講解下如何快速找到這些請求入口,因?yàn)镾truts2和Spring MVC的原理比較接近,所以本節(jié)只以Spring MVC為例。
7.3.1 查找Spring MVC所有的控制器
如果有源碼的情況下可以使用find命令或者IDEA的全局搜索功能即可快速搜索到所有的控制器,如果只有class文件的情況下可以使用find命令:
find ~/cms/ -type f -name "*.class" |xargs grep -E "Controller|@RestController|RepositoryRestController"
7.3.2 查找所有的請求處理URL
查找請求處理URL的方式同理,使用如下find命令查找所有class中的請求處理注解:
find ~/cms/ -type f -name "*.class" |xargs grep -E "RequestMapping|GetMapping|PostMapping|PutMapping|DeleteMapping|PatchMapping|RepositoryRestResource"
7.4 Spring MVC和Struts2控制器小結(jié)
這一小節(jié)我們只是簡單的介紹下Spring MVC和Struts2的控制器,在后面的框架服務(wù)章節(jié)將會詳細(xì)介紹。至于如何去快速定位Struts2的action請自行參考Spring MVC的Controller查找方式這里不再講解。
Java語言動(dòng)態(tài)性一直以來都比較差,并不像PHP那樣靈活。在Java中的動(dòng)態(tài)性往往需要使用一些曲折的方式來實(shí)現(xiàn).這里簡單列舉了Java十余種動(dòng)態(tài)性相關(guān)技術(shù)并總結(jié)部分技術(shù)實(shí)現(xiàn)安全問題。
1.Java反射機(jī)制
2.MethodHandle
3.JDK動(dòng)態(tài)代理
4.使用JVM上的動(dòng)態(tài)語言(如:Groovy、JRuby、Jython)
5.表達(dá)式庫(如:OGNL、MVEL、SpEL、EL)
6.JSP、JSPX、Quercus(Resin容器提供了PHP5支持)
7.字節(jié)碼庫(如:Asm、Javassist、Cglib、BCEL)
8.ScriptEngineManager(腳本引擎)。
9.動(dòng)態(tài)編譯(如:JDT、JavaCompiler)
10.ClassLoader、URLClassLoader
11.模版引擎(如:Freemarker、Velocity)
12.序列化、反序列化(包含Java 對象序列化、XML、JSON等)
13.JNI、JNA(Java調(diào)用C/C++)
14.OSGi(Open Service Gateway Initiative)
15.RMI(Java遠(yuǎn)程方法調(diào)用,基于對象序列化機(jī)制實(shí)現(xiàn))
16.WebService
17.JDWP(Java Platform Debugger Architecture Java調(diào)試協(xié)議)
18.JMX(Java Management Extensions)
Java反射機(jī)制可以無視類方法、變量訪問權(quán)限修飾符,可以調(diào)用任何類的任意方法、訪問并修改成員變量值。也就是說只要發(fā)現(xiàn)一處Java反射調(diào)用漏洞幾乎就可以為所欲為了。當(dāng)然前提可能需要你能控制反射的類名、方法名和參數(shù)。
一行代碼即可實(shí)現(xiàn)反射調(diào)用Runtime執(zhí)行本地命令:
Runtime.class.getMethod("exec", String.class).invoke(Runtime.class.getMethod("getRuntime").invoke(null), "whoami")
獲取一個(gè)類的對象(如Runtime類)我們一般會采用如下幾種方式:
1.Class.forName("java.lang.Runtime")、"".getClass().forName("java.lang.Runtime")
2.Runtime.class
3.ClassLoader.getSystemClassLoader().loadClass("java.lang.Runtime")
Java反射獲取類方法有兩種方式:
1.getMethod(xxx),getMethods()
2.getDeclaredMethod(xxx)、getDeclaredMethods()。
區(qū)別在于getMethod會返回當(dāng)前類和父類的所有public方法,而getDeclaredMethod返回的是當(dāng)前的所有方法。
Java反射獲取類成員變量有兩種方式:
1.getField(xxx)、getFields()
2.getDeclaredField(xxx)、getDeclaredFields()
getField和getDeclaredField區(qū)別同上,如果想要調(diào)用private修飾的Field或者M(jìn)ethod只需要設(shè)置下setAccessible為true就可以了,如:xxxMethod.setAccessible(true)。
Java的大部分框架都是采用了反射機(jī)制來實(shí)現(xiàn)的(如:Spring MVC、ORM框架等),所以我們不得不掌握J(rèn)ava反射機(jī)制來提升我們的代碼審計(jì)能力。
Java反射機(jī)制實(shí)現(xiàn)無關(guān)鍵字執(zhí)行命令
import java.io.InputStream; import java.lang.reflect.Method; import java.util.Scanner; /** * @author yz */ public class ReflectionTest { public static void exec() { try { System.out.println(Runtime.class.getMethod("exec", String.class).invoke(Runtime.class.getMethod("getRuntime").invoke(null), "curl -i localhost:8000")); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { try { String str = "whoami"; // java.lang.Runtime String runtime = new String(new byte[]{106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101}); // Runtime.class Class<?> c = Class.forName(runtime); // 獲取getRuntime方法,Runtime.getRuntime() Method m1 = c.getMethod(new String(new byte[]{103, 101, 116, 82, 117, 110, 116, 105, 109, 101})); // 獲取Runtime的exec方法,rt.exec(xxx) Method m2 = c.getMethod(new String(new byte[]{101, 120, 101, 99}), String.class); // Runtime.getRuntime().exec(str) Object obj2 = m2.invoke(m1.invoke(null), str); // 獲取命令執(zhí)行結(jié)果Process類的getInputStream()方法 Method m = obj2.getClass().getMethod(new String(new byte[]{103, 101, 116, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109})); m.setAccessible(true); // process.getInputStream() InputStream in = (InputStream) m.invoke(obj2, new Object[]{}); // 輸出InputStream內(nèi)容到 Scanner scanner = new Scanner(in).useDelimiter("\\A"); System.out.println(scanner.hasNext() ? scanner.next() : ""); } catch (Throwable t) { t.printStackTrace(); } } }
JDK7開始Java提供了MethodHandle可以非常方便的訪問和調(diào)用類方法,MethodHandle的能力和Java反射機(jī)制相似,但效率卻遠(yuǎn)高出Java反射機(jī)制,但MethodHandle也并不是那么完美的,缺點(diǎn)是MethodHandle必須要求JDK版本大于等于1.7,MethodHandle也無法像反射那樣調(diào)用私有方法和變量。
參考:通過代碼簡單介紹JDK 7的MethodHandle,并與.NET的委托對比。
基于MethodHandle實(shí)現(xiàn)的調(diào)用Runtime執(zhí)行系統(tǒng)命令
import java.io.InputStream; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.Scanner; /** * @author yz */ public class MethodHandlesTest { public static void main(String[] args) { try { String str = "ping p2j.cn -c 1"; Class runtimeClass = Runtime.class; MethodHandles.Lookup lookup = MethodHandles.lookup(); // Runtime rt = Runtime.getRuntime() MethodHandle methodHandle = lookup.findStatic( runtimeClass, "getRuntime", MethodType.methodType(runtimeClass) ); // 獲取Runtime的exec方法 MethodHandle execMethod = lookup.findVirtual( runtimeClass, "exec", MethodType.methodType(Process.class, new Class[]{ String.class }) ); // 獲取Process的getInputStream方法 MethodHandle inputStreamMethod = lookup.findVirtual( Process.class, "getInputStream", MethodType.methodType(InputStream.class) ); // 調(diào)用Runtime.getRuntime().exec(xxx).getInputStream() InputStream in = (InputStream) inputStreamMethod.invoke( execMethod.invoke(methodHandle.invoke(), str) ); // 輸出InputStream內(nèi)容到 Scanner scanner = new Scanner(in).useDelimiter("\\A"); System.out.println(scanner.hasNext() ? scanner.next() : ""); } catch (Throwable t) { t.printStackTrace(); } } }
通常我喜歡把代碼審計(jì)的方向分為業(yè)務(wù)層安全問題、代碼實(shí)現(xiàn)和服務(wù)架構(gòu)安全問題,。
業(yè)務(wù)層的安全問題集中在業(yè)務(wù)邏輯和越權(quán)問題上,我們在代碼審計(jì)的過程中盡可能的去理解系統(tǒng)的業(yè)務(wù)流程以便于發(fā)現(xiàn)隱藏在業(yè)務(wù)中的安全問題。
1.1 業(yè)務(wù)層中常見的安全問題Checklist
1.用戶登陸、用戶注冊、找回密碼等功能中密碼信息未采用加密算法。
2.用戶登陸、用戶注冊、找回密碼等功能中未采用驗(yàn)證碼或驗(yàn)證碼未做安全刷新(未刷新Session中驗(yàn)證碼的值)導(dǎo)致的撞庫、密碼爆破漏洞。
3.找回密碼邏輯問題(如:可直接跳過驗(yàn)證邏輯直接發(fā)包修改)。
4.手機(jī)、郵箱驗(yàn)證、找回密碼等涉及到動(dòng)態(tài)驗(yàn)證碼等功能未限制驗(yàn)證碼失敗次數(shù)、驗(yàn)證碼有效期、驗(yàn)證碼長度過短導(dǎo)致的驗(yàn)證碼爆破問題。
5.充值、付款等功能調(diào)用了第三方支付系統(tǒng)未正確校驗(yàn)接口(如:1分錢買IPhone X)。
6.后端采用了ORM框架更新操作時(shí)因處理不當(dāng)導(dǎo)致可以更新用戶表任意字段(如:用戶注冊、用戶個(gè)人資料修改時(shí)可以直接創(chuàng)建管理員賬號或其他越權(quán)修改操作)。
7.后端采用了ORM框架查詢數(shù)據(jù)時(shí)因處理不當(dāng)導(dǎo)致可以接收任何參數(shù)導(dǎo)致的越權(quán)查詢、敏感信息查詢等安全問題。
8.用戶中心轉(zhuǎn)賬、修改個(gè)人資料、密碼、退出登陸等功能未采用驗(yàn)證碼或Token機(jī)制導(dǎo)致存在CSRF漏洞。
9.后端服務(wù)過于信任前端,重要的參數(shù)和業(yè)務(wù)邏輯只做了前端驗(yàn)證(如:文件上傳功能的文件類型只在JS中驗(yàn)證、后端不從Session中獲取用戶ID、用戶名而是直接接收客戶端請求的參數(shù)導(dǎo)致的越權(quán)問題)。
10.用戶身份信息認(rèn)證邏輯問題(如:后臺系統(tǒng)自動(dòng)登陸時(shí)直接讀取Cookie中的用戶名、用戶權(quán)限不做驗(yàn)證)。
11.重要接口采用ID自增、ID可預(yù)測并且云端未驗(yàn)證參數(shù)有效性導(dǎo)致的越權(quán)訪問、信息泄漏問題(如:任意用戶訂單越權(quán)訪問)。
12.條件競爭問題,某些關(guān)鍵業(yè)務(wù)(如:用戶轉(zhuǎn)賬)不支持并發(fā)、分布式部署時(shí)不支持鎖的操作等。
13.重要接口未限制請求頻率,導(dǎo)致短信、郵件、電話、私信等信息轟炸。
14.敏感信息未保護(hù),如Cookie中直接存儲用戶密碼等重要信息。
15.弱加密算法、弱密鑰,如勿把Base64當(dāng)成數(shù)據(jù)加密方式、重要算法密鑰采用弱口令如123456。
16.后端無異常處理機(jī)制、未自定義50X錯(cuò)誤頁面,服務(wù)器異常導(dǎo)致敏感信息泄漏(如:數(shù)據(jù)庫信息、網(wǎng)站絕對路徑等)。
17.使用DWR框架開發(fā)時(shí)前后端不分漏洞(如:DWR直接調(diào)用數(shù)據(jù)庫信息把用戶登陸邏輯直接放到了前端來做)。
代碼審計(jì)的核心是尋找代碼中程序?qū)崿F(xiàn)的安全問題,通常我們會把代碼審計(jì)的重心放在SQL注入、文件上傳、命令執(zhí)行、任意文件讀寫等直接威脅到服務(wù)器安全的漏洞上,因?yàn)檫@一類的漏洞殺傷力極大也是最為致命的。
###2.1 代碼實(shí)現(xiàn)中常見的安全問題Checklist
1.任意文件讀寫(文件上傳、文件下載)、文件遍歷、文件刪除、文件重命名等漏洞
2.SQL注入漏洞
3.XXE(XML實(shí)體注入攻擊)
4.表達(dá)式執(zhí)行(SpEL、OGNL、MVEL2、EL等)
5.系統(tǒng)命令執(zhí)行漏洞(ProcessBuilder)
6.反序列化攻擊(ObjectInputStream、JSON、XML等)
7.Java反射攻擊
8.SSRF攻擊
2.1.1 Java 文件名空字節(jié)截?cái)嗦┒?%00 Null Bytes)
空字節(jié)截?cái)嗦┒绰┒丛谥T多編程語言中都存在,究其根本是Java在調(diào)用文件系統(tǒng)(C實(shí)現(xiàn))讀寫文件時(shí)導(dǎo)致的漏洞,并不是Java本身的安全問題。不過好在高版本的JDK在處理文件時(shí)已經(jīng)把空字節(jié)文件名進(jìn)行了安全檢測處理。
2013年9月10日發(fā)布的Java SE 7 Update 40修復(fù)了空字節(jié)截?cái)噙@個(gè)歷史遺留問題。此次更新在java.io.File類中添加了一個(gè)isInvalid方法,專門檢測文件名中是否包含了空字節(jié)。
修復(fù)的JDK版本所有跟文件名相關(guān)的操作都調(diào)用了isInvalid方法檢測,防止空字節(jié)截?cái)唷?/p>
修復(fù)前(Java SE 7 Update 25)和修復(fù)后(Java SE 7 Update 40)的對比會發(fā)現(xiàn)Java SE 7 Update 25中的java.io.File類中并未添加\u0000的檢測。
受空字節(jié)截?cái)嘤绊懙腏DK版本范圍:JDK<1.7.40,單是JDK7于2011年07月28日發(fā)布至2013年09月10日發(fā)表Java SE 7 Update 40這兩年多期間受影響的就有16個(gè)版本,值得注意的是JDK1.6雖然JDK7修復(fù)之后發(fā)布了數(shù)十個(gè)版本,但是并沒有任何一個(gè)版本修復(fù)過這個(gè)問題,而JDK8發(fā)布時(shí)間在JDK7修復(fù)以后所以并不受此漏洞影響。
參考:
JDK-8014846 : File and other classes in java.io do not handle embedded nulls properly。
維基百科-Java版本歷史
Oracle Java 歷史版本下載
2.1.2 測試Java寫文件截?cái)鄿y試
測試類FileNullBytes.java:
import java.io.File; import java.io.FileOutputStream; import java.io.IOException; /** * @author yz */ public class FileNullBytes { public static void main(String[] args) { try { String fileName = "/tmp/null-bytes.txt\u0000.jpg"; FileOutputStream fos = new FileOutputStream(new File(fileName)); fos.write("Test".getBytes()); fos.flush(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } }
使用JDK1.7.0.25測試成功截?cái)辔募?/p>
使用JDK1.7.0.80測試寫文件截?cái)鄷r(shí)拋出java.io.FileNotFoundException: Invalid file path異常:
空字節(jié)截?cái)嗬脠鼍?/strong>
Java空字節(jié)截?cái)嗬脠鼍白畛R姷睦脠鼍熬褪俏募蟼鲿r(shí)后端使用了endWith、正則使用如:.(jpg|png|gif)$驗(yàn)證文件名后綴且文件名最終原樣保存,同理文件刪除(delete)、獲取文件路徑(getCanonicalPath)、創(chuàng)建文件(createNewFile)、文件重命名(renameTo)等方法也可適用。
空字節(jié)截?cái)嘈迯?fù)方案
最簡單直接的方式就是升級JDK,如果擔(dān)心升級JDK出現(xiàn)兼容性問題可在文件操作時(shí)檢測下文件名中是否包含空字節(jié),如JDK的修復(fù)方式:fileName.indexOf('\u0000')即可。
2.1.2 任意文件讀取漏洞
任意文件讀取漏洞即因?yàn)闆]有驗(yàn)證請求的資源文件是否合法導(dǎo)致的,此類漏洞在Java中有著較高的幾率出現(xiàn),任意文件讀取漏洞看似很簡單,但是在這個(gè)問題上翻車的有不乏一些知名的中間件:Weblogic、Tomcat、Resin又或者是主流MVC框架:Spring MVC、Struts2。所以在審計(jì)文件讀取功能的時(shí)候要非常仔細(xì),或許很容易就會有意想不到的收獲!
任意文件讀取示例代碼file-read.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="java.io.ByteArrayOutputStream" %> <%@ page import="java.io.File" %> <%@ page import="java.io.FileInputStream" %> <% File file = new File(request.getParameter("path")); FileInputStream fis = new FileInputStream(file); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] b = new byte[1024]; int a = -1; while ((a = fis.read(b)) != -1) { baos.write(b, 0, a); } out.write("<pre>" + new String(baos.toByteArray()) + "</pre>"); fis.close(); %>
訪問file-read.jsp文件即可讀取任意文件:http://localhost:8080/file/file-read.jsp?path=/etc/passwd
快速發(fā)現(xiàn)這類漏洞得方式其實(shí)也是非常簡單的,在IDEA中的項(xiàng)目中重點(diǎn)搜下如下文件讀取的類。
1.JDK原始的java.io.FileInputStream類
2.JDK原始的java.io.RandomAccessFile類
3.Apache Commons IO提供的org.apache.commons.io.FileUtils類
4.JDK1.7新增的基于NIO非阻塞異步讀取文件的java.nio.channels.AsynchronousFileChannel類。
5.JDK1.7新增的基于NIO讀取文件的java.nio.file.Files類。常用方法如:Files.readAllBytes、Files.readAllLines
如果仍沒有什么發(fā)現(xiàn)可以搜索一下FileUtil很有可能用戶會封裝文件操作的工具類。
Java WebSevice
Web Service是一種基于SOAP協(xié)議實(shí)現(xiàn)的跨語言Web服務(wù)調(diào)用,在Java中Web Service有如下技術(shù)實(shí)現(xiàn):Oracle JWS、Apache Axis1、2、XFire、Apache CXF、JBossWS。
Axis1.4 配置
web.xml配置Axis1.4
配置server-config.wsdd文件注冊Web Service服務(wù)類和方法:
FileService類,提供了文件讀寫接口:
使用IDEA創(chuàng)建Web Service項(xiàng)目默認(rèn)會創(chuàng)建管理Web Service的API:/servlet/AxisServlet、/services、SOAPMonitor、/servlet/AdminServlet,*.jws以及用監(jiān)控Web Service的端口5001或5101。
訪問Web Service的FileService服務(wù)加上?wsdl參數(shù)可以看到FileService提供的服務(wù)方法和具體的參數(shù)信息。
使用SOAP-UI調(diào)用Web Service接口示例:
需要注意的是Web Service也是可以設(shè)置授權(quán)認(rèn)證的,如實(shí)現(xiàn)了WS-Security的WSS4J。
使用IDEA根據(jù)wsdl生成Web Service客戶端代碼:
設(shè)置wsdl地址、包名:
新建FileServiceTest類測試接口調(diào)用:
package org.javaweb.codereview.axis.client; import java.net.URL; /** * 文件Web Service服務(wù)測試 * * @author yz */ public class FileServiceTest { public static void main(String[] args) { try { FileServiceService fileService = new FileServiceServiceLocator(); URL webServiceUrl = new URL("http://localhost:8080/services/FileService"); FileServiceSoapBindingStub soapService = new FileServiceSoapBindingStub(webServiceUrl, fileService); String content = soapService.readFile("/etc/passwd"); System.out.println(content); } catch (Exception e) { e.printStackTrace(); } } }
關(guān)于如何分析Java Web安全中的代碼審計(jì)就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。
分享文章:如何分析JavaWeb安全中的代碼審計(jì)
標(biāo)題路徑:http://jinyejixie.com/article28/gpgejp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)、網(wǎng)站收錄、標(biāo)簽優(yōu)化、App開發(fā)、App設(shè)計(jì)、移動(dòng)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)