找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2703

积分

1

好友

371

主题
发表于 前天 04:08 | 查看: 8| 回复: 0

在攻防演练中,我们经常会遇到目标单位名称提供不准确的情况。这导致在后续搜集资产时“查无此家”,极大地影响了效率。例如,目标可能给出“浙江中医六院”这样的简称,但其备案全称实为“浙江中医药大学附属第六医院”;又或者某单位已更名,但各类备案信息仍未更新。手动对大量此类名称进行纠错既费时又费力,在争分夺秒的演练前期阶段尤为吃亏。为此,开发一个能够批量、自动纠正目标名称的脚本就显得非常必要。

起初,我的思路是寻找一个类似爱企查或天眼查的企业信息查询网站,通过抓包分析后替换参数来实现批量请求。然而,这些大型商业网站的 API 参数和风控校验极为严格,很难绕过。经过一番寻找,最终在 https://riskbird.com/ 这个网站上找到了突破口,成功实现了批量查询。

其核心是编写一个向风鸟(RiskBird)网站发送查询请求的 Python 函数:

def req_riskbird(company_name, token):

    url = "https://riskbird.com/riskbird-api/newSearch"

    headers = {
        "Host": "riskbird.com",
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
        "Accept": "application/json",
        "App-Device": "WEB",
        "Content-Type": "application/json",
        "Origin": "https://riskbird.com",
        "Referer": "https://riskbird.com/search/company",
        "Accept-Language": "zh-CN,zh;q=0.9",
        "Cookie": f"token={token}; app-device=WEB;"
    }

    payload = {
        "queryType": "1",
        "searchKey": company_name,
        "pageNo": 1,
        "range": 10,
        "selectConditionData": "{\"status\":\"\",\"sort_field\":\"\"}"
    }

    try:
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()  # Raise an exception for HTTP errors

        data = response.json()

        if data.get("code") == 20000 and data.get("success"):
            # Extract company names from the response
            company_names = []
            for company in data.get("data", {}).get("list", []):
                if "entName" in company:
                    company_names.append(company["entName"])
            return company_names
        else:
            print(f"Error: {data.get('msg', 'Unknown error')}")
            return []

    except requests.exceptions.RequestException as e:
        print(f"Request failed: {e}")
        return []
    except json.JSONDecodeError:
        print("Failed to parse response as JSON")
        return []

我们指定查询“美的”进行测试,可以看到函数成功返回了包含“美的”关键词的一系列公司名称,功能正常。

查询“美的”返回的公司列表结果

接下来是实现自动化纠错逻辑。我设计了两种模式:当 num 参数为 1 时,程序默认选择匹配度最高的第一个结果;为 2 时,则会列出所有结果让用户手动选择。

def compare_company(company_name, num, token):

    # 调用 req_riskbird 函数获取公司名称列表
    company_names = req_riskbird(company_name, token)

    if not company_names:
        # 如果没有返回任何公司名,则返回空列表
        print("未找到相关公司:",company_name)
        return []

    if num == 1:
        # 如果选择 1,返回第一个结果
        return [company_names[0]]
    elif num == 2:
        # 如果选择 2,返回所有结果并允许用户手动选择
        print("以下是搜索到的所有公司名称:")
        for i, name in enumerate(company_names, 1):
            print(f"{i}. {name}")

        # 用户选择公司
        try:
            print("搜索单位:",company_name)
            selected = int(input("请输入选择的公司编号:"))
            if 1 <= selected <= len(company_names):
                return [company_names[selected - 1]]
            else:
                print("无效的选择")
                return []
        except ValueError:
            print("无效的输入")
            return []
    else:
        print("无效的 num 参数")
        return []

指定参数 -n 2 运行脚本,程序会列出所有搜索结果并等待用户输入选择,功能正常。

手动选择公司编号的交互界面

我们的最终目标是从文件中批量读取目标名称进行处理。因此,需要一个读取文件的函数:

def read_file():
    """
    读取同目录下的tar.txt文件,将每行内容(公司名称)存入列表并返回

    参数:
        无

    返回值:
        list: 包含所有公司名称的列表

    异常:
        如果文件读取失败,将抛出相应的异常
    """
    company_list = []

    try:
        with open('tar.txt', 'r', encoding='utf-8') as file:
            for line in file:
                # 去除每行的空格和换行符
                company_name = line.strip()
                if company_name:  # 确保不添加空行
                    company_list.append(company_name)
        return company_list

    except FileNotFoundError:
        raise FileNotFoundError("错误:tar.txt 文件不存在")
    except IOError:
        raise IOError("错误:无法读取 tar.txt 文件")
    except Exception as e:
        raise Exception(f"读取文件时发生错误: {str(e)}")

将待纠正的单位名单放入 tar.txt,运行脚本后,工具能够成功输出纠正后的标准化名称列表。例如,输入文件中可能包含大量不规范的简称或旧称,输出则是对应的准确全称。

批量处理文件后输出的组织结构对比

在实际攻防演练中,这个工具能发挥巨大作用。例如,某目标单位在信息中使用了简称“中科华微有限公司”,这可能导致资产发现引擎无法准确关联。通过工具纠正,我们得到了其准确的全称“中科华微大数据科技(广东)有限公司”,从而能够顺利展开后续的资产搜集与漏洞扫描工作。

使用工具纠正简称“中科华微有限公司”

另一个典型案例是“浙江省中山医院”。通过查询得知,其完整名称应为“浙江中医药大学附属第三医院(浙江省中山医院)”。这对于精准定位该医院的官网、子域名、备案系统等资产至关重要。

