为了直观体现代码审计的思想,对漏洞场景进行了简化。1.不一致的安全标准。一种新的编程语言,作为后端处理程序,必须要和中间件/数据库等其他模块对接。如果他们以不同的安全标准对待请求,可能会导致安全问题。下面我们用一些已知语言的例子来证明这一点。案例1WSGI与中间件不一致WSGI作为连接中间件和应用程序的桥梁,而这种全新的编程语言作为应用程序也会在这个环节存在安全问题。当WSGI和中间件具有重叠的权限,或者WSGI和应用程序具有重叠的控制区域时,就会出现问题。以nginx+gunicorn为例。Gunicorn是中间件和python之间的桥梁。就是图中的一种WSGI,也可以处理http请求。如果中间件是nginx,它和gunicorn都有检查http请求的权限,这时候可能会出问题。python部分和nginx部分在这一点上,nginx对待请求的方式与gunciron不同。构造/privateHTTP/1.1/../../public。nginx会解析../返回父目录,认为请求是访问/public,安全的传递给gunicron,但是gunicorn不会这样解析,而是认为发送了两个包,分别是解析为访问/private和访问/public。这绕过了安全检查。案例2数据类型安全标准不一致这种新的编程语言必须有多种数据类型以满足不同的需求,例如列表、数组等。这就是不一致的安全标准可能导致问题的地方。no-sql一度以为自己无法注入,最后败给了这个。以mongodb+js为例。mongodb舍弃了sql语句,标准的写法不使用拼接调用执行。即使有安全规范,与php结合也容易出现问题。mongdb部分的js部分不能在这里拼接,string为字符串。但是借助js和php可以传入数组参数,构造/login?username=admin&password['$ne']=1可以让Mongdb解析为db.Users.find({username:'管理员',密码:{'$ne':'1'}});其中$ne是mongodb的运算符,表示不相等,此时的语义make如果admin密码不为1,则可以登录成功。案例3多重注入防御机制不一致。这种新的编程语言通常需要在不同场景下进行输入/输出。在html中输出可能会导致xss注入,在mysql中输出可能会导致sql注入。我们可以采用一些安全措施来限制它们的产生,但是当这两种防御机制不兼容时就会出现问题。以xss注入防御+sql注入防御为例。Xss防御部分:删除所有标签SQL防御部分:删除黑名单关键字整体效果可以通过在关键字中插入标签来绕过。其次,代码和数据可以转换成新的编程语言。为了方便使用,往往需要将一些代码转换成数据,或者将一些数据转换成代码,这可能会导致安全问题。下面我们将通过几个案例来论证这一点,案例1不安全的模板渲染模板渲染是编程语言的一个共同特征,有时会存在一些安全问题。前端部分对应代码模板部分。我们可以看到开发者在安全限制方面下了很大功夫,尽可能的避免漏洞。每个变量的限制都是为了避免漏洞,但是漏洞还是会出现。这是因为这仍然没有完全分离数据和代码,导致安全问题。我们可以在user部分输入)/*,然后在punc部分输入*/。任何没有字母数字的shell?>让punc从数据变成代码,跳出安全限制,顺利getshell。要知道开发者已经尽了最大的努力来做安全限制了,但是还是坏掉了。错误的渲染方式可能会导致数据和代码没有严格分离,从而产生漏洞。Case2跨语言数据传输这种新的编程语言有时需要与其他语言的脚本交互,传输数据时可能会用到标记语言,如xml、json、yaml等。或者使用配置文件来存储一些关键常量。这有时会产生安全问题。yaml是一种标记语言,可以存储数组、对象和列表等各种数据类型,用于编写配置文件或跨语言传输数据。以yaml反序列化漏洞为例。python的部分功能是为在线解压的压缩包写一个配置文件yaml部分。当我们通过一定的方式覆盖yaml文件,替换为如下内容时,就会形成一个反弹壳。3.可预见的安全处理方式一种新的编程语言必然会有一些逻辑代码来提高安全性。当我们不选择拒绝非法输入而是安全地处理非法输入时,可能会导致安全问题。Case1人性化的输入修正有时我们会善意修正输入者可能错误的输入形式,这可能会为攻击者提供方便。以CVE-2022-30333为例。在低于6.12的unRAR版本中,存在人性化修正输入造成的漏洞。简单的说,我们输入解压后的文件路径即可。开发商在这里下了很大的功夫。安全限制,尝试目录遍历的../等操作将被认为是危险的。然而,漏洞仍然存在。DosSlashToUnix()函数将\(反斜杠)转换为/(正斜杠)是出于人性化的考虑,这样..\就可以变成../来绕过安全检查,造成目录遍历。最终效果是任何文件都可以写入任何目录。案例2不安全的安全过滤输入如果我们修改非法输入而不是拒绝非法输入,很可能会出现问题。以sql注入防御不成熟为例。有人可能会说黑名单不完整。其实,即使把SQL中所有的保留字都加入黑名单,还是有问题,因为你不是拒绝输入,而是重写输入。在这种情况下,可以绕过双写。input?id='oorr1=1#因为输入被改写了,可以使用可预测的改写形式造成绕过。Case3可预测的密钥加密当我们使用一个被认为攻击者不可能获取的系统变量作为密钥,我们对程序的安全性沾沾自喜时,我们可能会翻车。以flask模块的session为例:flask的session放在cookie中,使用key加密保证不被篡改。这里的关键是主机名。如果通过某种方式获取到这个变量,会话将完全被攻击者控制,网站的所有用户和管理员都将被捕获。后续服务提供的下载功能存在缺陷,组合拳导致会话掉线。第四,意外的可控变量。这种全新的编程语言肯定需要与用户交互来控制一些变量。我们通常会对它进行安全检查,所以偶然的可控变量(我们认为不可控但实际上是用户可控的)很容易导致安全问题。Case1在两处存储变量当我们在两处存储变量时,可能会导致安全检查失败。以二次注入为例:这里实现了一个用户登录的功能,开发者在这里做了很大的安全限制和各种逃逸处理。但是他把变量存储在两个地方,导致漏洞还是出现了。我们可以发现非法输入隐藏在会话中以逃避安全检查。如果我们构造username='或1=1#,就可以修改所有用户的密码。案例2认为一个可控的变量是不可控的其实,有些变量即使是通过编程语言中获取常量的方式得到的,也不能大意,它们还是有可能是可控的。以用户代理注入为例。可以看出,这里的开发者在安全方面下了很大的功夫,安全意识也很强,但是还是会出现问题。这都是因为在开发者使用的语言中,获取变量的方式可能是常量的形式,开发者认为这是不可控的。结语有安全意识的开发者还是有可能制造漏洞,因为很多开发中用不到的特性,甚至编程语言官方意想不到的场景都不是开发者的常识,代码安全审计是很有必要的。这种全新的编程语言可能出现的问题,是任何编程语言代码安全审计需要关注的共性点。
