banner
肥皂的小屋

肥皂的小屋

github
steam
bilibili
douban
tg_channel

SRC資産情報整理Pythonスクリプト

2021-12-08 深夜更新

2 行のコードを追加しました

  • 初期文字列処理を追加し、.を含まないものは直接除去します。なぜなら、ドメイン名や IP は必ず.を含むため、特殊文字列の報告頻度を大幅に減少させました。
  • *.10bis.co.ilのような一般的なワイルドカードを処理しました。この形式は前の*.を直接除去し、*aaa.comaaa.*のような他の形式はそのまま保持します。

コードは下のコードブロックに直接更新されています。


はい、約 1 年更新していませんでしたが、ついに 1 本の文を書きます。

スクリプト誕生の意義#

ご存知の通り、スクリプト、特に Python や Go の小さなスクリプトは、簡単な情報を処理するのに適しています。

SRC を掘り下げる際に、しばしば一部のベンダーが大量の文字をそのまま投げ込んだり、Excelをそのまま投げ込んで「これはテスト範囲です(範囲外の内容)」と言ってきます。

さらに、しばしば中国語や中国語の句読点、IP、内容が混乱しています。

そこで、このような資産を処理し、整理して出力するスクリプトを書きました。

入力 txt は中国語、中国語の句読点、ドメイン名、IP、任意の混乱した文字を含むことができます。

出力 txt は順番にルートドメイン、サブドメイン、IP アドレス(ソート済み)、および URL リンクを含みます。

多くを語らず、スクリプトを直接掲載します。

スクリプト#

#!/usr/bin/env python
# 資産情報整理、元の形式は中国語及び中国語の句読点を含むIP、URL、httpリンクであることができ、単一のIPまたはサブドメインを含むこともできます。
# 抽出後は純粋なドメイン、ルートドメインのコレクション、URLの合計、純IPを含みます。
import tldextract
import re
import sys
from urllib.parse import urlparse
from zhon.hanzi import punctuation
import socket
import os

if len(sys.argv) != 2:
    print("ファイルが指定されていません!")
    exit(0)

subdomain_list, root_domain_list, ip_list, url_list = [], [], [], []
# 元のファイル内容を読み取り、空行を除去
origin_file_content = re.sub(
    r"\n[\s| ]*\n", '', open(str(sys.argv[1]), "r", encoding="utf-8").read())
# 中国語の文字を除去
content_without_chs = re.sub('[\u4e00-\u9fa5]', '', origin_file_content)
# 中国語の句読点を除去
content_without_chs_punctuation = re.sub(
    '[{}]'.format(punctuation), "", content_without_chs)
# フォーマットを単行に整理し、前後の余分な空白を除去
line_list = re.sub("http", " http", content_without_chs_punctuation).split()
# 点を含まない行を除去します。なぜなら、どんな形式でも行中に`.`が必ずあるからです。
line_list = [line.strip() for line in line_list if "." in line]
# ファイルが大きすぎる場合はエラーを報告し、プログラムを直接終了します。
if len(line_list) > 100000:
    print("現在のテキスト解析件数が多すぎます。このスクリプトの使用はお勧めしません。カクカクしますので、分割してこのスクリプトを使用することをお勧めします。")
    print("もちろん、今テキスト分割スクリプトを書くこともできます。")
    exit(0)

# for line in line_list:
#     print(line)
# exit(0)


def domain_extract(string):
    # URLからドメインを抽出する関数、URLまたは純粋なドメインを渡します。
    if bool(re.search(r"[a-zA-Z]", string)) and "." in string:
        # アルファベットを含まない文字列をフィルタリングし、`.`を含まないものをフィルタリングします。
        if "http" in string:
            # URL処理方式
            domain = urlparse(string).netloc.strip()
            if len(domain) and bool(re.search(r"[a-zA-Z]", domain)):
                if ":" in domain:
                    # ポートを含むURLを渡された場合、ポートを除去します。
                    domain = domain.split(":")[0]
                domain_processing(domain.strip().replace("www.", ""))
        else:
            # 純粋なドメイン処理方式
            if ":" in string:
                # ポートを含むURLを渡された場合、ポートを除去します。
                string = string.split(":")[0]
            domain_processing(string.strip().replace("www.", ""))


def domain_processing(domain):
    # ドメイン抽出後の処理ロジック、ドメインがルートドメインであるかどうかを判断し、そうでない場合はサブドメインリストに追加します。
    # ワイルドカードドメインであるかどうかを判断します。ワイルドカードには2つの形式があります。1つは直接`*.xxx.yyy.com`の前のワイルドカードを除去し、他の形式(例えば`*aaa.com`や`aaa.*`)はそのまま保持します。
    domain = domain.lstrip("*.") if "*" in domain and domain.startswith("*.") else domain
    root_domain = tldextract.extract(domain).registered_domain.strip()
    if len(root_domain) and root_domain not in root_domain_list:
        root_domain_list.append(root_domain)
    if domain != root_domain and domain not in subdomain_list:
        subdomain_list.append(domain)


def extract_ip_from_re_ip(re_ip):
    # re_ipリストからIPを抽出します。
    if len(re_ip) == 1:
        # もし純粋なIPであれば、IPをIPリストに追加します。
        addip_to_ip_list(re_ip[0].strip())
    else:
        # もし複数のIPが一致した場合は、分割してip_listに追加します。
        for ip in re_ip:
            addip_to_ip_list(ip.strip())


