2021-12-08 凌晨更新
添加了兩行代碼
- 添加初始字符串處理,不包含點
.
的直接去除,因為不管是域名還是 IP 都一定包含.,大幅降低了特殊字符串報告頻率 - 處理了一般通配符例如
*.10bis.co.il
,這種直接去除前面的 *.,其他的格式比如*aaa.com
或者aaa.\*
這些格式保留原樣
代碼已直接更新在下面的代碼塊中
是的,快一年沒更新,終於來水一篇文
腳本誕生的意義#
眾所周知,腳本,尤其是 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):
# 域名提取後的處理邏輯,判斷域名是否為根域名,不是則加入子域名列表
# 判斷是否為通配符域名,通配符有兩種形式,一種是直接*.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:
# 如果只有一條鏈接
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\troot domain list as follows\n{}\n{}\n{}\n\t\tsubdomain list as follows\n{}\n{}\n{}\n\t\tip list as follows\n{}\n{}\n{}\n\t\turl list as follows\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 腳本名 文件名 即可
如果有相關建議可以在評論區進行討論