本文是智能合约审计系列文章的第二部分,主要讨论了如何发现智能合约中的漏洞。文章首先定义了智能合约中的bug类型,然后详细介绍了在理解代码的过程中以及如何通过测试假设来发现潜在的漏洞,并强调了采用攻击者思维模式的重要性,通过不断提问和探索各种可能性来进一步挖掘潜在的漏洞。文章还提到审计是一个不断学习和改进的过程,即使失败也能提供宝贵的经验。
寻找打破智能合约的方法(审计:第 2 部分)
你是否也遇到过这些问题?请继续阅读。
这是审计循序渐进:第 2 部分。或者,更具体地说: 寻找打破智能合约的方法
本文的结构如下:
智能合约中的 Bug 是什么
如何在智能合约中查找 Bug 2.1. 在理解代码的同时 2.2. 测试假设 扩展假设列表 那么,它何时真正结束?
如果你在寻找某样东西,你最好知道它是什么。
定义 Bug 的方式有很多。精确的定义并不重要,重要的是拥有某种定义。
我最喜欢的定义是,Bug 可能由以下原因引起:
错误的实现 这是指代码没有按照它应该做的来执行。这发生在编写代码之前逻辑不健全,或者在开发人员的意图和实现之间有所偏差时。 例如:由于没有考虑 token 的小数位数而导致的错误计算
可被利用的实现 这是指有人可以利用代码的方式。大多数情况下,这将是盗窃、恶意破坏或 DoS 攻击。 例如:一个允许攻击者耗尽合约的重入漏洞
有些 Bug 可能属于这两个类别,但这并不重要。定义的目的是为了阐明我们正在寻找的东西。
注意:本节仅作为对本文后续部分的解释而包含。我在审计时不会主动考虑这些类别。
理解代码是一个迭代的过程,包括阅读代码和重构其背后的逻辑(或意图)。
👉 我通过检查预期逻辑是否与实现相匹配来查找错误:
迭代:阅读、重构、检查匹配
很多时候,实现与意图不符,这就是一个 Bug——一种错误的实现类型的 Bug。
让我用一个例子来说明:
flawedWithdraw ()
阅读代码后,我们可以重构出其背后的意图是允许用户从协议中提取他们的资金。
现在再次阅读它,并将意图与实现进行比较,我们可以看到存在不匹配:该函数不是将 token 发送给用户,而是从他们那里提取 token。这是一个 Bug!
(你是否在阅读解释之前发现了这个 Bug?请在下面评论)
这可能是一个愚蠢的例子,但这种类型的 Bug 非常普遍。
事实上,我通常以这种方式在我的审计中发现大多数 Bug——与理解代码库并行进行。
例外情况是当代码库成熟时。成熟的代码库具有坚实且良好实现的逻辑,因此阅读代码可以让你理解它的工作原理,但不会发现任何 Bug...
但是,它可能会揭示一些同样有价值的东西:线索。
当我有足够的时间时,我会达到一个地步,我凭记忆就能知道范围内每个函数的作用。这并非由于记忆,而是多次全神贯注地阅读和重构逻辑的副作用。
一个意想不到的后果是,在这一点上,继续阅读代码实际上没有意义。
在我作为审计员的最初几个月里,当我达到这一点时,我的审计就完成了。我觉得可能还存在 Bug,但阅读代码感觉毫无价值。
所以我别无选择,只能结束审计并继续进行下一个...
👉 这就是我上面提到的达到瓶颈并且无法找到其他东西的地步。
但随着时间的推移,我找到了一种克服这个问题的方法:测试假设。
在阅读代码时,我可能会获得一些可能被利用的线索,但缺乏足够的背景知识来确定。而且我可能不想中断我的流程来调查这条线索。
因此,我没有陷入兔子洞,而是写下假设并将其从我的脑海中移除。它可以是任何东西:
这个特定的舍入是否会导致下溢并 DoS 用户?
每条路径在执行代码之前都会累积利息,但有一条路径除外。为什么?
是否有一种方法可以膨胀用于计算提款金额的特定变量?
到审计结束时,我有一个包含 30 多个问题的列表,我在审计期间无法回答这些问题。但现在我应该能够回答,因为我现在有更多的背景知识。
不过,令人惊讶的是,在阅读这些问题时,我总是发现我无法回答所有问题。这意味着在研究代码时,我没有考虑到某个特定的角度。
而这可能正是隐藏一个晦涩 Bug 的角度。
所以我回到代码来调查这些假设。
测试假设是一个自我驱动的过程:在调查假设时,我会提出进一步的假设,写下来并进行检查。
我总是通过这样做来发现 Bug。
👉 如果,如果,如果?
你知道代码的作用,它打算做什么,以及用户对它的期望。
现在你必须采取攻击者的心态,并将你的态度从理解转变为打破。
目标是彻底摧毁。你想造成尽可能多的破坏,尽可能多地窃取。
我通过提问进一步扩展我的假设列表:
价值如何在协议内流动?
哪些变量会影响协议内部发生的价值重新分配?
哪些(函数、参数)会影响影响价值重新分配的变量?
什么可能会破坏协议?
是否有时效性函数?我可以阻止某人调用它吗?
我可以永远继续下去。这是一个永无止境的问题列表。
深入研究这一步真的值得。
有些问题有太多的前提条件,以至于它们看起来不可能发生。
有些假设几乎是 Bug,但最终却走向了死胡同。
但是,将 2 个无害的死胡同与一个高度受限的问题结合起来,可能就提供了关键漏洞所需的必要前提条件。
或者:何时停止寻找新的假设?
我不知道。我从未达到过一个可以说:我确信这里没有更多 Bug 了的地步。而且我认为我永远不会达到。
但在你检查完所有假设之前不要停止。
感谢你的阅读。
我希望本文能帮助你在审计竞赛中取得佳绩。
但我有个坏消息...
你会失败。
这是不可避免的,每个人都会经历它。
好消息是,失败是获胜的最快方式:错过问题为你提供了你能获得的最好的反馈。
你想知道我是如何利用错过的发现来提高我的审计技能的吗?
- 原文链接: x.com/philbugcatcher/sta...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!