def addip_to_ip_list(ip):
    # IPをIPリストに追加します。
    ip = ip.strip()
    if ip not in ip_list:
        ip_list.append(ip)


def addurl_to_url_list(line):
    # httpリンクを渡し、httpリンクリスト処理を行います。
    if line.strip() not in url_list:
        url_list.append(line.strip())


def addhttp_and_addurl_to_url_list(line):
    # ポートを含む文字列やHTTPヘッダーがないurlリンクにhttpを追加して処理します。
    url = "http://{}".format(line)
    addurl_to_url_list(url)


def http_domain_url_extract(url):
    # HTTPヘッダーを含むドメインの文字列からURLを抽出します。
    re_url = re.findall(re.compile(
        r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+'), url)
    if re_url and "." in re_url[0]:
        # `.`を追加して判断します。これは、時々httpリンクのマッチングメカニズムが完全でなく、`http://pay-p`のような誤ったリンクも認識される可能性があるためです。
        addurl_to_url_list(re_url[0].strip())
        domain_extract(re_url[0].strip())

# 様々な可能性のある形式を、スクリプトの実際の処理順序に従って並べます。
# www.baidu.com
# www.baidu.com/1.html
# www.baidu.com:81
# 文字化け
# http://www.baidu.com
# http://www.baidu.com:81
# http://11.22.33.44
# http://11.22.33.44:81
# 11.22.33.44:81
# 11.22.33.44


for line in line_list:
    try:
        re_http = re.findall(r"http[s]?://", line)
        re_ip = re.findall(r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}", line)
        if not re_http and not re_ip and "." in line:
            # 非httpリンクで、IPも含まれていない場合、この部分には純粋なドメインとHTTPヘッダーがないリンク及びドメインの後にポートが付いているものが含まれます。特殊文字をフィルタリングし、ドメインを抽出します。
            if line.endswith("/"):
                line.rstrip("/")
            domain_extract(line)
            addhttp_and_addurl_to_url_list(line)
        elif re_http and not re_ip and "." in line:
            # HTTPリンクですがIPを含まない場合、リンクとドメインを直接抽出します。
            if len(re_http) == 1:
                # もし1つのリンクしかない場合
                http_domain_url_extract(line.strip())
            else:
                # もし同じ行に複数のリンクがある場合、すべてのhttpの前に空白を追加し、空白で分離します。
                re_url_list = re.sub("http", " http", line).split()
                for url in re_url_list:
                    http_domain_url_extract(url.strip())
        elif re_http and re_ip and "." in line:
            # httpとIPの両方を含む形式(例:http://222.33.44.55またはhttp://11.22.33.44:81)の処理。IPを抽出し、リンクに追加するだけです。
            extract_ip_from_re_ip(re_ip)
            addurl_to_url_list(line)
        elif not re_http and re_ip and "." in line:
            # httpを含まないがIPを含む文字列をフィルタリングします。
            if ":" not in line:
                # 純粋なIPをフィルタリングし、IPアドレスリストに追加します。
                extract_ip_from_re_ip(re_ip)
            else:
                # IP:PORTの特殊形式をフィルタリングします。このロジックは少し複雑です。
                # もし複数のIP:PORTが連続している場合、複数のIPが一致することは不可能です。したがって、複数のIP:PORTがある場合、間に必ず間隔があります。
                re_ip_port = re.findall(
                    r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}\:\d+", line)
                if len(re_ip) == 1:
                    # もし純粋なIPであれば、
                    addip_to_ip_list(re_ip[0].strip())
                else:
                    # もし複数のIPが一致した場合は、分割してip_listに追加します。
                    for ip in re_ip:
                        addip_to_ip_list(ip.strip())
                if re_ip_port:
                    if len(re_ip_port) == 1:
                        ip_port = re_ip_port[0].strip()
                        addhttp_and_addurl_to_url_list(ip_port)
                    else:
                        # 複数のIP:PORTの特殊形式
                        for i in re_ip_port:
                            ip_port = i.strip()
                            addhttp_and_addurl_to_url_list(ip_port)
                if len(re_ip) != len(re_ip_port):
                    print("IPとIP:PORTの数が異なる場合が発生しました。")
        else:
            print("特殊文字列:{}".format(line))
    except Exception as e:
        continue

# IPアドレスをソート
ip_list = sorted(ip_list, key=socket.inet_aton)

# 結果を集約
dividing_line = "-"*60
results_content = "\t\tルートドメインリストは以下の通りです\n{}\n{}\n{}\n\t\tsubdomainリストは以下の通りです\n{}\n{}\n{}\n\t\tIPリストは以下の通りです\n{}\n{}\n{}\n\t\tURLリストは以下の通りです\n{}\n{}\n".format(
    dividing_line, "\n".join(root_domain_list), dividing_line, dividing_line, "\n".join(subdomain_list), dividing_line, dividing_line, "\n".join(ip_list), dividing_line, dividing_line, "\n".join(url_list))

results_save_path = "parsed_asset.txt"
if (os.path.exists(results_save_path)):
    os.remove(results_save_path)
with open(results_save_path, "a+") as f:
    f.write(results_content)

スクリプト効果#

効果は以下の通りで、補天のいくつかの専用ベンダーをテストしましたが、期待通りの出力が得られました。

台州市大データ発展センター#

image

image

浙江移動情報安全部#

image

image

使用方法:直接 python スクリプト名 ファイル名で実行できます。

関連する提案があればコメント欄で議論できます。

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