浅谈公众短信接口安全

/文 空虚浪子心

网站给用户发送手机短信,已经是一种流行元素,几乎各大门户网站都有给用户发短信的功能。网站发送短信一般有主动和被动两种方式。主动方式,是让用户填写自己的手机号,之后发送。被动的方式,是用户发短信给某个网站提供的号码,之后网站回复。
朋友以前做过短信服务,短信接口毕竟不是免费的,虽然成本很低,但是大量的短信,也会让有些人心疼一阵。为了节约成本,为了防止用户发恶意内容的短信(欺诈、不“绿坝”等),网站都会采取加入验证码、不允许用户控制短信内容等防御。但是总会有人疏漏很多。

抓个典型:
某大型门户网站有个功能,发送“手机游戏”的下载地址给用户的手机。
 image001.jpg
看看给网页提交了什么内容

SHELL代码
  1. POST http://mobile. ****.com.cn/sa/appdown.php HTTP/1.0   
  2. User-Agent: Opera/9.64 (Windows NT 5.1; U; Edition IBIS; zh-cn) Presto/2.1.1 Paros/3.2.13   
  3. Host: mobile. ****.com.cn   
  4. Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1   
  5. Accept-Language: zh-CN,zh;q=0.9,en;q=0.8   
  6. Accept-Charset: iso-8859-1, utf-8, utf-16, *;q=0.1   
  7. Accept-Encoding: identity, *;q=0  
  8. Referer: http://mobile. ****.com.cn/sa/game_content.html?f=12053  
  9. Cookie: ****_NEWS_CUSTOMIZE_city=% DDE; ****GLOBAL=27.6401122; ULV=124618208vjuids=-1de8654069555; vjlast=1246458586; VISITED_TEL_RBT_****=13333333333; VISITED_RID_RBT_****=11; user_mobile=13333333333user_aid_27=114Apache=12467562.6912   
  10. Cookie2: $Version=1  
  11. Proxy-Connection: Keep-Alive   
  12. Content-Length: 141   
  13. Content-Type: application/x-www-form-urlencoded   
  14.   
  15. model=MOT-L6smodel=71phonenum=13333333333game_num=102177game_id=837game_name= %c8%fd%b9%fa%c3%cd%bd%ab%bb%aa%d3%e9%b0%e6x=47y=10  

发送后,手机就会受到以下内容
三国猛将华娱版
http://XXXXXX(一个URL)

再次把这个包提交,又收到一条短信,再次最后次提交,还有。
这个功能并没有限制发送次数
从短信内容上看,“三国猛将华娱版”,这个字符经过URLencode后,是http包中的“game_name”的值。改动包中的值,继续发送,收到的短信中,就会相应的改动。
既然我们可以影响短信中的部分内容,就可以给用户传递任何消息了,这里发送的还是个彩信。
这个功能,存在不限制发送次数漏洞,和部分内容可自定义漏洞。
我们可以做个自定义内容的短信炸弹。
 image003.jpg
工具危害巨大,自己研究用,恕不提供。
看看收到的短信:
 image004.jpg
如果用这个平台做个短信欺诈,不知道会有什么后果?最近好像很流行这个,可以发短信“您中奖了,我们是****短信平台,请汇款到XXX。汇款前,请先到****网站确认短信号码是否一致,以免上当受骗”。这毕竟是门户网站的短信平台,即使无所谓短信费用金钱损失,也要考虑用户印象。
目前该漏洞已经修补。

再抓个典型:
GOOGLE的天气预告短信提醒订阅功能。会主动发短信验证码给用户,用户在输入验证码,就可以订阅天气预告了。
 image005.jpg
抓个包看看:
第一次发包请求(如果包中没有COOKIE,就会返回setcookie的包)。

SHELL代码
  1. GET http://www.google.com/sms/alerts/register HTTP/1.1   
  2. Accept: */*   
  3. Accept-Language: zh-cn   
  4. Referer: http://www.google.com/sms/alerts   
  5. User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022) Paros/3.2.13   
  6. Host: www.google.com   
  7. Proxy-Connection: Keep-Alive  

先请求验证码。
返回一个JSON的数组,内容中有个ID,并setcookie。

SHELL代码
  1. HTTP/1.1 200 OK   
  2. Content-Type: text/html; charset=UTF-8   
  3. Date: Thu, 02 Jul 2009 03:06:44 GMT   
  4. Pragma: no-cache   
  5. Expires: Fri, 01 Jan 1990 00:00:00 GMT   
  6. Cache-Control: no-cache, must-revalidate   
  7. Set-Cookie: PREF=ID=745e252e402062d3:TM=1246504004:LM=1246504004:S=ZNBHGP6FuQzKLL_Fexpires=Sat, 02-Jul-2011 03:06:44 GMT; path=/; domain=.google.com   
  8. Server: Google Alerts SMS Frontend   
  9.   
  10. {"error_msg": "",   
  11.  "captchaid": "AEs32l-Z7ZJGkIqHzPxjGIw8xRUMPD259ZjV5Sd6tRFfXweYubVSP0ZKHbIf7EMwcvdQGADKeLNlrmZoIuVSP-0CmHe72qqYKw"   
  12. }   

这个ID会作为发送短信时的参数,和验证码以及COOKIE对应起来,提交给发送短信的页面,就会通过验证码的验证。
第二次发包请求

SHELL代码
  1. POST http://www.google.com/sms/alerts/sendverification HTTP/1.1   
  2. Accept: */*   
  3. Accept-Language: zh-cn   
  4. Referer: http://www.google.com/sms/alerts   
  5. Content-Type: application/x-www-form-urlencoded;charset=utf-8   
  6. User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022) Paros/3.2.13   
  7. Host: www.google.com   
  8. Content-Length: 154   
  9. Proxy-Connection: Keep-Alive   
  10. Pragma: no-cache   
  11. Cookie: PREF=ID=745e252e402062d3:TM=1246504004:LM=1246504004:S=ZNBHGP6FuQzKLL_Fexpires=Sat, 02-Jul-2011 03:06:44 GMT; path=/; domain=.google.com   
  12.   
  13. phone_number=13333333333captcha_idAEs32l-Z7ZJGkIqHzPxjGIw8xRUMPD259ZjV5Sd6tRFfXweYubVSP0ZKHbIf7EMwcvdQGADKeLNlrmZoIuVSP-0CmHe72qqYKw captcha_answer=4460  
  14.   

