今天来分析一下还热乎的,TF的代码注入漏洞。出问题的是TF的saved_model_cli程序。
saved_model_cli 主要功能是用来保存模型,这个程序在安装TF的时候就默认自带了,不知道有多少人用过它。
下面是它run的用法:
(base) [~] saved_model_cli run -h 21:24:46
usage: saved_model_cli run [-h] --dir DIR --tag_set TAG_SET --signature_def
SIGNATURE_DEF_KEY [--inputs INPUTS]
[--input_exprs INPUT_EXPRS]
[--input_examples INPUT_EXAMPLES] [--outdir OUTDIR]
[--overwrite] [--tf_debug] [--worker WORKER]
[--init_tpu]
Usage example:
To run input tensors from files through a MetaGraphDef and save the output tensors to files:
$saved_model_cli show --dir /tmp/saved_model --tag_set serve \
--signature_def serving_default \
--inputs input1_key=/tmp/124.npz[x],input2_key=/tmp/123.npy \
--input_exprs 'input3_key=np.ones(2)' \
--input_examples 'input4_key=[{"id":[26],"weights":[0.5, 0.5]}]' \
--outdir=/out
可以看到,里面有一个--input_examples的参数,它接受一个字典作为值。
从源码中,我们去看下是怎么处理这个输入参数值的。
def preprocess_input_examples_arg_string(input_examples_str):
input_dict = preprocess_input_exprs_arg_string(input_examples_str)
...
在preprocess_input_exprs_arg_string这个函数中,我们发现了一个很敏感的函数eval。
def preprocess_input_exprs_arg_string(input_exprs_str):
input_dict = {}
for input_raw in filter(bool, input_exprs_str.split(';')):
...
input_key, expr = input_raw.split('=', 1)
# ast.literal_eval does not work with numpy expressions
input_dict[input_key] = eval(expr) # pylint: disable=eval-used
return input_dict
eval的存在,直接让恶意用户通过控制输入来让eval执行任意字符串,导致了代码注入。
这个漏洞在TensorFlow 2.7.0版本被修复,CVE号为CVE-2021-41228。
下图为修复的方法,现在使用了literal_eval来判断是否为合法的类型。

以前写过Python语言安全问题,这篇文章里面介绍的第一个敏感函数就是eval。
那为什么这么牛逼的Google项目还会犯这么低级错误呢?你注意到eval函数上面的注释了吗?如果你要挖这种漏洞,会用什么样的思路,欢迎留言讨论。
参考链接:
1.https://jfrog.com/blog/tensorflow-python-code-injection-more-eval-woes/
2.https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-41228
3.https://github.com/tensorflow/tensorflow/security/advisories/GHSA-3rcw-9p9x-582v
4.https://github.com/tensorflow/tensorflow/commit/8b202f08d52e8206af2bdb2112a62fafbc546ec7