登录页面的SQL注入漏洞
1、在Linux准备一套Xampp或者Phpstudy:模拟攻防2、在vscode安装Rremote Development插件,进行远程调试新添加主机在opt/lampp/security创建项目login.phpwelcome.php二、进行登陆的渗透测试注入类攻击的核心点:(1)、拼接为有效的代码或语句(2)、确保完成了闭合,并且可以改变原有执行逻辑通常并非处理一下就可以完成拼接和闭合,需要
一、环境准备
1、在Linux准备一套Xampp或者Phpstudy:模拟攻防
2、在vscode安装Rremote Development插件,进行远程调试
新添加主机
ssh root@192.168.74.132
编辑ssh的配置文件的路径
在opt/lampp/security创建项目
三个页面
login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js" charset="utf-8"></script>
<style>
*{
margin: 0;
padding: 0;
text-decoration: none;
font-family: montserrat;
box-sizing: border-box;
}
body{
min-height: 100vh;
background-image: linear-gradient(120deg, #3498db, #8e44ad);
}
.login-form{
width: 360px;
background: #f1f1f1;
height: 580px;
padding: 80px 40px;
border-radius: 10px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.login-form h1{
text-align: center;
margin-bottom: 60px;
}
.txtb{
border-bottom: 2px solid #adadad;
position: relative;
margin: 30px 0;
}
.txtb input{
font-size: 15px;
color: #333;
border: none;
width: 100%;
outline: none;
background: none;
padding: 0 5px;
height: 40px;
}
.txtb span::before{
content: attr(data-placeholder);
position: absolute;
top: 50%;
left: 5px;
color: #adadad;
transform: translateY(-50%);
z-index: -1;
transition: .5s;
}
.txtb span::after{
content: '';
position: absolute;
left: 0%;
top: 100%;
width: 0%;
height: 2px;
background: linear-gradient(120deg, #3498db, #8e44ad);
transition: .5s;
}
.focus + span::before{
top: -5px;
}
.focus + span::after{
width: 100%;
}
.logbtn{
display: block;
width: 100%;
height: 50px;
border: none;
background: linear-gradient(120deg, #3498db, #8e44ad, #3498db);
background-size: 200%;
color: #fff;
outline: none;
cursor: pointer;
transition: .5s;
}
.logbtn:hover{
background-position: right;
}
.bottom-text{
margin-top: 60px;
text-align: center;
font-size: 13px;
}
</style>
</head>
<body>
<form action="./login.php" class="login-form" method="post">
<h1>欢迎登陆</h1>
<div class="txtb">
用户名:<input type="text" name="username">
<span ></span>
</div>
<div class="txtb">
密码: <input type="password" name="password">
<span ></span>
</div>
<div class="txtb">
验证码:<input type="text" name="vcode">
<span ></span>
</div>
<input type="submit" class="logbtn" value="bt" name="bt">
</form>
<script type="text/javascript">
$(".txtb input").on("focus", function(){
$(this).addClass("focus");
});
$(".txtb input").on("blur", function(){
if($(this).val() == "")
$(this).removeClass("focus");
});
</script>
</body>
</html>
login.php
<?php
if(!empty($_POST['bt'])){
//获取用户提交的登陆信息
$username = $_POST["username"];
$password = $_POST["password"];
$vcode = $_POST["vcode"];
//万能验证码,存在安全漏洞,owasp-认证和授权失败
if ($vcode !== "0000"){
die("vcode-error");
}
$conn = new mysqli("127.0.0.1","root","","learn") or die("数据库连接不成功");
$conn -> query("set names utf8");
$conn -> set_charset("utf8");
$sql = "SELECT * FROM user where username ='$username' and password = '$password'";
$result = $conn -> query($sql);
//存在注入漏洞
if($result->num_rows == 1){
echo "login-pass";
echo "<script>location.href='./welcome.php'</script>";
}
else{
echo "login-fail";
echo "<script>location.href='./login.html'</script>";
}
# 关闭数据库
$conn -> close();
}
welcome.php
<?php
// 以下代码违背了owasp 失效访问控制
echo "欢迎登陆到安全测试平台";
二、进行登陆的渗透测试
在登陆页面输入一个单引号[']作为用户名,密码任意,验证码0000,响应如下
---------------------------------------------------------------------------------------------------
<br />
<b>Notice</b>: Trying to get property of non-object in <b>/opt/lampp/htdocs/security/login.php</b> on line <b>17</b><br />
login-fail<script>location.href='./login.html'</script>
----------------------------------------------------------------------------------------------------
以上信息出现MySQL的报错信息,上述报错信息存在两个可能漏洞
1、单一很高可以成功引起sql语句报错,说名后台没有对单引号进行处理
SELECT * FROM user where username ='$username' and password = '$password'
正常情况:SELECT * FROM user where username ='zhangsan' and password = 'zhangsan123'
攻击情况:SELECT * FROM user where username =''' and password = '123456'
username : x' or id =1#'
post正文:username=1' or 1=1 #'&password=111&vcode=0000&bt=bt
2、在报错语句中出现了敏感信息:
/opt/lampp/htdocs/security/login.php ,当前代码的绝对路径
注入类攻击的核心点:
(1)、拼接为有效的代码或语句
(2)、确保完成了闭合,并且可以改变原有执行逻辑
通常并非处理一下就可以完成拼接和闭合,需要不停的尝试,知道出现不一样的结果。这个尝试过程建议使用python+字典快速处理
上述代码一共存在6个漏洞
1、welcome.php页面谁都可以访问,没有进行登陆判断(中)
2、在登陆也买你输入 ' 作为用户名报错信息存在login.php的绝对路径,暴露了系统后台的敏感信息(低)
3、保存用户信息的数据表中,密码是明文保存,不够安全(中)
4、登陆页面可以进行SQL注入,进而轻易实现登陆(高)
5、login.php页面中使用了万能验证码(中)
6、登陆功能可以被爆破,没有进行爆破防护(中)
三、漏洞修复
一、使用python进行fuzz测试
# 利用python对php的登陆页面进行fuzz测试
import requests
def login_fuzz():
url = "http://192.168.74.132/security/login.php"
data = {"username": f"'", "password": f"{1}", "vcode": "0000", "bt": "bt"}
resp = requests.post(url, data)
if "Notice" or "error" or "Warning" in resp.text:
print("本登陆功能可能存在sql注入漏洞,可以试一试")
# 如果单引号存在嫌疑则继续利用
with open("../dict/sql.txt", "r", encoding="utf8") as file:
# 获取字典列表
payload_list = file.readlines()
file.close()
for payload in payload_list:
payload = payload.replace("\n","")
resp = requests.post(url, {"username": f"{payload}", "password": f"12312", "vcode": "0000", "bt": "bt"})
if "login-fail" not in resp.text:
print(f"测试payload成功:{payload}")
else:
print("通过测试,发现后台页面对单引号不敏感")
if __name__ == '__main__':
login_fuzz()
二、任意访问授权
//1、在comm.php中启用session
session_start();
//2、welcome.php导入
if( empty($_SESSION['islogin']) or $_SESSION["islogin"] != 'true'){
die("你还没有登陆,请先登陆"."<br>");
}
echo "欢迎登陆到安全测试平台"."<br>";
//3、在login.php中设置session
if($result->num_rows == 1){
echo "login-pass";
# 登陆成功后,记录session变量
$_SESSION['username'] = $username;
$_SESSION['islogin'] = 'true';
echo "<script>location.href='./welcome.php'</script>";
}
三、敏感信息暴露
//1、在login.php中如果sql语句执行失败
$sql = "SELECT * FROM user where username ='$username' and password = '$password'";
$result = $conn -> query($sql) or die("SQL语句执行失败");
四、数据库用户密码是明文存储
//1、注册时应该设置为md5,使用php内置的md5函数
//2、将数据库密码字段长度设置为32位及以上
$result = md5("password");
echo $result;
SQL注入的防御
登陆漏洞:登陆页面可以进行SQL注入,进而轻易实现登陆(高)
1、从代码和SQL语句的逻辑层面进行考虑,不能让密码对比失效
2、基于将用户输入的引号(单引号和双引号)继续转义处理的前提,可以使用PHP内置函数addslashes进行强制转义
3、另外一种方法:使用mysqli预处理的功能,也可以进行防御
一、登陆SQL语句的逻辑问题
// sql存在逻辑问题,用户名和密码不应该放在同一条sql中
//应该先通过查询user表,找到一条记录(用户名唯一),再进行密码的单独对比
// $sql = "SELECT * FROM user where username ='$username' and password = '$password'";
$sql = "SELECT * FROM user where username ='$username'";
$result = $conn -> query($sql) or die("SQL语句执行失败");
// 如果用户名真实存在,刚好找到一条,则单独再继续密码的比,即使出现SQL注入漏洞,但是只要密码正确,也无法登录
if($result->num_rows == 1){
$row = $result->fetch_assoc();
if($password == $row['password']){
echo "login-pass";
# 登陆成功后,记录session变量
$_SESSION['username'] = $username;
$_SESSION['islogin'] = 'true';
echo "<script>location.href='./welcome.php'</script>";
}
else{
// echo "pass-error"; // 不建议直接明确告诉用户,是密码还是用户名错误
echo "login-fail";
echo "<script>location.href='./login.html'</script>";
}
}
else{
echo "login-fail";
echo "<script>location.href='./login.html'</script>";
}
二、使用addslashes函数
可以将字符串的单引号双引号,反斜杠,NULL值自动添加转义符,从而方式SQL注入中对单引号和双引号的预防
原始SQL语句如下:
$sql = "SELECT * FROM user where username ='$username'" and 'password' =$password;
如果用户输入 1' or 1=1 #',则SQL语句变成:
$sql = "SELECT * FROM user where username ='1' or 1=1 #''" and 'password' =$password;
如果使用addslashes函数,则变成
$sql = "SELECT * FROM user where username ='1\' or 1=1 #\''" and 'password' =$password;
逻辑上保持不变
$username = addslashes($_POST["username"]);
三、Mysqli面向对象的预处理
预处理的过程:先交给MYSQL数据库进行SQL语句的准备,准备好后再将SQL语句中的参数进行值的替换,引号会进行转义处理,将所有参数变成普通字符串,再进行第二次的正式的SQL语句执行。MYSQLi的预处理既支持面向对象,也支持面向过程
//mysqli预处理
function mysqli_prepare_oop(){
$conn = new mysqli("127.0.0.1","root","","learn") or die("数据库连接不成功");
$conn -> set_charset("utf8");
// ?在预处理语句中表示参数
$sql = "select * from user where id = ?";
// 实例化预处理对象
$stmt = $conn ->prepare($sql);
//实例化后需要将参数进行绑定,并在执行时进行替换,bind_param第一个参数位数据类型:i整数,s字符串,d小数,b二进制
$stmt -> bind_param("i", $userid);
$userid = 1;
//正式执行sql语句,如果是更新类的操作,update,delete,insert,执行后不做其他操作没问题
// $stmt->execute(); //execute方法返回不二类型,返回执行成功还是失败
//如果针对 查询语句,单纯只是执行无法取得查询结果的,需要进行结果绑定
$stmt->bind_result($id,$username,$password);
$stmt->execute();
// 调用结果并进行处理
$stmt ->store_result();
// 输出行数
echo $stmt->num_rows()."<br>";
//遍历结果
while ($stmt->fetch()){
echo $userid." ".$username." ".$password."<br>";
}
}
2、使用预处理防止SQL注入
$sql = "SELECT * FROM user where username =?";
// $result = $conn -> query($sql) or die("SQL语句执行失败");
$stmt = $conn->prepare($sql);
$stmt->bind_param('i',$username); //绑定查询参数
$stmt->bind_result($id,$username2,$password2); //绑定结果参数
$stmt->execute();
$stmt->store_result();
if($stmt->num_rows() == 1){
$row = $stmt->fetch();
if($password == $password2){
echo "login-pass";
# 登陆成功后,记录session变量
$_SESSION['username'] = $username;
$_SESSION['islogin'] = 'true';
echo "<script>location.href='./welcome.php'</script>";
}
else{
// echo "pass-error"; // 不建议直接明确告诉用户,是密码还是用户名错误
echo "login-fail";
echo "<script>location.href='./login.html'</script>";
}
}
else{
echo "login-fail";
echo "<script>location.href='./login.html'</script>";
}
更多推荐
所有评论(0)