标题: Java底层修改对XXE利用FTP通道的影响
创建: 2019-11-01 11:22 更新: 2019-11-12 14:17 链接: https://scz.617.cn/web/201911011122.txt
目录:
☆ 背景介绍
☆ sun.net.ftp.impl.FtpClient.issueCommand()
☆ sun.net.www.protocol.ftp.FtpURLConnection.checkURL()
☆ 参考资源
☆ 背景介绍
本篇没有讲述XXE利用FTP通道,也不提供任何额外XXE利用技巧,仅仅是从Java逆向 工程角度探究一下Java底层修改对XXE利用FTP通道的影响,属于"无用"知识,当然, 这要看你如何定义"有用"。
过去在XXE利用中,有时会利用FTP命令向外传递被窃取的数据,这些数据本身可能包 含\r、\n等字符,xxer([2])就是用来接收这些数据的。
不知从何时开始,Java底层对FTP通道做了修改,比如FTP命令中部不得包含\n,这使 得XXE利用FTP通道时被严重干挠。
☆ sun.net.ftp.impl.FtpClient.issueCommand()
参[1],tint0提到Java底层对FtpClient的修改。
用JD-GUI查看:
X:\Java\jdk1.8.0_221\jre\lib\rt.jar
sun.net.ftp.impl.FtpClient.class
private boolean issueCommand ( String paramString ) throws IOException, FtpProtocolException { if ( !isConnected() ) { throw new IllegalStateException( "Not connected" ); } if ( this.replyPending ) { try { completePending(); } catch ( FtpProtocolException localFtpProtocolException1 ) { } } / * * 如果FTP命令中包含\n,抛出异常 / if ( paramString.indexOf( '\n' ) != -1 ) { FtpProtocolException localFtpProtocolException2 = new FtpProtocolException( "Illegal FTP command" );
localFtpProtocolException2.initCause( new IllegalArgumentException( "Illegal carriage return" ) );
throw localFtpProtocolException2;
}
sendServer( paramString + "\r\n" );
return readReply();
}
X:\Java\jdk1.8.0_221\src.zip
这里面没有FtpClient.java。网上找到一个老版本源码:
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8-b132/src/share/classes/sun/net/ftp/impl/FtpClient.java
/*
* Sends a command to the FTP server and returns the error code
* (which can be a "success") sent by the server.
*
* @param cmd
* @return true
if the command was successful
* @throws IOException
/
private boolean issueCommand ( String cmd ) throws IOException
{
if ( !isConnected() )
{
throw new IllegalStateException( "Not connected" );
}
if ( replyPending )
{
try
{
completePending();
}
catch ( sun.net.ftp.FtpProtocolException e )
{
// ignore...
}
}
sendServer( cmd + "\r\n" );
return readReply();
}
老版JDK 8源码中没有检查FTP命令中是否包含\n,JDK 1.8.0_221有检查。
按leadroyal的说法([4]),2018年3月,7u141、8u162修改了issueCommand()。
http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/jdk7u141-b00/src/share/classes/sun/net/ftp/impl/FtpClient.java http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u162-b00/src/share/classes/sun/net/ftp/impl/FtpClient.java
☆ sun.net.www.protocol.ftp.FtpURLConnection.checkURL()
vulnd_xxe([3])是个XXE靶场,跟xxer是同一个作者,如果在最新版Java 8上测试 vulnd_xxe+xxer,FTP通道已经无法向外传递被窃取的数据。
最初发现FTP通道被阻断,以为就是tint0说的issueCommand()中的修改所致。当时想 "redefine sun.net.ftp.impl.FtpClient",把对\n的检查临时Patch掉,先测了 vulnd_xxe+xxer再说。为此对issueCommand()设断,试图看其形参内容,结果发现数 据中包含\n时,根本断不下来,同时在xxer中看不到任何FTP数据进来;说明在流程 到达issueCommand()之前另有一些检查存在,想把它找出来。
先用不带\n的数据断在issueCommand(),获取其调用栈回溯:
@sun.net.ftp.impl.FtpClient.issueCommand() at sun.net.ftp.impl.FtpClient.issueCommandCheck(FtpClient.java:550) at sun.net.ftp.impl.FtpClient.tryLogin(FtpClient.java:1029) at sun.net.ftp.impl.FtpClient.login(FtpClient.java:1056) at sun.net.www.protocol.ftp.FtpURLConnection.connect(FtpURLConnection.java:328) at sun.net.www.protocol.ftp.FtpURLConnection.getInputStream(FtpURLConnection.java:417) at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:623) at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:1304) at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:1240) at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.startPE(XMLDTDScannerImpl.java:741) at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.skipSeparator(XMLDTDScannerImpl.java:2110) at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.scanDecls(XMLDTDScannerImpl.java:2073) at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.scanDTDInternalSubset(XMLDTDScannerImpl.java:363)
对各层函数设断,用带\n的数据测试,在前述调用栈回溯中找到最后一个被调用到的 函数:
sun.net.www.protocol.ftp.FtpURLConnection.getInputStream // 无命中 com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity // 有命中
简单跟踪setupCurrentEntity()内部执行时(只遍历一层),发现抛出异常:
java.lang.IllegalArgumentException
重新调试,查看IllegalArgumentException的调用栈回溯:
@java.lang.IllegalArgumentException.
sun.net.www.protocol.ftp.FtpURLConnection.checkURL()中检查是否出现\n,检查 失败时,根本不会创建FTP连接,issueCommand()新增的\n检查没有派上用场。
这是1.8.0_232中的checkURL():
static URL checkURL(URL u) throws IllegalArgumentException { if (u != null && u.toExternalForm().indexOf(10) > -1) { MalformedURLException mfue = new MalformedURLException("Illegal character in URL"); throw new IllegalArgumentException(mfue.getMessage(), mfue); } String s = IPAddressUtil.checkAuthority(u); if (s != null) { MalformedURLException mfue = new MalformedURLException(s); throw new IllegalArgumentException(mfue.getMessage(), mfue); } return u; }
FtpURLConnection(URL url, Proxy p) { super(FtpURLConnection.checkURL(url)); this.instProxy = p; this.host = url.getHost(); this.port = url.getPort(); String userInfo = url.getUserInfo(); if (userInfo != null) { int delimiter = userInfo.indexOf(58); if (delimiter == -1) { this.user = ParseUtil.decode(userInfo); this.password = null; } else { this.user = ParseUtil.decode(userInfo.substring(0, delimiter++)); this.password = ParseUtil.decode(userInfo.substring(delimiter)); } } }
public FtpURLConnection(URL url) { this(url, null); }
看一下老版本源码:
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8-b132/src/share/classes/sun/net/www/protocol/ftp/FtpURLConnection.java
/*
* Creates an FtpURLConnection from a URL.
*
* @param url The URL
to retrieve or store.
/
public FtpURLConnection(URL url) {
this(url, null);
}
/* * Same as FtpURLconnection(URL) with a per connection proxy specified / FtpURLConnection(URL url, Proxy p) { super(url); instProxy = p; host = url.getHost(); port = url.getPort(); String userInfo = url.getUserInfo();
if (userInfo != null) { // get the user and password
int delimiter = userInfo.indexOf(':');
if (delimiter == -1) {
user = ParseUtil.decode(userInfo);
password = null;
} else {
user = ParseUtil.decode(userInfo.substring(0, delimiter++));
password = ParseUtil.decode(userInfo.substring(delimiter));
}
}
}
老版本中FtpURLConnection()没有调用checkURL(),也没有checkURL()。
如果XXE利用FTP通道失败,最好反编译rt.jar确认。
Zimbra 8.6.0用了自带的Java 8,前述checkURL()、issueCommand()对\n的检查都不 存在,从而可以利用FTP命令向外传递被窃取的数据。
☆ 参考资源
[1] A Saga of Code Executions on Zimbra - [2019-03-13] https://blog.tint0.com/2019/03/a-saga-of-code-executions-on-zimbra.html
[2] xxer https://github.com/TheTwitchy/xxer
[3] vulnd_xxe https://github.com/TheTwitchy/vulnd_xxe
[4] 9102年Java里的XXE - [2019-07-17] https://www.leadroyal.cn/?p=914 https://github.com/LeadroyaL/java_xxe_2019