欢迎光临
我们一直在努力

一个基于用户个人信息生成潜在密码猜测的自动化工具:可用于安全测试

一、 程序总览与核心功能

1.1 程序定位

一个批量密码猜测生成器。其核心任务是:读取一个包含多名用户个人信息的结构化文本文件(datasets.txt),针对每一位用户,综合利用其姓名、账号、手机号、生日等敏感信息,并结合常见弱密码模式,生成一个高度定制化的潜在密码列表。最终,将所有用户的猜测密码按顺序写入输出文件(answer.txt),并以 <END>作为每个用户猜测列表的分隔符。

1.2 工作流程

程序遵循一个清晰的“数据输入-处理-输出”管道,其主流程如下图所示(此处以文字描述):

  • 数据读取与解析:从 datasets.txt中读取原始数据,解析成结构化的用户信息字典列表。

  • 逐用户处理:遍历每个用户的信息字典。

  • 多策略猜测生成:对单个用户,并行调用多个生成函数,分别基于姓名、账号、手机号、生日生成猜测,并引入常见密码库。

  • 猜测组合与增强:将上述基础猜测进行组合、拼接数字和后缀,生成更复杂的猜测。

  • 结果清洗与截断:过滤掉长度不合格的猜测,并确保输出数量不超过上限。

  • 结果写入:将当前用户的猜测列表写入文件,并添加分隔符 <END>。

  • 此流程体现了模块化设计思想,将复杂任务分解为多个单一职责的函数,便于维护和扩展。

    二、 数据结构深度解析

    程序的核心数据结构围绕着如何表示单个用户信息以及如何管理生成的密码猜测。

    2.1 用户信息的数据结构:嵌套字典列表

    在 parse_data函数中,原始数据被转化为一个由字典组成的列表 users。

    • users: 类型为 list。该列表包含了所有用户的信息,每个元素代表一个用户。

    • user_data: 类型为 dict。代表单个用户的所有信息。其键值对来源于原始数据文件每行中由冒号分隔的字段。

    一个典型的 user_data字典可能包含如下键:

    {
    'name': '张|三丰', # 原始姓名字段
    'account': 'zhangsan',
    'phone': '138-0013-8000',
    'birth': '19900515',
    'name_parts': ['张', '三丰'] # 解析后的姓名部分列表
    }

    • 关键字段分析:

      • name_parts: 这是一个非常重要的衍生字段。程序通过管道符 |分割原始 name字段,得到一个字符串列表 list[str]。这种设计极具实用性,因为它能灵活处理中文名(如“张|三丰”)、英文名(如“John|Doe”)等多种命名格式,为后续的姓名组合算法奠定了基础。

      • birth: 被假定为固定8位数字的字符串(YYYYMMDD格式),这种标准化处理简化了日期相关猜测的生成逻辑。

      • phone: 允许包含非数字字符,在处理时会被清洗,这增强了程序对真实数据中不规则格式的鲁棒性。

    2.2 密码猜测的数据结构:集合与列表

    在整个生成过程中,密码猜测主要使用两种数据结构管理:

    • set: 在各个生成函数(如 generate_name_based_guesses)内部,猜测被优先添加到集合中。集合的优势在于自动去重,能高效避免生成完全相同的猜测(例如,通过不同策略可能生成相同的字符串)。

    • list: 当函数需要返回结果,或在 generate_user_guesses函数中进行最终处理时,集合会被转换为列表。列表的优势在于有序且可切片,便于进行数量控制(如 [:max_guesses])和写入文件。

    这种“set去重 -> list排序/截断”的模式是处理此类生成任务的经典数据结构选择。

    三、 核心算法与生成策略详解

    程序的安全性测试效果直接取决于其猜测生成算法的强度和多样性。以下逐一剖析每个生成策略。

    3.1 姓名导向的猜测算法 (generate_name_based_guesses)

    该函数是生成策略中最复杂的一部分,旨在模拟用户使用姓名相关元素作为密码的常见习惯。

    • 输入:name_parts(列表),如 ['张', '三丰']。

    • 输出:基于姓名的猜测列表。

    • 算法步骤:

    • 基础变形:对 name_parts中的每个部分(如“张”、“三丰”),分别生成其小写、大写和首字母大写形式。这覆盖了 zhang, ZHANG, Zhang, sanfeng, SANFENG, Sanfeng等常见写法。

    • 组合拼接:如果姓名部分数量大于等于2(通常表示有姓和名),则进行两两组合,特别是第一个和最后一个部分(考虑到中间名情况)。组合方式包括:

      • 直接拼接:zhangsanfeng, sanfengzhang

      • 点号连接:zhang.sanfeng

      • 首字母与另一部分拼接:zsanfeng(姓的首字母+名), zhangs(姓+名的首字母), z.sanfeng

    • 算法评价:此函数考虑到了中西文化差异中常见的密码构造习惯,组合策略多样,是生成高质量猜测的关键。

    3.2 日期导向的猜测算法 (generate_date_based_guesses)

    该函数利用生日信息生成猜测,因为生日是密码中最常用的数字组合之一。

    • 输入:birth(8位数字字符串),如 '19900515'。

    • 输出:基于生日的猜测列表。

    • 算法步骤:对生日字符串进行不同粒度的切分和重组。

      • 19900515(完整8位)

      • 1990(年份,4位)

      • 90(年份后两位,2位)

      • 0515(月日,4位)

      • 1505(日月,4位,常见于欧洲日期格式)

      • 19900515(同完整8位,此处可能冗余)

      • 051590(月日+年后两位,6位)

    • 算法评价:覆盖了常见的日期格式变体,但假设输入是完美的8位数字。如果现实数据格式不统一(如1990-05-15),则依赖前期的数据清洗。

    3.3 手机号导向的猜测算法 (generate_phone_based_guesses)

    函数处理可能包含分隔符的手机号,提取纯数字并生成常见片段。

    • 输入:phone(字符串),如 '138-0013-8000'。

    • 输出:基于手机号的猜测列表。

    • 算法步骤:

    • 数据清洗:使用正则表达式 re.sub(r'\\D', '', phone)移除非数字字符,得到 '13800138000'。

    • 截取片段:取后4位 ('8000')、后6位 ('138000'? 注意:此处原文 clean_phone[-6:]从完整号码截取,通常是中间段或尾号,取决于号码结构)、后8位 ('00138000')。这种策略模拟了用户记忆手机号局部作为密码的习惯。

    • 算法评价:清洗步骤增强了健壮性。截取尾号的策略符合常见行为,但截取中间6位或8位的有效性高度依赖于手机号的编码规则和用户习惯。

    3.4 账号导向的猜测算法 (generate_account_based_guesses)

    算法较为简单,直接对账号字符串进行大小写变换。

    • 输入:account(字符串),如 'zhangsan'。

    • 输出:['zhangsan', 'ZHANGSAN', 'Zhangsan']。

    • 算法评价:虽然简单,但确有必要,因为很多用户会直接使用账号或其简单变形作为密码。

    3.5 常见密码库 (generate_common_patterns)

    返回一个预定义的常见弱密码列表,如 '123456', 'password'等。

    • 算法评价:这是密码生成器的标准配置,用于捕获那些与用户个人信息无关的、广泛存在的弱密码。这个列表可以随时扩展以增强威力。

    3.6 组合与增强算法 (generate_combined_guesses)

    这是整个程序中最强大、最能体现“智能”的部分。它不直接依赖原始数据,而是将前面生成的基础猜测进行二次加工,模拟用户创建“复杂”密码的常见模式。

    • 输入:user_data(字典),用于获取基础猜测。

    • 输出:组合后的复杂猜测列表。

    • 算法步骤:

    • 收集基础猜测:调用所有基础生成函数,得到一个庞大的基础猜测列表 all_base_guesses。

    • 双猜测拼接:使用双重循环,将基础猜测列表中的前20个元素两两拼接(guess1 + guess2)。例如,姓名猜测 'zhang'和生日猜测 '1990'拼接为 'zhang1990'。为了避免组合爆炸,对循环范围进行了限制 ([:20]和 [:10])。

    • 添加数字后缀:遍历基础猜测列表中的前50个元素,为每个猜测添加流行的数字模式:

      • 单个数字:guess + '0'… guess + '9'

      • 重复数字:guess + '00'… guess + '99'

      • 年份:guess + '1990'(完整年), guess + '90'(短年)

    • 算法评价:此策略极大地提高了猜测的覆盖面和成功率。许多用户认为在单词后加数字就很安全,此算法正是针对这种心理。通过限制循环范围(如前20、前10、前50),在保证生成多样性的同时,有效控制了计算复杂度和输出文件大小,是一种实用的工程折衷。

    四、 工程实现与代码质量分析

    4.1 模块化与函数设计

    程序高度模块化。每个生成函数职责单一,接受明确的输入,返回明确的输出。这使得代码易于阅读、测试和修改。例如,要添加一个新的生成策略(如基于邮箱前缀),只需编写一个新函数并在 generate_user_guesses中调用即可。

    4.2 健壮性处理

    代码在多个地方体现了对异常或脏数据的考虑:

    • parse_data中使用 if not line.strip(): continue跳过空行。

    • 各个生成函数(如 generate_name_based_guesses, generate_date_based_guesses)在开头会检查输入是否有效(如 name_parts是否空,birth_date长度是否为8),避免无效操作。

    • generate_user_guesses最后有长度验证:if g and len(g) >= 3 and len(g) <= 30,确保生成的猜测符合一般密码系统的长度要求。

    4.3 性能与资源管理

    • 去重:广泛使用 set进行中间结果存储,避免重复,节省内存和计算资源。

    • 数量控制:

      • generate_combined_guesses中通过切片 ([:20], [:10], [:50]) 限制组合数量,防止组合爆炸。

      • generate_user_guesses最终通过 [:max_guesses]硬性截断,确保输出不会无限大(默认上限为10000)。

    • I/O操作:文件读写使用 with语句,确保资源被正确释放。

    4.4 潜在改进点

  • 配置化:常见密码列表、组合策略的循环次数、密码长度限制、最大猜测数等硬编码在代码中。可以将其提取到外部配置文件,增加灵活性。

  • 日期格式灵活性:当前算法假设生日是 YYYYMMDD。可以增强 parse_data函数,使其能解析更多格式(如 YYYY-MM-DD),并在生成日期猜测时产生更多变体(如 DD-MM-YYYY)。

  • 更精细的组合控制:generate_combined_guesses中的循环范围是固定的。可以引入更智能的启发式方法,优先组合那些最有可能的猜测对。

  • 键盘模式:可以加入基于键盘布局的猜测(如 qwerty, 1qaz2wsx),这同样是常见的弱密码模式。

  • 五、 总结

    一个设计良好、功能实用的密码猜测生成工具。它通过模块化的函数设计清晰地定义了数据处理流程,利用字典和集合等高效数据结构来存储和去重信息,并实现了多维度、深浅结合的生成算法(从简单的姓名变形到复杂的组合与数字后缀添加)。程序不仅涵盖了安全领域内众所周知的弱密码模式,还通过组合策略模拟了用户创建“自认为安全”密码的常见行为,具有较高的实用价值。

    尽管在日期格式解析和策略配置化方面有提升空间,但其整体的架构和算法实现已经相当成熟,有效地平衡了猜测的威力、生成效率和资源消耗。该程序是理解社会工程学在密码安全中的应用以及如何自动化进行安全测试(在合法合规前提下)的优秀范例。

    源代码

    import re
    from itertools import product

    def parse_data(file_content):
    """解析数据集"""
    users = []
    lines = file_content.strip().split('\\n')

    for line in lines:
    if not line.strip():
    continue

    user_data = {}
    # 解析各个字段
    fields = line.split('\\t')
    for field in fields:
    if ':' in field:
    key, value = field.split(':', 1)
    user_data[key] = value

    # 解析姓名部分
    if 'name' in user_data:
    name_parts = user_data['name'].split('|')
    user_data['name_parts'] = [part.strip() for part in name_parts if part.strip()]
    else:
    user_data['name_parts'] = []

    users.append(user_data)

    return users

    def generate_common_patterns():

    common_passwords = [
    '123456', 'password', '12345678', 'qwerty', 'abc123',
    '1234567', '111111', '1234567890', '123456789', '123123',
    '000000', 'iloveyou', '1234', '1q2w3e4r', 'qwertyuiop',
    '123', 'monkey', 'dragon', '654321', '123321'
    ]
    return common_passwords

    def generate_name_based_guesses(name_parts):

    guesses = set()

    if not name_parts:
    return list(guesses)

    for part in name_parts:
    if len(part) > 1:
    guesses.add(part.lower())
    guesses.add(part.upper())
    guesses.add(part.capitalize())

    if len(name_parts) >= 2:
    first_name = name_parts[0].lower()
    last_name = name_parts[-1].lower()

    guesses.add(first_name + last_name)
    guesses.add(last_name + first_name)
    guesses.add(first_name + '.' + last_name)
    guesses.add(first_name[0] + last_name)
    guesses.add(first_name + last_name[0])
    guesses.add(first_name[0] + '.' + last_name)

    return list(guesses)

    def generate_date_based_guesses(birth_date):

    guesses = set()

    if not birth_date or len(birth_date) != 8:
    return list(guesses)

    year = birth_date[:4]
    month = birth_date[4:6]
    day = birth_date[6:8]

    guesses.add(birth_date) # YYYYMMDD
    guesses.add(year) # YYYY
    guesses.add(year[2:]) # YY
    guesses.add(month + day) # MMDD
    guesses.add(day + month) # DDMM
    guesses.add(year + month + day) # YYYYMMDD
    guesses.add(month + day + year[2:]) # MMDDYY

    return list(guesses)

    def generate_phone_based_guesses(phone):

    guesses = set()

    if not phone:
    return list(guesses)

    clean_phone = re.sub(r'\\D', '', phone)

    if clean_phone:
    guesses.add(clean_phone)
    if len(clean_phone) >= 4:
    guesses.add(clean_phone[-4:])
    if len(clean_phone) >= 6:
    guesses.add(clean_phone[-6:])
    if len(clean_phone) >= 8:
    guesses.add(clean_phone[-8:])

    return list(guesses)

    def generate_account_based_guesses(account):

    guesses = set()

    if not account:
    return list(guesses)

    guesses.add(account)
    guesses.add(account.upper())
    guesses.add(account.capitalize())

    return list(guesses)

    def generate_combined_guesses(user_data):

    guesses = set()

    name_parts = user_data.get('name_parts', [])
    account = user_data.get('account', '')
    phone = user_data.get('phone', '')
    birth = user_data.get('birth', '')

    # 基础猜测
    name_guesses = generate_name_based_guesses(name_parts)
    account_guesses = generate_account_based_guesses(account)
    phone_guesses = generate_phone_based_guesses(phone)
    date_guesses = generate_date_based_guesses(birth)
    common_guesses = generate_common_patterns()

    all_base_guesses = name_guesses + account_guesses + phone_guesses + date_guesses + common_guesses

    for guess1 in all_base_guesses[:20]:
    for guess2 in all_base_guesses[:10]:
    if guess1 != guess2:
    combined = guess1 + guess2
    if len(combined) >= 4 and len(combined) <= 20:
    guesses.add(combined)

    for base_guess in all_base_guesses[:50]:
    for i in range(10):
    guesses.add(base_guess + str(i))
    guesses.add(base_guess + str(i) + str(i))
    for year in range(1950, 2025):
    guesses.add(base_guess + str(year))
    guesses.add(base_guess + str(year)[2:])

    return list(guesses)

    def generate_user_guesses(user_data, max_guesses=10000):

    all_guesses = set()

    all_guesses.update(generate_name_based_guesses(user_data.get('name_parts', [])))
    all_guesses.update(generate_account_based_guesses(user_data.get('account', '')))
    all_guesses.update(generate_phone_based_guesses(user_data.get('phone', '')))
    all_guesses.update(generate_date_based_guesses(user_data.get('birth', '')))
    all_guesses.update(generate_common_patterns())
    all_guesses.update(generate_combined_guesses(user_data))

    valid_guesses = [g for g in all_guesses if g and len(g) >= 3 and len(g) <= 30]

    return valid_guesses[:max_guesses]

    def main():

    with open('datasets.txt', 'r', encoding='utf-8') as f:
    file_content = f.read()

    users = parse_data(file_content)

    with open('answer.txt', 'w', encoding='utf-8') as f:
    for i, user in enumerate(users):
    print(f"处理用户 {i + 1}/{len(users)}…")

    guesses = generate_user_guesses(user)

    for guess in guesses:
    f.write(guess + '\\n')

    f.write('<END>\\n')

    print(f"完成!共处理 {len(users)} 个用户")
    print("结果已保存到 answer.txt")

    if __name__ == "__main__":
    main()

    赞(0)
    未经允许不得转载:171主机测评 » 一个基于用户个人信息生成潜在密码猜测的自动化工具:可用于安全测试
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址