Less-24
前言
根据题目
Second Order injections
Real treat
Stored injections
- 1
- 2
- 3
我们晓得本次的知识点是二次注入。
那么再闯关之前,我们需要先了解一下二次注入。
1. 二次注入的原理
1.1 个人理解
二次注入(存储型注入),这种手法所利用的原理是:在网站处理用户提交的数据的时候,只是将某些敏感字符进行了转义。因而使得用户第一次提交的时候不会被当做代码执行。但是这些数据存入数据库的时候却没有转义,而网站程序默认数据库中的数据都是安全的,当网站程序第二次调用刚才存储的脏数据的时候,则不会转义使用而是直接使用,因此就会达到注入的效果。
注意两点:
- 网站会将数据原封不动的存入数据库中。
- 网站会直接调用数据库中的数据而不会对其进行检测等操作。(信任数据库中的数据)
1.2 Example
举一个例子:
某个网站,在处理注册登录的时候,对输入的用户名中的敏感字符执行转义操作,进行完对应的SQL查询后再将数据存入数据库中。比如输入了admin' #
,网站则会将'
和#
转义后进行SQL查询,但是最后存入数据库中的结果仍然是admin' #
(并没有转义存储)。
这样一来,由于第一次存在转义的机制,我们无法通过第一次SQL的构造就实现注入,但是此时目标数据库中已经存在了SQL注入的语句,我们只要想办法让网站自己去调用这条数据,那么就会造成二次注入。
假设成功登录后,在修改密码时网站会自动从数据库中提取你的用户名,由于网站默认数据库中的数据都是安全的,因此当提取数据库中的用户名admin' #
的时候,并不会进行转义操作,而是直接拼接到SQL语句中执行。假设执行如下语句:
UPDATE users SET PASSWORD='$pass' WHERE username='$username' and password='$curr_pass'
- 1
这样,当我们修改密码的时候,网站则会直接提取数据库中的admin' #
拼接到SQL中执行。
就会变成
UPDATE users SET PASSWORD='$pass' WHERE username='admin' #' and password='$curr_pass'
- 1
那么真正奏效的语句则是
UPDATE users SET PASSWORD='$pass' WHERE username='admin'
- 1
此时我们的用户是admin' #
但是却成功的修改了admin
账户的密码。这样我们就可以登录admin
账户。
2. 题目分析
本次题目的页面比较多,通过分析我们列举如下(这里将可能存在注入点的页面加粗):
- 主页(登录页面): 可输入
login_user
和login_password
。 - 忘记密码界面:图片一张。
- 用户注册界面:可输入
username
,password
,re_password
三个字段。 - 登录界面:可修改密码。
- 登录失败:图片一张。
通过上述分析,发现可能存在注入点的界面有三个
- 主页
- 注册页
- 登录成功页(修改密码)
2.1 主页
看一下主页中登录功能的相关代码
$username = mysql_real_escape_string($_POST["login_user"]);
$password = mysql_real_escape_string($_POST["login_password"]);
$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
- 1
- 2
- 3
- 4
在这里可以看到在登陆的时候网站会接收用户输入的账号和密码,然后调用mysql_real_excape_string()
处理。
而mysql_real_excape_string()
的作用就是转义字符串中的特殊字符:
- \x00
- \n
- \r
- \
- ’
- "
- \x1a
这样我们如果输入一个admin' #
那么执行的时候则是select * from users where username ='admin\' #'
。转义会将单引号当做内容进行查询而使得闭合作用失效。因此在这个页面我们无法注入。
2.2 注册页面
我们来看一下注册页面相关的代码
$username= mysql_escape_string($_POST['username']) ;
$pass= mysql_escape_string($_POST['password']);
$re_pass= mysql_escape_string($_POST['re_password']);
$sql = "insert into users ( username, password) values(\"$username\", \"$pass\")";
- 1
- 2
- 3
- 4
- 5
这里将和数据库操作有关的放在上面。
在注册这里,首先是利用mysql_escape_string
对用户输入的数据进行转义。
这里我们输入一个test' #
那么后台获取到的$username
就是test\' #
,然后同样处理两次输入的密码并核对,最后通过insert
将账号和对应的密码插入表中。
而最终的插入SQL的语句则变成了:
insert into users ( username, password) values("test\' #", "123456");
- 1
在SQL执行的过程中,单个转义符号是透明的(相当于告诉SQL我后面的单引号是一个插入的值而不是字符串的闭合标志),也就是说最终存储在
数据库中的数据是test' #
。这就对应了第一点网站会将数据原封不动的存入数据库中。
综上所述,此页面是存在二次注入漏洞的。我们可以利用这个漏洞构造SQL注入语句然后存储于服务器数据库中。当服务器上的程序二次调用此数据的时候,就可能出现问题。
2.3 登录成功界面(修改密码)
看一下相关代码
$username= $_SESSION["username"];
$curr_pass= mysql_real_escape_string($_POST['current_password']);
$pass= mysql_real_escape_string($_POST['password']);
$re_pass= mysql_real_escape_string($_POST['re_password']);
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
- 1
- 2
- 3
- 4
- 5
- 6
这里由于登录成功,因此程序直接从后台数据库中获取账户信息。那么获取到的数据就是刚才我们精心制作的“间谍”了。然后直接将这个username
拼接到SQL中去执行。我们人工复现一下这个过程。
首先获取$username="test' #"
,然后执行SQL
$sql = "UPDATE users SET PASSWORD='$pass' where username='test' #' and password='$curr_pass' ";
- 1
这样一来,#
后面的语句都被注释掉了。那么这条语句改的则是test
账户的密码而不是test' #
的密码。
这就对应了第二点:网站会直接调用数据库中的数据而不会对其进行检测等操作。
综上所述:我们利用注册界面先将被污染的数据存入数据库中,然后利用登录成功后的修改密码功能二次利用污染数据,造成最终的SQL注入。
3. 注入过程
如果你认真的看完了上面的思路整理。相信你一定可以自己实现后面的注入了。
首先看一下数据库中原本的用户信息:
这里注意一下admin
账户的密码是admin'
然后,首先我们进入用户注册界面,构造一个可以注入的用户名:
用户名:admin' #
密码: 123456
注册后再看一下数据库
可以看到刚才注册的数据已经进去了,并且是原封不动的存入数据库了。
那么此时我们登进去看一下
可以看到我们的用户名是admin' #
,然后我们将密码改成654321
。
再次观察数据表,就可以发现,最终是admin
账户的密码改为654321
而我们自己创建的admin' #
的密码没有改变。原理上面已经说的很清楚了。
那么我们来登录一下admin
账户试一下
成功了。