python

批量生成联系人信息并可以导入到手机通讯录

2026-07-06 #社会工程学#渗透测试#CTF#红队攻防

本文仅供合法授权的安全研究与学习使用,请勿用于非法用途。读者应确保行为符合当地法律法规。

前言

在红队社工攻击演练中,”联系人注入”是一种高效的攻击前置手段。攻击者通过生成包含大量虚假联系人的vCard文件(.vcf),导入到目标手机或通讯录中,为后续的钓鱼攻击、水坑攻击建立信任基础。当目标手机通讯录中存在”张主任”、”李局长”等联系人时,攻击者便可以冒充这些身份发送钓鱼短信或诱导转账。

此外,在物理渗透场景中,红队成员可以将生成的联系人文件预装在丢弃的U盘或手机中,当目标拾取并导入联系人后,攻击者可以通过预设的号码进行后续社工攻击。本文基于真实Python项目源码,深入讲解批量vCard联系人生成技术,涵盖姓名生成、号码模拟、关系建模和vCard格式规范。

技术背景

1. vCard格式规范

vCard是电子名片的行业标准格式(RFC 6350),一个典型的vCard条目如下:

1
2
3
4
5
6
7
8
BEGIN:VCARD
VERSION:3.0
N:张三;;;;
FN:张三
TEL;TYPE=CELL:13800138000
BDAY:1990-01-01
UID:550e8400-e29b-41d4-a716-446655440000
END:VCARD
  • N:结构化姓名(姓氏;名字;中间名;前缀;后缀)
  • FN:格式化显示名称
  • TEL;TYPE=CELL:手机号码
  • BDAY:生日
  • UID:唯一标识符(UUID)
  • EMAIL:电子邮箱
  • ADR:地址

2. 社工攻击中的联系人建模

为了让生成的联系人看起来真实可信,需要模拟真实通讯录的结构分布:

  • 家庭关系(约20%):爸爸、妈妈、爷爷、奶奶、叔叔、舅舅等
  • 领导关系(约10%):张主任、李局长、王总等
  • 同事关系(约15%):同事、客户、供应商等
  • 朋友关系(约15%):朋友、同学、战友等
  • 服务人员(约10%):快递员、外卖员、医生等
  • 随机联系人(约30%):普通姓名

3. 关键技术点

  • random模块:随机选择姓氏、名字、号段等
  • uuid模块:为每个联系人生成唯一标识符
  • 百家姓数据:完整的中国姓氏库,包含单姓和复姓
  • 号段模拟:基于真实运营商号段分布生成手机号
  • vCard 3.0格式:遵循RFC 6350标准,确保兼容性

实现思路

项目采用分层建模策略,从简单到复杂分为四个版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌────────────────────────────────────────────┐
│ 联系人生成工具演进路线 │
├────────────────────────────────────────────┤
│ V1 (联系人.py) - 基础版 │
│ └─ 亲戚+领导+随机姓名,仅姓名和电话 │
├────────────────────────────────────────────┤
│ V2 (非虚拟的联系人.py) - 真实号段版 │
│ └─ 家庭+领导+同事+朋友+随机 │
│ └─ 使用真实运营商号段,增加生日字段 │
├────────────────────────────────────────────┤
│ V3 (虚构的号段联系人生成.py) - 虚拟号段版 │
│ └─ 家庭+领导+同事+朋友+服务人员+随机 │
│ └─ 使用虚拟号段避免真实号码冲突 │
│ └─ 增加邮箱、地址字段 │
├────────────────────────────────────────────┤
│ V4 (真实加虚拟的全号段联系人生成.py) │
│ └─ 混合真实+虚拟号段,30条小批量测试 │
└────────────────────────────────────────────┘

核心代码解析

1. 基础版——亲戚与领导建模

基础版本聚焦于社交关系建模,通过称谓生成联系人:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import random
import uuid

