短信验证码暴力突破

考点:短信验证码爆破

首先查看提示:

image-20251103102157477

可以知道是不会真的发送验证码,需要进行前端代码审计来绕过

所以我们可以随便输个手机号试试就行

image-20251103102312921

这里点击发送后抓包

image-20251103102408694

然后输验证码再抓包

image-20251103102456879

为什么会是这个格式呢?我们看看前端代码

send部分:

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
function sendCode() {
const phone = document.getElementById('phone').value.trim();
if (!validatePhone(phone)) {
showMsg('请输入有效的手机号');
shakeInput('phone');
return;
}
showMsg('');
const btn = document.getElementById('sendCodeBtn');
btn.disabled = true;
btn.innerText = '发送中...';

fetch('/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone })
})
.then(response => response.json())
.then(data => {
if (data.success) {
showMsg('验证码已发送,请在输入框输入4位验证码');
startCountdown();
} else {
showMsg(data.msg || '发送失败');
btn.disabled = false;
btn.innerText = '发送';
}
})
.catch(() => {
showMsg('网络错误');
btn.disabled = false;
btn.innerText = '发送';
});
}

login部分:

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
function login() {
const phone = document.getElementById('phone').value.trim();
const code = document.getElementById('code').value.trim();
if (!validatePhone(phone)) {
showMsg('请输入有效的手机号');
shakeInput('phone');
return;
}
if (!/^\d{4}$/.test(code)) {
showMsg('请输入4位验证码');
shakeInput('code');
return;
}
showMsg('登录中...');
fetch('/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, code })
})
.then(response => response.json())
.then(data => {
if (data.success) {
showMsg('登录成功');
setTimeout(() => {
showMsg('');
}, 1500);
} else {
showMsg(data.msg || '登录失败');
}
})
.catch(() => {
showMsg('网络错误');
});
}

可以看到都是post请求相关接口,然后传入json格式的数据。所以就明白了传数据的方式

对code字段进行爆破,从0000-9999,最后爆破出来就能拿到flag

image-20251103102551483

验证码居然会出现这个地方?

考点:短信验证码泄露

image-20251103103407208

依然前端代码审计,找到验证码是六位数:

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
function login() {
const phone = document.getElementById('phone').value.trim();
const code = document.getElementById('code').value.trim();
if (!validatePhone(phone)) {
showMsg('请输入有效的手机号');
shakeInput('phone');
return;
}
if (!/^\d{6}$/.test(code)) {
showMsg('请输入6位验证码');
shakeInput('code');
return;
}
showMsg('登录中...');
fetch('/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, code })
})
.then(response => response.json())
.then(data => {
if (data.success) {
showMsg('登录成功');
// 弹出美化后的flag弹窗
customAlert(data.msg, "登录成功");
} else {
showMsg(data.msg || '登录失败');
}
})
.catch(() => {
showMsg('网络错误');
});
}

随便输入手机号,抓包,在响应包里看到code泄露了

image-20251103103851267

直接登录,拿到flag

短信轰炸-好玩但违法

考点:单手机号短信轰炸漏洞

image-20251103104138398

依然是熟悉的登录界面

这次先直接发送验证码,看到是4位数的验证码

image-20251103104543596

抓包也看不到code泄露,于是我们对/send接口进行测试

抓包进行重放,发现可以无限制地重发,于是我们直接对其进行多次重发,在某次响应包中拿到flag

image-20251103105120279

另一种短信轰炸

考点:多手机号短信轰炸

image-20251103105230707

老规矩,输手机号,发验证码,发现是4位数验证码

抓包,响应包没有验证码泄露

进行重放攻击,发现存在短信轰炸,但是看不到flag

我们进行重放时对手机号进行修改,修改后两位就行(遍历00-99),然后就能看到flag

什么你告诉我短信码没有什么用

考点:验证码可以删除绕过造成的任意用户注册漏洞

这次界面有点不同,可以输入密码,但是大差不差,密码先随便输

image-20251103110158386

老流程走一套

发验证码,是6位数的code

看响应包,没有code泄露

抓包进行重放,发现可以进行重放攻击,但是没有flag,两种短信轰炸都测试,还是没有

现在我们测试/login接口,将请求包的code删除,再发包就能登录,拿到flag