行程卡接口安全测试

起因

这段时间每天都要下楼做核酸,而做核酸需要查验两码。

由于我13天前有一个外地的行程轨迹(非疫情严重地区),所以今天在做核酸检测的时候,遇到了一点点小麻烦。

好奇心驱使,就想看看这个行程卡的查询到底是怎么去实现的,并且对行程卡api进行了一些简单的安全测试,以希望能够帮助提高行程卡的安全性。

摸索了一下,大概有两种方式查询行程卡:

  1. 需要输入手机号码,获取验证码短信
  2. 支付宝直接授权当前手机号码

第一种方式:短信验证码

环境:PC端浏览器,burpsuite抓包

网址:https://xc.caict.ac.cn/#/login(中国信通院的域名)

img

流程:输入手机号,点击“获取二维码”。

此时,浏览器会向/dYvFMYL8/h8A2xuUsHKoMz发一个POST请求包

img

手机号是随便从一个接码平台上找的,可惜接码平台接不了行程卡的短信。。。

请求包里有4个字段,虽然都被混淆了,但还是可以一眼看出前2个字段的含义。

  • p090983d:手机号码
  • s324234m:当前时间

后面两个字段一看就是hex编码的数据,解码一下可知,原始数据为16字节

F12看js代码,搜字符串"h8A2xuUsHKoMz",找到相关代码:

image-20220415195424963

  • q435434f:客户端随机生成的16字节uuid,作为queryID唯一标识此次请求

最后一个字段s234234s,是由代码_0xfc3b9f()('MOFXTCJq8bOhlSi' + _0x3759ef)生成。其中变量_0x3759ef就是s324234m字段,也就是当前时间Date的字符串表示。这里在前面又补了一段随机字符串MOFXTCJq8bOhlSi,并把补完的字符串作为参数传入_0xfc3b9f()函数中。追了一下_0xfc3b9f(),混淆太多了,看不出来。16字节,盲猜一下是md5,本地测了一下,果然!

image-20220415200351910

  • s234234s: md5("MOFXTCJq8bOhlSi" + s324234m).hexdigest(),用作签名校验

测了一下这个接口:

  • 4个字段缺一不可
  • 从报错信息中看出来后端是Java写的,com.fasterxml.jackson.core(没什么比较猛的现成Nday)
  • s324234m(Date)字段跟当前时间相差不能超过15min
  • 可以对这个请求包原封不动地重放
    • 如果验证码还未使用,则服务端会返回“您的验证码仍在有效期内”
    • 如果验证码被使用过,则服务器会重新给手机发短信验证码(感觉这是个小问题,影响不大)
  • 理论上,由于在发短信前没有一层人工验证码的check,所以非法分子其实是可以利用这个api接口来“短信轰炸”的(尚未测试高频率访问是否会被禁ip)

用户收到短信验证码后,就可以点“查询”按钮对行程信息进行查询。

浏览器会向/PMfdZtmQM6PIu/jKDFMlURRsN6D9发请求包。

image-20220415202308331

此处短信验证码随便填的“123456”。

image-20220415202543259

请求包中有5个字段,稍微看看js代码也能看出来每个字段的具体含义

  • p234324:手机号
  • q363209:随机的16字节uuid,作为queryID唯一标识此次请求(与请求短信验证码的相同)
  • y892342:6位数字 短信验证码
  • c098465:当前url的key(默认为空字符串“”)
  • s456hr8:md5("OcpqZSOIZOxr0" + p234324).hexdigest()

短信验证码会作为身份认证的凭据,如果不正确,就会返回“非法访问”。

image-20220415203408476

如果短信验证码正确,则会返回一段base64编码的图片数据。随后,浏览器会渲染出图片,呈现出此手机号近14天内行程信息。

测了一下这个接口:

  • 想查别人的行程卡信息,必须要知道其他人的短信验证码,在请求里提供y892342字段才行

  • 验证码只能用一次,重放会返回“验证码错误”

  • 自娱自乐改响应包是可以的

    • 把响应包里的message图片数据改成另外一个图片数据,可以把途径地改成其他的,绕过“某些检测”
    • 改color字段,有3种:“green”、“yellow”、“red”,可以把自己的行程卡改成其他颜色的码:)

    image-20220415231141917

第二种方式:直接授权

环境:手机支付宝, HTTP Catcher(网球)抓包

打开行程卡,支付宝会自动授权本机手机号,然后直接点击“查询”就能查询。

img

用网球抓包,可以抓到2条访问xcweb01.caict.ac.cn(信通院)的流量。

image-20220415231841269

首先访问/alipay/getPhoneByAuthSec,会带4个请求参数(已对authCode、secret字段做了脱敏处理):

1
2
3
4
5
6
{
  "queryId" : "UZGzwVWFsDUY6h2jLjhTqpWdg6hZCRiW",
  "sendTime" : "2022-04-15 21:50:30",
  "authCode" : "79f7************************MX44",
  "secert" : "894b************************dee2"
}

第一个请求是用来获取手机号的,响应包中会返回手机号(已对phone字段做了脱敏处理):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "status": "1",
  "code": "00",
  "errorDesc": "请求成功",
  "result": {
    "query_style": "1",
    "phone": "1**********"
  },
  "queryId": "UZGzwVWFsDUY6h2jLjhTqpWdg6hZCRiW"
}

然后再带着加密的手机号去访问/alipay/queryAdressQuicklyNewSec,访问的queryId保持不变,secret和authCode则会重新生成。(已对phone、secret、authCode字段做了脱敏处理)

1
2
3
4
5
6
7
{
  "phone" : "********************86A==",
  "queryId" : "UZGzwVWFsDUY6h2jLjhTqpWdg6hZCRiW",
  "sendTime" : "2022-04-15 21:50:32",
  "secert" : "8413************************da6c",
  "authCode" : "5877************************HX44"
}

服务器响应包中的message即携带有相关的行程信息(已对phone、message做了脱敏处理)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "status": "1",
  "code": "00",
  "errorDesc": "请求成功",
  "result": {
    "color": "green",
    "phone": "1**********",
    "time": "2022.04.15 21:50:33",
    "message": "IwU9/4J3********************************************************"
  },
  "queryId": "UZGzwVWFsDUY6h2jLjhTqpWdg6hZCRiW"
}

测试发现:

  • 两次请求的secret字段为hex编码的数据,原始数据长度为16字节(猜测为密钥key?或者hash的salt?)
  • 两次请求的authCode字段看似是hex编码的数据,但是末尾都是HX44、MX44,怀疑为一种自定义的校验码
  • 试了好几次,第二次请求里面的phone字段和响应的message字段每次都不会变
  • phone字段是base64编码的数据,解码后得到16字节的数据,怀疑为
    • block_cipher_enc(key, phone)
    • hash(salt + phone)
  • message字段也有可能是这样的?

支付宝小程序,不像浏览器可以直接看到js源码,所以对于这些字段到底是什么含义不太能搞懂。也盲测了很多,都不太对,感觉还是要看源码才行。

搜了一下小程序逆向,网上的方案是:安卓手机root,adb连进去,去找到对应的tar文件,然后拖出来解压。

设备受限,无法下一步,就不深挖了。。


不过还是可以自娱自乐地改一下行程信息。

例如:用朋友的手机号(正常的行程信息)去查询,抓第二次响应包,记录一下里面的message字段。然后网球里面有改包功能,把/alipay/queryAdressQuicklyNewSec响应包里的message字段固定改成想要的取值,就能实现修改行程信息的目的。

以上纯属娱乐行为,请勿用做非法行为