2021-12-08 深夜更新
2 行のコードを追加しました
- 初期文字列処理を追加し、
.
を含まないものは直接除去します。なぜなら、ドメイン名や IP は必ず.
を含むため、特殊文字列の報告頻度を大幅に減少させました。 *.10bis.co.il
のような一般的なワイルドカードを処理しました。この形式は前の*.
を直接除去し、*aaa.com
やaaa.*
のような他の形式はそのまま保持します。
コードは下のコードブロックに直接更新されています。
はい、約 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)
スクリプト効果#
効果は以下の通りで、補天のいくつかの専用ベンダーをテストしましたが、期待通りの出力が得られました。
台州市大データ発展センター#
浙江移動情報安全部#
使用方法:直接 python スクリプト名 ファイル名で実行できます。
関連する提案があればコメント欄で議論できます。