本文是关于如何查找代码漏洞文章系列的第一篇。从宏观来看,过程是这个样子:
我们将从第一部分开始:如何找到潜藏危险的功能。以我的经验,80%的错误都藏在大约 20%的代码中。具体来说,这个比例更接近 90/10。由于我们通常需要彻底理解代码才能发现罕见的漏洞,因此找出需要重点关注的那 20%就变得至关重要。我使用的一种方法是关注“坏词(bad words)”的群集。
我将通过一个简短的故事告诉大家。最近在一次红队行动中,我浏览一个巨大的 terraform 仓库时看到一行看起来很正常的代码:
driver.raw_exec.enable = 1
这个时候,我甚至都没有在尝试寻找漏洞,但是看到这行代码后,我就停下来弄清楚它在做什么。原来,它正在配置 Hashicorp 一个名为 Nomad 的作业调度程序。作为一名尽责的红队队员,我立刻查询了 Nomad 文档。在那里,我发现了一个不祥警告:
这让你可以无隔离地运行作业;出于安全原因,默认情况下将其禁用。
我马上精神了,迅速做出一个最简单的 Nomad 作业。几分钟后,我就有了对该集群的 root 访问权限和一大堆凭证。这次行动马上就可以完成了。
在此之前,我甚至从未听说过 Nomad,更不用说这个配置选项了。那么这行代码到底有什么特殊引起了我的关注,让我想要深入研究下去呢?其实是因为两个安全坏词的组合,它们流连在同一代码段,分别是“raw”和“exec”。这些坏词可以帮助你筛选出代码中最关键的安全部分,让你能高效地利用你的注意力。
Raw 表示你正在访问较低级别的抽象。当在较高级别上实施安全控制时,这将成为一个问题,让这个“raw”界面的用户可以绕过它们。
例子:
raw_exec
驱动程序允许你创建在容器外部运行的作业,拥有nomad代理的许可。rawQuery
或rawSQL
方法,让你可以直接执行查询。ORM生成的查询通常是不可注入的,但是要由用户决定使用“raw”界面时是否阻止SQLi。一般来说,将用户输入与用动态语言(JavaScript、SQL、bash 等)编写的代码相结合,通常是注入攻击的好途径。攻击者可以提交代码作为输入,从而导致解释器执行不当行为。运行这种代码通常称为“executing”、“evaluating”或“running”。
例子:
raw_exec
无隔离地运行Nomad作业conn.cursor().execute(sql)
在许多python数据库驱动程序中运行SQL查询exec(code)
是运行传递给它的代码的python方法eval(code)
是许多动态语言提供的一个函数,例如JavaScript,可以用它运行你传递的代码。Python也有一个eval函数,但仅用于表达式。*这将返回很多误报,因为正如 Steve Yegge 预测的那样,似乎每个动词都通过run()
、execute()
或justDoIt()
方法变成了一个名词。
这些词可以用来指示子进程的创建。如果子进程生成了一个外壳,那么你就可能注入外壳命令。即使它直接调用 execve syscall,你仍然可以在程序中添加/修改参数。
例子:
subprocess
模块child_process
模块os/exec
包os.system
方法popen
模块这些单词可以帮助你找到负责向用户、容器、进程、文件、EC2 实例等授予特权的代码。你可以使用任何拥有高特权的实体来下手,甚至完全绕过 authz。
例子:
jwt.ParseWithClaims
之类的函数来验证它们。许多编程语言都允许你通过名称来查找函、类、方法、变量等(甚至实例化/调用它们)。这通常称为“reflection”。如果用户可以控制要调用方法的名称或要返回变量的名称,则可能会导致程序行为异常。
例子:
Reflect
对象String#constantize
方法Class.forName
方法klass
是通过反射查找类的一个通用变量名称(因为“class”往往是保留字)这些词表示程序可能正在使用支持复杂对象的格式对数据进行反序列化。这可以让攻击者读取文件、发送 HTTP 请求甚至执行任意代码,具体取决于序列化格式以及运行时可用的对象(JVM 类路径上的类、python 中sys.path
上的包等)。
例子:
这些单词可能很有趣,原因和eval()
之类差不多:攻击者可以输入有问题的 parser 识别的元字符来更改其行为。主要区别在于,你不是在动态语言中运行代码,而是利用 parser 来访问文件或 URL 等资源。
例子:
有时,API 开发人员喜欢在名称中包含“insecure”或“unsafe”来引起开发人员对危险 API 的关注。
例子:
unsafe {}
块InsecureSkipVerify
dangerouslySetInnerHtml()
unsafe
软件包随着代码的发展,开发人员会添加注释,以提醒自己要实现功能、修复错误或清理一些自己不喜欢的代码。有时,这些注释可能会帮助你发现重要的错误、缺少的功能等,可以利用它们。
例子:
todos.txt
文件。它包含一长串未修补的安全漏洞。这些词通常表示一个对象、字典、映射等正在与另一个对象合并或克隆到一个新对象中。这可能会导致有趣的安全问题,例如 JavaScript 原型污染漏洞、大规模分配漏洞等。
例子:
_.merge
_.clone
它们是发生手动内存管理的一个很好的线索。众所周知,这很难解决,并且可能导致诸如缓冲区溢出、释放后使用、双重释放等漏洞。
例子:
[object alloc]
消息这些是加密原语,可以表明作者正在使用自己的密码系统,而不是使用更高级别的抽象。有许多不起眼的细节可以让它们变得不够安全,因此请仔细阅读代码并咨询加密专家。
例子:
JSON Web 令牌是安全传输数据的标准,在现代应用程序栈中非常常用。有很多不安全地使用它们的途径,因此值得关注处理 JWT 的代码。
常见的 JWT 问题:
例子:
这些词是很好的指示,表明你可能已经将一些秘密硬编码到了存储库中,例如 API 密钥、数据库密码、加密密钥等。
例子:
这些词通常表示代码正在执行业务/安全规则。请仔细检查这些内容,以获取通过了验证,但也可能导致漏洞的输入。他们试图禁止的输入类型也可以为你提供有关潜在漏洞的线索。
例子:
app.get('/signup', (req, res) => {
// verify! this probably means that only users with certain
// emails are allowed to sign up. I wonder what it's
// verifying?
if (!verifyEmail(req.body.email)) {
res.send('unauthorized');
return;
}
register(req.body.email);
res.redirect('/dashboard');
});
// looks like it's verifying the email belongs to a user
// on company.com. can you think of a way to make this return
// true without having a @company.com email address?
//
// what about will@company.com.btlr.dev?
function verifyEmail(email) {
return email.includes('@company.com');
}
解析攻击者控制的 XML 可能会导致许多安全问题,从本地文件读取到拒绝服务攻击等等。
例子:
原文链接:
https://btlr.dev/blog/how-to-find-vulnerabilities-in-code-bad-words
领取专属 10元无门槛券
私享最新 技术干货