声明,所有演示均为真实环境可实际操作,仅供学习参考和讨论,请勿自行实施攻击,本人不承担任何法律责任。
今年上半年,我花了几个月时间深入测试了各类微信第三方服务(公众号、企业号、小程序),发现的数据漏洞很多,甚至触目惊心。虽然网络安全法出台了,但是企业的安全防护不见得就提高了。就拿微信小程序来说事,据某厂商自己描述:
这款微信小程序叫做群应用,号称几十天就斩获千万用户,在微信小程序里属于明星产品,这款小程序主要干吗用的呢?就是把个人信息、联系方式、公司等存储进去,然后发给别人,方便别人快速保存。经过测试,所有用户信息接口直接对外暴露、加密规则对外暴露,直接导致所有用户数据裸奔。
如上图所示,我们只要知道card_id和aid就能够获取一个用户的详细信息。aid看起来是个加密的数据,正常情况是不能遍历的,保证了安全性。但是他们提供了下面这个接口,传递任意uid即用户id,就可以获取card_id和aid。
至此,千万级别用户数据就这么暴露出来了。话说,商家所谓的千万级用户,我只能呵呵。。。
趁着新兴市场的刚刚开始,只要肯花时间,真的可以手握一堆高危漏洞,因为厂商在新型领域投入的服务,确实安全防护要弱一些。
为什么这么说呢?接下来我们以sql注入为例子,对于sql注入这类漏洞的挖掘,一般情况下,我们首先要找到待测试url地址。
常见的sql注入接口大概长这个样子:
http://test.com/article?id=123
http://test.com/newsList?num=3&limit=20
http://test.com/shop?sort=price
这些url地址,都可以直接丢到sqlmap里面跑,通过修改get/post参数进行注入尝试,攻击手法简单,只要在一边等就可以了。
不过目前大部分网站都提高了安全性,让攻击者无法进行sql注入,方法有很多,比如:
1. 阿里云云翼计划网址,参数不能够随意更改。
https://promotion.aliyun.com/ntms/campus2017.html
2. i春秋的某篇文章,这里用到了伪静态页。
https://bbs.ichunqiu.com/thread-24693-1-1.html
3. csdn新闻页,参数进行了数字类型强制转换。
http://geek.csdn.net/news/detail/211237
对于网站而言,这一层保护很重要,提高了安全门槛。那么对于攻击者而言,有没有什么更好的思路呢?
本篇文章,将会给大家介绍一种方法————从微信端进行sql注入攻击。
这种方法,是建立在移动互联网趋势下的。随着移动互联网的发展,大部分互联网产品都不再提供单一的网站服务,产品往往涵盖了移动端APP、移动端web站、微信公众号、小程序、微信/支付宝企业号等,对于企业级产品可能还会作为应用投放到某个平台,比如钉钉平台。程序员在开发以上这些互联网服务时,就没办法像网站那样做同样的安全防护。传统网站的页面可以直接由服务器渲染,将数据接口隐藏起来;但是像APP和微信小程序这类应用服务,数据是通过调用api接口进行获取,这些接口本身必须暴露给客户端。
好了,我们拿i春秋作为演示。(为什么拿i春秋做演示?因为是真爱。)
这是i春秋在微信客户端的展示页面
https://wx.ichunqiu.com/m/courseShare/index.html?id=57945&type=0&from=singlemessage&isappinstalled=0
通过burpsuit抓包分析,我很快找到了api的请求接口:
https://api.ichunqiu.com/rest?app_key=100003&datetime=2017-07-18%2003:16:42&from=100000003&id=57945&method=open.course.list.get×tamp=1500362202&type=0&ver=1&sig=5ddc3d61e0a1556612be2eeb28f53bed&callback=jQuery31107037434192105723_1500362196996&_=1500362196997
将这个url地址进行copy,然后在浏览器进行输入,得到了这么一个页面:
上图说明,i春秋获取数据的接口做了时间限制,超过一段时间,就会返回请求时间无效。也就是说,每次调用这个api接口,都要使用当前最新的时间。没办法,再次抓包,并快速拷贝url地址到浏览器访问,确保时间不失效,最终浏览器显示数据是这样的:
我们再深入分析一下这个接口,还可能有哪些其他限制?在这里,我随意修改了某个get参数,请求如下,提示签名错误:
分析到这里,基本可以得到一个结论:
i春秋的api接口,做了时间限制和签名限制,修改任意参数,都会导致此api接口无法正常运行。
像这种情况,如果我们直接把url地址丢给sqlmap来跑,肯定会失败的,所有请求直接被拦截在时间和签名判断处了,根本没有走到执行数据库这一步。对于小白黑客而言,时间失效这个问题容易搞定,但是签名怎么破,最终只能白白浪费时间去跑sqlmap没有结果,或者直接放弃此处sql注入。
这时,请大家回想一下我最上面提供的案例,其实有一个结论:
当加密规则本身暴露给攻击者时,加密机制再强,也形同裸奔。
没错,我会试图寻找i春秋api接口的加密规则。根据url地址,https://api.ichunqiu.com/rest?app_key=100003&datetime=2017-07-18%2003:16:42&from=100000003&id=57945&method=open.course.list.get×tamp=1500362202&type=0&ver=1&sig=5ddc3d61e0a1556612be2eeb28f53bed&callback=jQuery31107037434192105723_1500362196996&_=1500362196997,GET参数有这么多。
app_key——app的标识
datetime——时间,用于判断时间有效性
from——来源
id——获取数据的id值,不同id获取不同数据
method——api方法名,此为获取课程列表,还有其它方法比如open.course.info.get,open.course.comment.get等,通过字面意思就能知道接口是干嘛的,i春秋接口还是蛮人性的。
timestamp——时间戳,和datetime保持一致
type、ver——这两个参数,目测用处不大
callback——这个关键词,第一反应就是JSONP,看了返回body,确认就是JSONP
sig——重点!只剩下sig了,看名字八九不离十,这个参数就是签名参数。
除了sig,其它所有参数都比较容易构造,只要知道sig的计算方式,那么就能够模拟api发起请求了。
这里吐槽一下i春秋,时间参数不够严谨,如果我选择一个非常大的时间,就可以一直不失效了。还有,我直接加字母,依旧可以,说明程序进行强制类型转换了,如果是我,只要不符合规范,直接出错,而不是进行强制类型转换,避免因为强制类型转换导致一些攻击绕过。
祭出一个nb的,不会被防住的值(哈哈哈):
timestamp='1501223421312310a'
sig计算规则怎么找?
还记得burpsuit抓包么,除了api接口,其实我们还抓到了几个js文件,我们从js文件里面找。
js文件地址:
http://wx.ichunqiu.com/m/courseShare/js/index.min.js?r=0.11729205804666631
果然,搜索到了sig,但是这代码看起来太恶心,明显被压缩了。在这里强烈推荐一款神器————Pretty Beautiful Javascript。
Chrome插件——Pretty Beautiful Javascript,可以让js展示的更美观清晰,利于攻击者分析被压缩的js源码。
js进行美化之后,我们可以发现sig的加密规则,可以看到,不同的method(api方法),sig加密是有区分的,找到我们需要的加密方法:
这是加密的代码:
var e = "";
Date.parse(new Date), (new Date).pattern("yyyy-MM-dd hh:mm:ss");
e += app_key, e += "method=open.course.list.get&", e += app_from, e += ver, e += timestamp, e += dateTime, e += "id=" + GetQueryString("id") + "&", e += "type=" + GetQueryString("type");
var t = e.split("&").sort().join("&"),
i = $.md5(t + "&612e9a2ebe762486d04eb29411344769");
$.ajax({
url: host + t + "&sig=" + i,
type: "GET",
dataType: "jsonp"
}).done(function(e) {
$("#videoPlay").attr("src", e.data.chapter_list[0].course_list[0].course_play_url), fn_foundElementByChapter(e)
}).fail(function() {
console.log("网络异常,请稍后重试")
})
}
到目前为止,所有参数我们都基本清楚了,现在就是如何把这个规则应用到sqlmap了,我们需要使用--eval参数。
参数:--eval
在有些时候,需要根据某个参数的变化,而修改另个一参数,才能形成正常的请求,这时可以用--eval参数在每次请求时根据所写python代码做完修改后请求。
例子:
python sqlmap.py -u "http://www.target.com/vuln.php?id=1&hash=c4ca4238a0b923820dcc509a6f75849b" --eval="import hashlib;hash=hashlib.md5(id).hexdigest()"
上面的请求就是每次请求时根据id参数值,做一次md5后作为hash参数的值。
出于安全起见,i春秋api接口的sqlmap命令,就不在这里给大家展示了。
不过给大家说一个注意事项,关于--eval参数的使用,sqlmap最新版本还有一个bug。
一般情况下,sqlmap这个bug极难触发,但是i春秋API接口本身的特殊性,导致这个bug真的就触发了!!!因为在测试的时候,出现了诡异的失败,才发现了这个bug。
对i春秋API实施攻击的sqlmap eval内容大致如下:
--eval=”import hashlib;str='app_key=' + app_key + '&datetime=' + datetime + '&from=' + from + '&method=' + method + '×tamp=' + timestamp + '&url=' + url + '&ver=' + ver + '&612e9a2ebe762486d04eb29411344769';sig=hashlib.md5(str).hexdigest()”
因为i春秋的get参数是from,所以eval执行的时候有这么一句:
'&from=' + from
而python语言里,from是关键词,不能直接这么用的,会导致程序异常,所以作者增加了一个非常赞的解决办法。这个解决办法,专门用来解决参数中使用import、from等参数时,避免抛出异常,并保证程序正确执行。
代码在lib/request/connect.py文件的,大概980多行到1000行之间:
sqlmap源码大概思路是:
1. 首先将所有参数存入字典,以k=》v形式存储,并将k值是关键字的如from,改成from_KEYWORD。
2. while True语句,会将eval语句通过分号进行切分。
3. 不断执行每一句切分的语句。
4. 当某一语句执行失败时,如果是因为关键字问题导致的,将语句中所有关键字增加后缀_KEYWORD。
5. 重新执行此语句。
而i春秋API这个例子,可以产生程序异常的语句是:
'&from=' + from
sqlmap会将这句话直接转换,变成'&from_KEYWORD=' + from_KEYWORD,最终执行sqlmap -v4 输出详细信息,你会发现,所有get参数from都被莫名其妙地替换成了from_KEYWORD,当然无法对i春秋实施攻击了。
临时解决方案,将语句拆分成两个:
temp = '&from='
temp += from
目前,我通过github给sqlmap作者提交bug修复代码,作者还没同意合并,所以大家在使用时候注意一下。
最后,针对i春秋的测试,附上一点个人建议:
1. sig加密规则避免在js中出现,否则防护达不到预期效果。
2. 如果一定要在客户端加密,加密关键字不使用sig等明显单词,可以使用a、e等词,这种词在代码搜索时非常困难,直接提高了攻击者搜索的难度。
3. i春秋api接口的时间判断存在问题,如果选择一个非常大的时间,那么就永不失效。
4. 参数有做强制类型转换,如果传递的时间戳。
5. i春秋API接口支持JSONP,而JSONP在使用中,并没有对refer等做白名单限制。那么,作为攻击者,可以通过JSONP进行意想不到的攻击。
关于JSONP的威力,可以参考我之前写的一篇文章:
利用jsonp原理精准定位约炮需求用户
本文首发于i春秋,禁止转载,如需转载,请联系i春秋:
https://bbs.ichunqiu.com/thread-24918-1-1.html
pupil
shy总最近好高产啊~
ranshy
嗯,最近有写blog时间和心情,所以就多写点~