# 随机中文姓名生成
def generate_name():
"""
生成随机中文姓名
使用百家姓作为姓氏库,随机搭配名字用字
"""
first_names = "赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张..." # 完整百家姓
last_names = "云荣蓉榕青晴晴子文杰明芳娜玲强军国华丽义益溷坤昆琨..."
name = random.choice(first_names) + "".join(random.sample(last_names, k=random.choice([1, 2])))
return name

# 随机手机号生成(中国大陆格式 1开头11位)
def generate_phone():
"""
生成随机中国大陆手机号码
从常见号段中随机选择前缀,后8位随机生成
"""
prefix = random.choice([
"130","131","132","133","134","135","136","137","138","139",
"150","151","152","153","155","156","157","158","159",
"180","181","182","183","185","186","187","188","189"
])
return prefix + "".join(random.choices("0123456789", k=8))

# 直系亲戚
direct_relatives = ["爸爸", "妈妈", "爷爷", "奶奶", "外公", "外婆"]
# 可以带排行的亲戚
ranked_relatives = ["叔叔", "舅舅", "姑姑", "姨"]
rank_prefixes = ["大", "二", "三", "四", "五"]

def generate_relative_contacts(count):
"""
生成亲戚联系人列表
先确保包含直系亲属,再随机添加带排行的亲戚
"""
relatives_list = []
relatives_list.extend(direct_relatives) # 直系亲属必定存在

# 舅舅/叔叔/姑姑/姨 → 随机加排行(如"大叔叔"、"二舅舅")
for rel in ranked_relatives:
for prefix in random.sample(rank_prefixes, k=random.randint(1, 3)):
relatives_list.append(prefix + rel)

# 数量不足时补充表兄弟姐妹
need_more = count - len(relatives_list)
if need_more > 0:
others = ["表哥", "表弟", "表妹", "堂哥", "堂姐"]
relatives_list.extend(random.sample(others, k=min(len(others), need_more)))

return relatives_list[:count]

代码讲解

  • generate_name():从完整百家姓中随机选取姓氏,再从名字用字池中随机选取1-2个字组成名字。百家姓数据包含单姓和复姓(如”司马”、”欧阳”),增加了真实性。
  • generate_phone():从中国大陆三大运营商的真实号段中随机选择前缀,后8位随机生成。
  • generate_relative_contacts():模拟真实通讯录中亲属称谓的分布规律。直系亲属必定存在,旁系亲属带排行前缀(”大叔叔”、”二舅舅”),这符合中国人的命名习惯。

2. 进阶版——多关系类型与性别区分

进阶版本引入了性别区分、年龄生成和生日计算:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from datetime import datetime

# 家庭关系定义(含性别和年龄范围)
family_relations = {
"爸爸": {"gender": "male", "min_age": 30, "max_age": 70},
"妈妈": {"gender": "female", "min_age": 30, "max_age": 70},
"爷爷": {"gender": "male", "min_age": 60, "max_age": 90},
"奶奶": {"gender": "female", "min_age": 60, "max_age": 90},
"叔叔": {"gender": "male", "min_age": 25, "max_age": 65},
"舅舅": {"gender": "male", "min_age": 25, "max_age": 65},
"姑姑": {"gender": "female", "min_age": 25, "max_age": 65},
# ... 更多关系定义
}

# 生成随机中文姓名(支持性别)
def generate_name(gender=None):
"""
生成随机中文姓名,可根据性别选择对应的名字用字
"""
surname = random.choice(common_surnames)
if gender is None:
gender = random.choice(['male', 'female'])
if gender == 'male':
name_part = random.choice(male_names)
else:
name_part = random.choice(female_names)
# 30%概率生成双字名
if random.random() < 0.3:
name_part += random.choice(["华", "明", "强", "军", "丽", "娟", "伟", "芳"])
return surname + name_part, gender

