0x1 前言
哈喽,师傅们!
最近在研究微信API接口相关漏洞问题,其中对于一个access_token泄露的相关研究发现,网上的这块资料很少,或者说对于这块的研究的文章好多都不全,于是有了写这篇文章的想法,也是主要给师傅们分享下,我对于这块的研究成果,然后希望师傅们有什么看法和意见欢迎在评论区留言。
0x2 微信API接口概述
一、获取接口调用凭据
功能描述
- 获取小程序全局唯一后台接口调用凭据,token
有效期为7200s
,开发者需要进行妥善保存。 - 如使用
云开发
,可通过云调用免维护access_token
调用。 - 如使用
云托管
,也可以通过微信令牌/开放接口
服务免维护 access_token 调用。
浅谈获取access_token
获取接口调用凭证实质就是获取access_token
。在微信接口开发中,许多服务的使用都离不开Access Token,Access Token相当于打开这些服务的钥匙,正常情况下会在7200秒内失效,重复获取将导致上次获取的Token失效,本文将首先介绍如何获取Access Token。
按微信官方的说明,access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
公众平台的API调用所需的access_token的使用及生成方式说明
- 建议公众号开发者使用中控服务器统一获取和刷新access_token,其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,否则容易造成冲突,导致access_token覆盖而影响业务;
- 目前access_token的有效期通过返回的expire_in来传达,目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新access_token。在刷新过程中,中控服务器可对外继续输出的老access_token,此时公众平台后台会保证在5分钟内,新老access_token都可用,这保证了第三方业务的平滑过渡;
- access_token的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新access_token的接口,这样便于业务服务器在API调用获知access_token已超时的情况下,可以触发access_token的刷新流程。
二、如何获取access_token值?
tips注意
- 公众号和小程序均可以使用
AppID
和AppSecret
调用接口来获取access_token。AppID和AppSecret可在“微信公众平台-开发-基本配置”页中获得(需要已经成为开发者,且帐号没有异常状态)。调用接口时,请登录“微信公众平台-开发-基本配置”提前将服务器IP地址添加到IP白名单中,点击查看设置方法,否则将无法调用成功。小程序无需配置IP白名单。 - 这儿需要特别说明的是:在调用所有微信接口时均使用https协议;还有就是如果第三方不使用中控服务器,而是使选择各个业务逻辑点各自去刷新access_taken,那么就有可能会产生冲突,导致服务不稳定。
获取Access Token
获取Access Token接口的网址如下:
https请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=[APPID]&secret=[APPSECRET]
给师傅们看下在一些站点的网页源代码泄露的appid和appsecret参数值,长什么样子
请求参数
我们需要使用上面的获取Access Token接口的网址,然后分别填入上面的两个appid和appsecret参数,如下详情:
属性 | 类型 | 必填 | 说明 |
---|---|---|---|
grant_type | string | 是 | 填写 client_credential |
appid | string | 是 | 小程序唯一凭证,即 AppID,可在「微信公众平台 - 设置 - 开发设置」页中获得。(需要已经成为开发者,且帐号没有异常状态) |
secret | string | 是 | 小程序唯一凭证密钥,即 AppSecret,获取方式同 appid |
返回参数
成功之后,师傅们就可以看到GET传参请求成功的回显结果,回显得到的参数如下:
属性 | 类型 | 说明 |
---|---|---|
access_token | string | 获取到的凭证 |
expires_in | number | 凭证有效时间,单位:秒。目前是7200秒之内的值。 |
返回数据示例
正常情况下,微信会返回下述JSON数据包给公众号:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200
}
错误时微信会返回错误码信息,JSON数据包实示例如下(该示例为AppID无效错误):
{
"errcode":40013,
"errmsg":"invalid appid"
}
常见的错误码
错误码 | 错误描述 | 解决方案 |
---|---|---|
-1 | system error | 系统繁忙,此时请开发者稍候再试 |
40001 | invalid credential access_token isinvalid or not latest | 获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口 |
40013 | invalid appid | 不合法的 AppID ,请开发者检查 AppID 的正确性,避免异常字符,注意大小写 |
40002 | invalid grant_type | 不合法的凭证类型 |
40125 | 不合法的 secret | 请检查 secret 的正确性,避免异常字符,注意大小写 |
40164 | 调用接口的IP地址不在白名单中 | 请在接口IP白名单中进行设置 |
41004 | appsecret missing | 缺少 secret 参数 |
50004 | 禁止使用 token 接口 | |
50007 | 账号已冻结 | |
61024 | 第三方平台 API 需要使用第三方平台专用 token | |
40243 | AppSecret已被冻结,请登录小程序平台解冻后再次调用。 |
三、实现方式
下面的代码我们封装了企业订阅号、媒体订阅号、个人订阅号、测试号的凭证调用。同时使用的Senparc.WeiXin SDK接口直接获取凭证的方式,大家可供参考,得到的凭证我是存在了数据库中,同时支持多公众号统一维护。
List<KeyValuePair<string, object>> parmeters = new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>(WeixinOfficialAccountTable.FieldDeleteMark, 0)
};
var listOfficialAccount = BaseEntity.GetList<WeixinOfficialAccountEntity>(RDIFrameworkService.Instance.WeixinBasicService.GetOfficialAccountDTByValues(parmeters));
if (listOfficialAccount != null && listOfficialAccount.Count() > 0)
{
foreach (WeixinOfficialAccountEntity entity in listOfficialAccount)
{
try
{
if (entity.Category == (int)WeChatSubscriberEnum.EnterpriseSubscriber)
{
if (!string.IsNullOrEmpty(entity.AppId) && !string.IsNullOrEmpty(entity.AppSecret))
{
//方法一:使用Senparc.WeiXin SDK的方法
entity.AccessToken = Senparc.Weixin.QY.CommonAPIs.CommonApi.GetToken(entity.AppId, entity.AppSecret).access_token;
//方式二,直接调用微信的接口方法
//var url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type={0}&appid={1}&secret={2}", "client_credential".AsUrlData(), entity.AppId.AsUrlData(), entity.AppSecret.AsUrlData());
//AccessTokenResult result = Get.GetJson<AccessTokenResult>(url);
//entity.AccessToken = result.access_token;
entity.ModifiedOn = DateTime.Now;
returnValue += RDIFrameworkService.Instance.WeixinBasicService.UpdateOfficialAccount(entity);
}
}
else
{
if (!string.IsNullOrEmpty(entity.AppId) && !string.IsNullOrEmpty(entity.AppSecret))
{
//方法一:使用Senparc.WeiXin SDK的方法
entity.AccessToken = Senparc.Weixin.MP.CommonAPIs.CommonApi.GetToken(entity.AppId, entity.AppSecret).access_token;
//方式二,直接调用微信的接口方法
//var url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type={0}&appid={1}&secret={2}", "client_credential".AsUrlData(), entity.AppId.AsUrlData(), entity.AppSecret.AsUrlData());
//AccessTokenResult result = Get.GetJson<AccessTokenResult>(url);
//entity.AccessToken = result.access_token;
entity.ModifiedOn = DateTime.Now;
returnValue += RDIFrameworkService.Instance.WeixinBasicService.UpdateOfficialAccount(entity);
}
}
}
catch (Exception ex)
{
}
}
}
参考文章如下:
- https://juejin.cn/post/7223245097848193083
- https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_the_WeChat_server_IP_address.html
- https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html
四、获取微信服务器IP地址
浅谈
- 如果公众号基于安全等考虑,需要获知微信服务器的IP地址列表,以便进行相关限制,可以通过该接口获得微信服务器IP地址列表或者IP网段信息。
- 由于出口IP及入口IP可能存在变动,建议用户每天请求接口1次,以便于及时更新IP列表。为了避免造成单点故障,强烈建议用户不要长期使用旧的IP列表作为api.weixin.qq.com的访问入口。
1、 获取微信API接口 IP地址
使用固定IP访问api.weixin.qq.com时,请开发者注意运营商适配,跨运营商访问可能会存在高峰期丢包问题
API接口IP即api.weixin.qq.com的解析地址,由开发者调用微信侧的接入IP
接口调用请求说明
http请求方式: GET
https://api.weixin.qq.com/cgi-bin/get_api_domain_ip?access_token=ACCESS_TOKEN
参数说明
参数 | 是否必须 | 说明 |
---|---|---|
access_token | 是 | 公众号的access_token |
返回说明
正常情况下,微信会返回下述JSON数据包给公众号:
{
"ip_list": [
"IP1",
"IP2",
"IP3",
"IP4",
......
]
}
参数 | 说明 |
---|---|
ip_list | 微信服务器IP地址列表 |
错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):
{
"errcode": 40013,
"errmsg": "invalid appid rid: 66dbb72e-7529f8ad-47f1afb9"
}
2. 获取微信callback IP地址
请开发者确保防火墙、ddos攻击白名单IP内已添加回调IP,以避免误拦截的情况出现。
callback IP即微信调用开发者服务器所使用的出口IP
接口调用请求说明
http请求方式: GET
https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=ACCESS_TOKEN
参数说明
参数 | 是否必须 | 说明 |
---|---|---|
access_token | 是 | 公众号的access_token |
五、在线测试接口的地址
下面这个在线的测试接口的地址是微信公众和小程序的官方测试使用的网址,相比上面的需要记住GET传参的url,下面的在线的测试接口的地址更加方便,且功能点也全
https://mp.weixin.qq.com/debug
比如我之前挖edu找到的这个body请求包,里面有appid和appsecret参数,那么就可以直接放到上面的在线网址使用了
0x3 某edu站点实测
一、信息收集+资产收集
针对于去找微信API接口相关APPid和appSecret泄露,然后可以成功获取access_token值的目标资产,这里简单给师傅们演示下使用FOFA进行相关资产和目标的检索
FOFA使用下面的检索语法如下:
(body="appId" && body="appSecret") && body="access_token"
使用fofa检索,可以看到可以匹配到127个独立的IP,当然这个语法不是固定的,师傅们可以去自己完善相关语法,有好的语法的师傅们也可以在评论区进行相互交流学习
然后在fofa里面找到了一个edu的站点,后面也是对该edu的站点进行一个测试
二、渗透测试
通过上面对fofa的测绘,然后找到的上面的这个edu的站点,像这样的直接右击网页源代码,然后在源代码里面检索appid参数,可以看到下面的源代码中找到了相关参数
然后就是先获取接口调用凭证(access_token)
https请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=[APPID]&secret=[APPSECRET]
使用上面的参数直接调用,发现显示40164状态码,意思是调用接口的IP地址不在白名单中,那就说明这个appid存在,但是被限制了
这个时候我就在想了,既然这个edu的站点存在appid和appSecret参数值泄露,且这个是微信小程序的相关API接口,那么有没有可能这个学校的微信小程序可以找到别的appid参数,且没有做限制,毕竟不同的小程序,做的防护也不一样。
我这里直接使用微信小程序去挨个找这个学校的微信小程序的接口,看看有没有接口可以找到相关泄露appid和appSecret值的接口,然后去获取access_token值
后面在小程序拿到这个GET传参的接口,/mobile/serve/wechat/getConfig,里面可以获取到一个微信小程序的appid和appSecret值
然后直接再次使用上面的获取接口调用凭证(access_token)
https请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=[APPID]&secret=[APPSECRET]
1、这里成功获取到了access_token值
2、下面就再尝试使用刚才获取到的access_token值,去获取微信服务器IP地址
http请求方式: GET https://api.weixin.qq.com/cgi-bin/get_api_domain_ip?access_token=ACCESS_TOKEN
参考文档:
https://developer.work.weixin.qq.com/document/path/92520
0x4 总结
这篇文章读到这里就已经结束了,希望上面的文章对师傅们有帮助。
师傅们可以多去找找相关站点漏洞,然后去看看我写的的相关传参使用的接口,然后去试试能不能成功打出相关API漏洞的泄露,可以尝试去微信小程序去找找相关的appid泄露值,然后去使用我们的传参,然后先去获取token值,然后再去利用微信开发者文章去获取别的对于的泄露的敏感信息,其中还有一个在线的使用站点,上面的文章中也给师傅们了,那个使用起来比较方便。
欢迎师傅们在评论区多交流学习!
文章中涉及的敏感信息均已做打码处理,文章仅做经验分享用途,切勿当真,未授权的攻击属于非法行为!文章中敏感信息均已做多层打码处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任,一旦造成后果请自行承担。