一、审题
这关是一个用户登录界面,还有忘记密码和注册新用户操作,和正常网站很相似了。本关考察内容是二次注入。
二、思考
简单讲解一下什么是二次注入,就是需要进行两次操作。第一次构建的恶意语句被写入数据库中保存,并没有触发。第二次执行SQL语句时触发了第一次的恶意语句,产生攻击效果。
sql
<?php
// 包含数据库连接文件
include("../sql-connections/sql-connect.php");
// 检查是否通过POST方法提交了表单
if (isset($_POST['submit'])) {
// 从POST请求中获取用户名,并使用mysql_escape_string进行转义(防止SQL注入)
$username = mysql_escape_string($_POST['username']);
// 获取密码和确认密码,并进行转义
$pass = mysql_escape_string($_POST['password']);
$re_pass = mysql_escape_string($_POST['re_password']);
// 设置输出文本的字体大小和颜色(黄色)
echo "<font size='3' color='#FFFF00'>";
// 构建SQL查询:检查数据库中是否已存在该用户名
$sql = "select count(*) from users where username='$username'";
// 执行SQL查询,如果失败则输出错误信息并终止脚本
$res = mysql_query($sql) or die('You tried to be smart, Try harder!!!! :( ');
// 获取查询结果的第一行数据
$row = mysql_fetch_row($res);
// 检查用户名是否已存在(如果查询结果不为0,表示已存在)
if (!$row[0] == 0) {
// 使用JavaScript弹出提示框,告知用户用户名已存在
?>
<script>alert("The username Already exists, Please choose a different username ")</script>;
<?php
// 1秒后重定向到注册页面
header('refresh:1, url=new_user.php');
} else {
// 用户名不存在,继续注册流程
if ($pass == $re_pass) {
// 密码和确认密码匹配,构建插入用户数据的SQL查询
$sql = "insert into users ( username, password) values(\"$username\", \"$pass\")";
// 执行插入查询,如果失败则输出错误信息
mysql_query($sql) or die('Error Creating your user account, : '.mysql_error());
// 输出注册成功的提示信息和图片
echo "</br>";
echo "<center><img src=../images/Less-24-user-created.jpg><font size='3' color='#FFFF00'>";
echo "</br>";
echo "</br>";
echo "</br>";
echo "</br>Redirecting you to login page in 5 sec................";
echo "<font size='2'>";
echo "</br>If it does not redirect, click the home button on top right</center>";
// 5秒后重定向到登录页面
header('refresh:5, url=index.php');
} else {
// 密码和确认密码不匹配,提示用户
?>
<script>alert('Please make sure that password field and retype password match correctly')</script>
<?php
// 1秒后重定向到注册页面
header('refresh:1, url=new_user.php');
}
}
}
?>
这是本关注册用户名的源代码, 为了防止sql注入,对获取的用户名和密码进行了转义。mysql_escape_string() 是 PHP 语言里用于处理 SQL 查询的一个函数,其主要功能是对字符串中的特殊字符进行转义操作,不过在 PHP 5.5.0 版本之后已被弃用。
比如注册的用户名是admin'#,函数会将单引号转义为反斜杠单引号,变成username =admin\\'#这样。但是在插入数据这行代码中,变量名username使用了双引号包裹,而双引号会解析转义字符\',将它还原为单引号,导致最终存储到数据库中的用户名还是admin'#。
sql
假设用户输入:
username = admin'#
password = 123456
转义后的值:
$username = "admin\'#"; // mysql_escape_string() 的结果
$pass = "123456";
SQL 拼接结果:
INSERT INTO users (username, password) VALUES("admin'#", "123456")
再看一下修改密码的源代码中核心的一句。
sql
//通过匹配用户名和原密码来更新密码;
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
攻击者第一次创建的admin'#用户名被存储到数据库中,当攻击者进行第二次操作,也就是修改密码时,会发生什么?
sql
//修改admin"#用户的密码时的语句;
$sql = "UPDATE users SET PASSWORD='123456' where username='admin'#' and password='$curr_pass' ";
但实际上(#' and password='$curr_pass' )被注释掉,语句成为:
sql
UPDATE users SET PASSWORD='123456' where username='admin'
结果导致admin管理员用户的密码被修改为123456。
这就是二次注入产生的效果。
三、做法
1.第一次注入恶意语句,可以看到注册成功,而此时管理员admin的密码是admin;
2.二次注入,进行修改密码操作;
3.显示密码修改成功,查看一下结果,发现实际上是admin用户的密码被修改为123456;