captcha_answer这个验证码的值,和captcha_id是一一对应的。
两次发包请求,保证了验证码的验证功能,以免被恶意用户利用,发送垃圾短信炸弹。GOOGLE认为,用户都是使用浏览器上网的,所以每次打开短信发送页面,都会刷新cookie对应的验证码,就可以让用户每次都输入验证码,才会发短信:
第一步:打开页面,页面自动完成下面第二步。
第二步:发送注册captchaid的请求。服务器返回一个captchaid。
第三步:使用这个captchaid,请求验证码,服务器刷新当前COOKIE对应的验证码。并返回给用户验证码图片。
第四步:用户输入验证码,手机号,发送短信。页面会把用户输入信息,和captchaid一次发送。服务器收到这些信息,就会发一次短信。
用户再次发开页面,又会生成新的captchaid,cookie对应的captchaid会被替换。
关键问题有以下几点:
1,Captchaid和验证码是一一对应的。
2,获取验证码图片时,需要带上Captchaid。
3,用户再次请求,生成新的captchaid。
问题是,验证码真的用一次就不能用了吗?也就是说,如果用户不再次打开页面,cookie对应的captchaid会被清理掉么?
我们再次提交最后一个包,返回正常,收到短信,再次请求,又正常,收到短信。
经过多次发包验证,分别取消第二个包中的字段后,得出证明,如果用户不主动请求验证码,cookie对应的验证码不变!那么我们就可以做一个工具,先让请求验证码,让用户输入验证码(第二个包中,是需要验证码的),之后就可以疯狂的发送短信了。
工具内部的原理:
1, 先请求captchaid
2, 使用captchaid去请求验证码,就可以得到并生成“可以无限发送的那个包”需要的cookie,referer,captcha_id等信息。
3, 最后根据用户定义的手机号和发送次数进行轰炸。
 image006.jpg
工具危害巨大,自己研究用,恕不提供。
GOOGLE的这个功能,可以用来发送短信炸弹,填满你的收件箱。
漏洞已经两次发给google安全团队,但是收到自动回复后,一个月了,迟迟不见动静,也许他们不认为这是个漏洞。
最后一个典型:
Qq.com,这是个正面的典型。
QQ有很多很多短信相关业务,看看他们是怎么运作的。
 image007.jpg
这幅图已经可以说明所有问题了,腾讯几乎所有的短信接口,都在采用这种被动的方式。用户不能主动给自己发短信,只能使用手机给腾讯发短信,等候回复。当然,如果让腾讯的这个短信功能和google或者其他短信炸弹联动起来,不知道会发生什么美妙的事情。偶只是猜测,不知道是否有DDOS的可能性存在。
被动模式,除非用户可以伪造短信的发送方,否则是很难出现短信安全问题的。当然,如果用户群体不多,用户不怎么喜欢,看到这个功能后,可能会嫌麻烦,不如主动方式简单。(QQ用户多的恐怖啊)

如何防御:
对于广泛的,公用的,暴露在网站上给任何人免费使用的短信接口,应该有一套详细的安全方案,本文只是纸上谈兵一下。
最好的方式,是让所有的服务都采取被动发送,只有用户发短信来请求了,才会给他发回复短信。
然而,我们不能总是否定需求,如果一定要采用主动地方式,那至少应该做到以下几点:
1, 限制每天发送给某个号码的次数(YAHOO就是这么做的),这一点,只能在最外层应用上做,不能直接在接口处HOOK,可能多个应用同时使用一个短信接口。
2, 禁止从客户端提交短信发送的内容或部分内容,所有的发出短信内容都不应该有用户参与。
3, 每次发送需要输入验证码,一定要用一次,清除一次。
4, 在web应用层,记录详细日志。
综合以上几点,可以尽量少的避免短信接口攻击。随着业务的复杂性,可能有一些覆盖不到的地方,就只能根据业务具体定制方案了。

发表评论?

4 条评论。

  1. 不错不错,挺好的。其实google这个问题,他们的思路我是明白的,他们想实现一个完全独立的验证码系统,让验证码和cookie中加密后的内容实现一个自检验的包,也就是说得到了post后无需再和水印系统交互即可检测验证码是否正确。

    和我以前设计的思路接近,不过遗憾的是,他们出现了逻辑问题。当然,我那个和session有关,如果纯cookie的,只能使用时间戳解决点一部分问题,但是依旧不够好。

  2. 很老的东西了。。google一搜手机炸弹一大片。。

  3. 其实google的这个例子,问题还是出在他们的验证码验证方式上啊。

  4. 当年玩了一把移动 12530 现在他妈的验证码(坑爹)还是假货。。。限次成5条了。

发表评论