# 根据年龄生成生日
def generate_birthday(age):
"""
根据年龄生成生日字符串
"""
current_year = datetime.now().year
birth_year = current_year - age
birth_month = random.randint(1, 12)
birth_day = random.randint(1, 28)
return f"{birth_year}-{birth_month:02d}-{birth_day:02d}"

代码讲解

进阶版本的关键改进是引入了关系属性建模family_relations字典不仅定义了关系名称,还包含性别和年龄范围。例如”爷爷”的年龄范围是60-90岁,”表弟”是5-30岁。这样生成的联系人不仅有合理的称谓,还有匹配的性别和生日,大大提高了真实感。

3. 虚拟号段版——避免真实号码冲突

为了避免生成的手机号与真实用户号码冲突,虚拟号段版使用了虚拟运营商号段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 虚拟手机号段(避免与现实中使用的号码冲突)
fake_phone_prefixes = [
"170", "171", "162", "165", "167", # 虚拟运营商号段
"190", "192", "193", "195", "197", # 较少使用的号段
"134", "135", "136", "137", "138" # 部分真实号段(用于混合)
]

def generate_phone():
"""
生成虚拟的中国大陆手机号码,避免与现实中使用的号码冲突
"""
prefix = random.choice(fake_phone_prefixes)
suffix = "".join(random.choices("0123456789", k=8))
# 在第4位固定为0或9,进一步降低与真实号码冲突的概率
suffix = suffix[:3] + random.choice(["0", "9"]) + suffix[4:]
return prefix + suffix

代码讲解

虚拟号段版使用了170/171等虚拟运营商号段,并在号码第4位固定为0或9进行额外区分。这种设计在红队演练中非常重要——避免生成的号码恰好属于某个真实用户,造成误打扰。

4. vCard生成与输出

所有版本最终都通过以下模式生成vCard文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 生成 vCard
vcf_entries = []
total_contacts = 1540
family_count = 300
leader_count = 150
colleague_count = 250
friend_count = 200
service_count = 150
random_count = total_contacts - family_count - leader_count - colleague_count - friend_count - service_count

# 生成家庭联系人vCard条目
family_relations_list = generate_family_contacts(family_count)
for relation in family_relations_list:
name, gender = generate_name(family_relations[relation]["gender"])
phone = generate_phone()
age = random.randint(family_relations[relation]["min_age"], family_relations[relation]["max_age"])
birthday = generate_birthday(age)
email = generate_email(name)
address = generate_address()
uid = str(uuid.uuid4())
entry = f"""BEGIN:VCARD
VERSION:3.0
N:{name};;;{relation};
FN:{name}({relation})
TEL;TYPE=CELL:{phone}
EMAIL:{email}
ADR:;;{address}
BDAY:{birthday}
UID:{uid}
END:VCARD"""
vcf_entries.append(entry)

# 打乱联系人顺序(模拟真实通讯录的随机性)
random.shuffle(vcf_entries)

# 拼接 vcf 内容,使用\r\n换行(vCard标准要求)
vcf_content = "\r\n".join(vcf_entries)

# 保存到文件,使用utf-8-sig编码(带BOM,确保中文正确显示)
file_path = "C:/Users/Administrator/Desktop/Decontacts.vcf"
with open(file_path, "w", encoding="utf-8-sig") as f:
f.write(vcf_content)

代码讲解

  • 比例分配:总1540个联系人按比例分配到不同关系类型,模拟真实通讯录的分布。
  • UUID生成:每个联系人都使用uuid.uuid4()生成唯一标识符,防止导入时重复。
  • 顺序打乱random.shuffle()打乱联系人顺序,避免同类联系人聚集在一起。
  • 编码选择:使用utf-8-sig编码(带BOM),确保Windows系统和手机端正确识别中文。
  • 换行符:使用\r\n换行,符合vCard RFC 6350标准要求。

5. 服务人员联系人生成