纠正“浙江省中山医院”得到其全称

最终完整的脚本代码如下,它整合了文件读取、API查询、交互选择与结果输出功能,可通过命令行参数灵活调用:

import requests
import json
import argparse

def read_file():
    """
    读取同目录下的tar.txt文件,将每行内容(公司名称)存入列表并返回

    参数:
        无

    返回值:
        list: 包含所有公司名称的列表

    异常:
        如果文件读取失败,将抛出相应的异常
    """
    company_list = []

    try:
        with open('tar.txt', 'r', encoding='utf-8') as file:
            for line in file:
                # 去除每行的空格和换行符
                company_name = line.strip()
                if company_name:  # 确保不添加空行
                    company_list.append(company_name)
        return company_list

    except FileNotFoundError:
        raise FileNotFoundError("错误:tar.txt 文件不存在")
    except IOError:
        raise IOError("错误:无法读取 tar.txt 文件")
    except Exception as e:
        raise Exception(f"读取文件时发生错误: {str(e)}")

def req_riskbird(company_name, token):

    url = "https://riskbird.com/riskbird-api/newSearch"

    headers = {
        "Host": "riskbird.com",
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
        "Accept": "application/json",
        "App-Device": "WEB",
        "Content-Type": "application/json",
        "Origin": "https://riskbird.com",
        "Referer": "https://riskbird.com/search/company",
        "Accept-Language": "zh-CN,zh;q=0.9",
        "Cookie": f"token={token}; app-device=WEB;"
    }

    payload = {
        "queryType": "1",
        "searchKey": company_name,
        "pageNo": 1,
        "range": 10,
        "selectConditionData": "{\"status\":\"\",\"sort_field\":\"\"}"
    }

    try:
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()  # Raise an exception for HTTP errors

        data = response.json()

        if data.get("code") == 20000 and data.get("success"):
            # Extract company names from the response
            company_names = []
            for company in data.get("data", {}).get("list", []):
                if "entName" in company:
                    company_names.append(company["entName"])
            return company_names
        else:
            print(f"Error: {data.get('msg', 'Unknown error')}")
            return []

    except requests.exceptions.RequestException as e:
        print(f"Request failed: {e}")
        return []
    except json.JSONDecodeError:
        print("Failed to parse response as JSON")
        return []

def compare_company(company_name, num, token):
    """
    该函数通过调用 req_riskbird 函数,获取与公司名称相关的搜索结果,
    根据 num 的值来返回用户选择的企业名称。

    参数:
        company_name (str): 需要查询的目标公司名称
        num (int): 选择显示结果的方式,1表示选择第一个结果,2表示输出所有结果并手动选择
        token (str): 用户登录后获取的授权令牌,用于API鉴权

    返回值:
        list: 包含用户选择的公司名称的列表,若查询失败或没有结果,则返回空列表。

    异常:
        若调用 req_riskbird 发生异常,将返回空列表。
    """

    # 调用 req_riskbird 函数获取公司名称列表
    company_names = req_riskbird(company_name, token)

    if not company_names:
        # 如果没有返回任何公司名,则返回空列表
        print("未找到相关公司:",company_name)
        return []

    if num == 1:
        # 如果选择 1,返回第一个结果
        return [company_names[0]]
    elif num == 2:
        # 如果选择 2,返回所有结果并允许用户手动选择
        print("以下是搜索到的所有公司名称:")
        for i, name in enumerate(company_names, 1):
            print(f"{i}. {name}")

        # 用户选择公司
        try:
            print("搜索单位:",company_name)
            selected = int(input("请输入选择的公司编号:"))
            if 1 <= selected <= len(company_names):
                return [company_names[selected - 1]]
            else:
                print("无效的选择")
                return []
        except ValueError:
            print("无效的输入")
            return []
    else:
        print("无效的 num 参数")
        return []

# Example usage
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="主程序功能:根据输入查询公司信息。")
    parser.add_argument("-n", "--num", type=int, choices=[1, 2], default=1,
                        help="选择查询方式:1 - 默认选择匹配度最高的公司;2 - 手动选择公司")
    parser.add_argument("-f", "--file", type=str, default="tar.txt",
                        help="存放公司名称的文件路径,默认为当前目录下的 'tar.txt'")
    parser.add_argument("-t", "--token", type=str, required=True,
                        help="风鸟网站的认证令牌,用于 API 鉴权")

    args = parser.parse_args()

    company_list=read_file()

    res = []
    for i in company_list:
        res.extend(compare_company(i,args.num, args.token))
    for j in res:
        print(j)
    print(f"\n输入{len(company_list)}家公司,输出{len(res)}家公司")

使用方法

  1. 将需要纠正的目标名称按行存入 tar.txt 文件。
  2. riskbird.com 网站获取登录后的 token
  3. 运行命令:python3 公司名纠错.py -t "你的token" -n 1(自动模式)或 -n 2(交互模式)。

这个工具本质上属于 安全/渗透/逆向 资产信息收集环节的辅助脚本,通过 Python 自动化解决了名称数据清洗的痛点,能显著提升在攻防演练、应急响应等场景下的前期准备工作效率。如果你对这类实战型脚本开发感兴趣,欢迎到 云栈社区 交流讨论。




上一篇:从Skill开发到架构设计:AI应用开发中的微服务式实践与感悟
下一篇:C++ STL vector insert函数gcc源码实现与内存管理机制解析
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-24 01:41 , Processed in 0.431977 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表