citrix 官方放了一个 CVE-2019-19781 – Verification Tool,是一个 Python 脚本,链接在 https://support.citrix.com/article/CTX269180。

为了方便大家看,我保存一个截图。

我看了之后觉得槽点满满,不过也符合我一贯的对安全人员代码水平的印象,下面先简单分析下这段代码。

代码分析

滥用 globals

这段代码中,很多 globals 的使用都是不必要的,这种会破坏代码的逻辑结构,而且可能会带来潜在的并发问题。正确的办法应该是在调用方接受函数返回值,然后继续传递给下一个函数。

可以使用原生代码的逻辑结果是调用命令

本漏洞是一个目录穿越,只要发送 ../ 这种的请求而且穿越成功即可,本来使用 Python urllib 两行的事情,这个人却使用了 curl 执行命令来检查返回值实现的,这种用法主要有以下缺点

 >>> subprocess.check_output("curl http://example.com; expr 1024 + 20480000", shell=False)
Traceback (most recent call last):
  ......
FileNotFoundError: [Errno 2] No such file or directory: 'curl http://example.com; expr 1024 + 20480000': 'curl http://example.com; expr 1024 + 20480000'


>>> subprocess.check_output("curl http://example.com; expr 1024 + 20480000", shell=True)
b'<!doctype html>\n<html>\n<head>\n    .....</html>\n20481024\n'

一个重大逻辑错误

如果说上面的问题不影响 poc 的效果的话,下面这个问题是真正的错误了。

if ("[global]") and ("encrypt passwords") and ("name resolve order") in str(response):
	pass

根据漏洞原理和代码写法猜测,原作者的意思是 response 中同时含有这三个字符串,但是这里的写法却错误的理解了 Python 的优先级。

这个代码等价于

True and True and ("name resolve order") in "name resolve order"

只要 response 含有最后一个字符串就会是 True,实际应该为

"str1" in response and "str2" in response

这种写法。

编码解码的问题

还是在这段代码

if ("[global]") and ("encrypt passwords") and ("name resolve order") in str(response):
    pass
elif NSIP_RESPONSE_MSG in str(response.decode("utf-8", errors="ignore")):
    pass

很多处没必要 bytes 转 string,直接去 bytes 匹配即可,毕竟匹配的都是英文单词,不涉及到解码的问题。

代码在转换 curl 输出为 string 的时候,上面就看到了两种写法。而查看 Python 的文档,str 的实现是 class str(object=b'', encoding='utf-8', errors='strict'),也就是说第二处 errors="ignore" 根本没有用处,如果真的发生错误,在 str 处就异常了。

其他的问题

看完代码之后,感觉就是这是一个安全人员写的代码,因为自己去检测这个漏洞只需要一个 curl 就够了,而讲这个命令转换为 Python 检测代码,这人的想法就是修修补补,比如发请求就去 subprocess 之前的 curl 就可以了,比如 shell 变量中使用 pipe 或者文件来存储中间结果,现在就直接全局变量。

xray 中自定义 poc

xray 面临的问题包括

所以我们需要一个”静态可拓展“的 poc 框架,让不太懂代码的安全人员也能方便上手来写 poc,而且尽可能的帮助他们避免踩坑,就如同 golang 和 Python 的对比,静态安全又不失灵活。

这个实现方式包括内置其他语言解释器(Python、Lua、JavaScript)、go plugin 等,但是这些门槛还是有些高,而且存在一些缺点,还有一种方法就是表达式技术,比如 sPEL、OGNL 等。

我们后续的设计就是基于 yaml 格式去写 poc,使用表达式去判断漏洞,这样可以兼顾静态和动态特性,下面是一个 demo。

name: poc-yaml-bash-cve-2014-6271
set:
  r1: randomInt(800000000, 1000000000)
  r2: randomInt(800000000, 1000000000)
rules:
  - method: GET
    headers:
      User-Agent: "() { :; }; echo; echo; /bin/bash -c 'expr {{r1}} + {{r2}}'"
    follow_redirects: false
    expression: response.body.bcontains(bytes(string(r1 + r2)))
detail:
  author: neal1991(https://github.com/neal1991)
  links:
    - https://github.com/opsxcq/exploit-CVE-2014-6271

expression 字段是一个表达式,除了上面的函数,还支持正则、字符串处理等函数,map、括号嵌套等更是基本语法层面的东西,并不是死板简单的正则匹配参数。我们使用的实现是 Google 的 cel 表达式,它是带有类型检查的,有错误根本通不过检查,不会到运行时才报错。同时我们给了 poclint 工具,类似 go fmt 帮助你格式化 poc 的代码和检查错误。

xray 中基于 go http 模块封装了一个 http client,实现了自动重试、代理、自定义 http 头、证书认证等功能,周边功能还包括参数解析和替换重组等等。说实话,我们在里面遇到了很多的坑,因为扫描器请求很多是畸形请求,而 go 的 http client 是很标准的,有些就需要自己去 hack 实现。

使用 xray 的 poc 不需要关心 http client 的细节,这都是底层架构上实现的,而很多安全的同学写的开源的扫描器全篇都是 requests.get(url) 这样使用非封装的 http client,根本无法实现全局的设置和统一的错误处理。

安全研发

如果一个人安全也懂一些,研发也懂一些,那就是符合安全开发这个岗位了,这个岗位在各大公司中主要是做扫描器引擎、WAF/IPS引擎、风控类、网络测绘、内部安全体系建设等等。

在长亭的这几年,后端和安全开发都做过一些,也面试过很多人,真正让人满意的安全研发是太太太稀缺了,很多安全比较厉害的人,研发就是上面的水平,很多研发还可以的人,是不怎么懂安全的。当然,我一直认为,一个研发大佬学习安全是没太大难度的,最主要的还是缺少安全大佬积累的奇技淫巧、对安全技术的热情等等,这些都阻碍着招聘的进度。一种解决方案是将安全研发拆分为安全研究和研发,安全研究团队负责研究技巧。给出思路和 demo 实现,然后由研发团队去进行产品化的实现。

长亭科技安全策略和安全研究一直都在招聘,可以点击下面的链接查看,如有意向,可以点击右上角 About 查看我的邮箱然后发送给我简历,这样和直接投递简历比较,好处是我可以帮你预先审核下简历,追踪面试进度,当然最重要的是我可以拿到内推奖金。