虚拟号段版新增了服务人员联系人,进一步增加真实感:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def generate_service_contacts(count):
"""
生成服务人员联系人列表
包括快递员、外卖员、医生、老师等
"""
services = []
service_relations = ["快递员", "外卖员", "物业管理员", "保安", "保洁",
"维修工", "理发师", "医生", "护士", "老师"]
for _ in range(count):
relation = random.choice(service_relations)
surname = random.choice(common_surnames)

# 50% 概率添加公司或机构信息
if random.random() < 0.5:
if relation in ["快递员", "外卖员", "维修工"]:
company = generate_company()
service_name = surname + relation + "(" + company + ")"
elif relation in ["医生", "护士"]:
hospital = random.choice(["人民医院", "中心医院", "第一医院", "协和医院"])
service_name = surname + relation + "(" + hospital + ")"
elif relation in ["老师"]:
school = random.choice(schools)
service_name = surname + relation + "(" + school + ")"
else:
service_name = surname + relation
else:
service_name = surname + relation

services.append(service_name)
return services

代码讲解:服务人员联系人的设计非常贴近生活。例如”张快递员(顺丰科技有限公司)”、”李医生(人民医院)”等,这些联系人在日常通讯录中非常常见。当目标手机中出现这样的联系人时,攻击者可以冒充快递员发送钓鱼链接,或冒充医生进行社工诈骗,成功率较高。

使用方法与运行效果

环境准备

1
2
# 仅需Python标准库,无需额外安装
python 虚构的号段联系人生成.py

运行效果

运行后会在桌面生成Decontacts.vcf文件,包含1540个联系人。将此文件导入手机通讯录后,联系人列表中会出现:

1
2
3
4
5
6
张三(爸爸) - 17012349876
李四(妈妈) - 17198765432
王主任-财务部 - 16201234567
赵同事(经理) - 19098765432
钱快递员(顺丰科技有限公司) - 16701234567
...

在红队演练中的应用场景

  1. 预置联系人注入:在钓鱼U盘中放置vCard文件,诱导目标导入。
  2. 水坑攻击前置:在目标常用网站上植入vCard下载链接。
  3. 社工信任建立:当目标通讯录中存在”张主任”时,攻击者冒充该身份发送信息。
  4. 钓鱼短信铺垫:利用预置的”银行客服”联系人发送钓鱼短信。

防御对策

  1. 通讯录导入审计:企业应管控员工手机通讯录的导入行为,对批量导入操作进行审计。

  2. 陌生号码预警:在即时通讯和邮件系统中,对不在企业通讯录中的联系人进行标记预警。

  3. 安全意识培训:教育员工不要轻易导入来源不明的vCard文件,对陌生联系人保持警惕。

  4. 社工演练:定期开展社工攻击演练,测试员工对虚假联系人的识别能力。

  5. 验证机制:涉及转账、授权等敏感操作时,通过多渠道(电话、视频)验证对方身份,不仅仅依赖通讯录中的联系人信息。

  6. 设备管理:对企业发放的移动设备实施MDM(移动设备管理)策略,限制通讯录的批量修改操作。

总结

本文基于真实项目源码,系统地讲解了批量vCard联系人生成技术。从基础版的亲戚与领导建模,到进阶版的性别区分与生日生成,再到虚拟号段版的服务人员模拟,展现了红队工具的迭代演进过程。

关键要点:

  • 关系建模是核心:真实的通讯录不是随机姓名的堆砌,而是有结构的关系网络。家庭、领导、同事、朋友、服务人员的比例分配直接影响可信度。
  • 号段选择需谨慎:在红队演练中应使用虚拟号段,避免与真实用户号码冲突。
  • vCard格式规范要严格:正确的编码(utf-8-sig)、换行符(\r\n)和UUID标识是确保跨平台兼容的关键。
  • 多字段增强真实感:除姓名和电话外,添加邮箱、地址、生日等字段,使联系人看起来更加真实。

联系人注入是红队社工攻击的重要前置手段,也是蓝队需要重点防范的攻击向量。理解这一技术的原理和实现,有助于双方在社工攻防中取得优势。

评论
分享