欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于约束的SQL攻击

程序员文章站 2022-05-26 08:57:09
...

首先需要注意的是,本文所述的是基于约束的SQL攻击而非SQL注入攻击

碰到这个问题,是在做CTF题时碰到的(题目网址:http://123.206.31.85:49163/) 

基于约束的SQL攻击

这一道题是要求我们以管理员admin的身份登陆系统方可查看flag,方法就是在注册时注册一个admin (admin后面带一个或几个空格),然后登录系统,系统会误认为是管理员登录了系统,所以赋予管理员权限。

那么,就算我们采用PHP中的PDO以及预查寻的方式(详见简单的PDO技术以及预处理方法预防SQL注入)来处理是否能预防基于约束的SQL攻击呢?这里给出靶场的源代码,环境使用的是Windows上的phpstudy(php-5.6.27+apache)

前端代码loginPDO.php

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>登录界面</title>
	</head>
	<body>
		<form action = "actionPDO.php" method = "post">
			<div id = "main" class = "main">
				<center>
					<h2>
						登录
					</h2>
					<p><lable>用户名称:</lable><input type="text" name="userName" placeholder="*************"></p>
					<p><lable>用户密码:</lable><input type="text" name="userPassword" placeholder="*************"></p>
				
					<input type="submit" name="submit" value="登录">
					<input type="reset" value="清空">
				</center>	
		</form>	

		<form action = "registerPDO.php" method = "post">
			<center>
				<h2>
					注册
				</h2>
					<p><lable>用户名称:</lable><input type="text" name="userName" placeholder="*************"></p>
					<p><lable>用户密码:</lable><input type="text" name="userPassword" placeholder="*************"></p>
					<input type="submit" name="submit" value="注册">
					<input type="reset" value="清空">
			</center>
		</form>
	</body>
</html>

注册模块代码registerPDO.php

<?php
	$name = $_POST['userName'];
	$password = $_POST['userPassword'];
	if($name == null || $password == null){
		header("location:loginPDO.php");
		return;
	}
	try {
	    //创建PDO对象
	    $pdo = new PDO("mysql:host=localhost;dbname=php10", "root", "root");
	}catch(PDOException $e) {
	    echo "数据库连接失败:".$e->getMessage();
	    exit;
	}
	$sql = "INSERT INTO loginPDO(userName,userPassword) VALUES (?,?)";
	//$sql = "INSERT INTO loginPDO(userName,userPassword) VALUES (:userName,:userPassword)";
	$stmt = $pdo->prepare($sql);
	$stmt->bindParam(1,$name);
	$stmt->bindParam(2,$password);
	//$stmt->bindParam(":userName",$name);		//绑定 一个 PHP 变量到预处理语句中对应的命名占位符或问号占位符
	//$stmt->bindParam(":userPassword",$password);
	$pdo->quote($name);
	$pdo->quote($password);
	$result = $stmt->execute();
		if($result){
			echo "<script>alert('注册成功')</script>";
			echo "<h2>注册成功!,即将跳转至登录页面...</h2>";
			header("refresh:3; url = //localhost/loginPDO.php");
		}else{
			echo "<script>alert('注册值错误!')</script>";
			echo "<h2>注册失败,用户名已经注册或注册值为空...</h2>";
		}
?>

 登陆模块actionPDO.php

<?php
	$name = $_POST['userName'];
	$password = $_POST['userPassword'];
    if($name == null || $password == null){
		header("location:loginPDO.php");
		return;
	}
	try {
	    //创建对象
	    $con = new PDO("mysql:host=localhost;dbname=php10", "root", "root");
	}catch(PDOException $e) {
	    echo "数据库连接失败:".$e->getMessage();
	    exit;
	}
	$sql = "SELECT * FROM loginPDO WHERE userName = :userName AND userPassword = :userPassword";
	//$sql = "SELECT * FROM login WHERE userName = ? AND userPassword = ?";
	$stmt = $con->prepare($sql);
	$stmt->bindParam(':userName',$name);    //bindParam方法与bindValue方法的区别在于bindParam的第二个参数可以传值用变量,而bindValue第二个参数只能传值用常量或字符串
	$stmt->bindParam(':userPassword',$password);
	//$stmt->bindParam('1',$name);
    //$stmt->bindParam('2',$password);
	$con->quote($name);          //quote方法是为普通的字符串添加引号
	$con->quote($password);
	$re=$stmt->execute();
	if($stmt->rowCount()!=0){
		echo "<script>alert('登录成功!')</script>";
		echo "<h2>欢迎您{$name}</h2>";
	}else{
		echo "<script>alert('登录失败!')</script>";
		echo "<h2>登录失败,3秒后自动跳转...</h2>";
		header("refresh:3; url = //localhost/loginPDO.php");
	}
?>

靶场效果如下

基于约束的SQL攻击

基于约束的SQL攻击

根据数据库中的初始数据,账号admin,密码root可以成功登录,注册不允许有相同的用户名。

首先我尝试了在admin后面加一个空格,密码不变,提示登录成功

基于约束的SQL攻击

基于约束的SQL攻击 

       然后尝试注册一个后面带空格的admin,结果提示注册失败,注册用户已存在,并不像刚开始所期望的那样。于是我猜测是否是因为bindParam()在绑定参数的时候去掉了末尾的空格,于是开始翻阅php手册,结果并没有发现手册中有提到bindParam()会去掉参数的首尾空格

       于是换一个方式,登录时在admin前面加一个空格,密码不变,点击登录提示登陆失败

基于约束的SQL攻击

 基于约束的SQL攻击

再注册一个admin前有一个空格的用户,结果发现注册成功

基于约束的SQL攻击

基于约束的SQL攻击 

基于约束的SQL攻击 

       那么猜测应该是成立的,bindParam()会截断绑定参数值后面的空格,而不会截断前面的空格,也就是允许注册用户名的第一个字符为空格

 

       也就是说,在一个有严格权限区分的系统,如果得知了管理员的用户名,可以利用管理员用户名后加空格的方法注册,再登录,系统也会默认为管理员,类似CTF的这道题一样,产生基于约束的SQL攻击

 

防御方法

1、使用过滤空格的函数,$data = trim($data),去掉传入参数的首位空格,再进行数据库查询、插入等工作

2、对用户名的长度进行限制

3、验证成功后返回的必须是用户传递进来的用户名,而不是从数据库取出的用户名。因为当我们以用户admin和密码root登陆时,其实数据库返回的是我们自己的用户信息,而我们的用户名其实是[admin      ],如果此后的业务逻辑以该用户名为准,那么就不能达到越权的目的了。

4、将要求或者预期具有唯一性的那些列加上UNIQUE约束

5、使用id作为数据库表的主键并设置自动递增,并且数据应该通过程序中的id进行跟踪

相关标签: 漏洞测试