首页 > 编程知识 正文

零日漏洞是什么意思(origin过多电脑于近期存取此账号)

时间:2023-05-03 18:23:10 阅读:84402 作者:840

“在etherscan上发现0日(零日)的洞怎么办? 我决定做“弹珠”。 ' ——paradigm研究伙伴samczsun

注:原文作者为samczsun。

我喜欢挑战假设。

我喜欢尝试做不可能的事,发现别人错过的东西,或者用他们从未见过的事来吓他们。 去年,我基于非常费解的Solidity漏洞向Paradigm CTF 2021写了挑战。 虽然公开了一种变体,但是我发现的漏洞实际上并没有被讨论过。 结果,想要挑战它的人大部分都被看起来不可能的性质所压倒。

几周前,我们在讨论关于Paradigm CTF 2022的计划。 当时,Georgios在推特上发布了一条难题推文。 我觉得在开始电话会议的同一天发表挑战很酷。 但是,这不仅仅是一个古老的挑战。 我想从这个世界得到什么。 有些没人看,有些超出了人们想象的极限。 希望利用0日(零日)的脆弱性,创建第一个以太坊CTF挑战赛。

1

如何制造0day(零日)漏洞

作为安全研究者,为了优化我们的时间,做一些基本的假设。 一个是,我们正在读的源代码确实产生了我们正在分析的合同。 当然,只有从可靠的地方(如Etherscan )读取源代码时,该假设才成立。 因此,如果Etherscan能找到验证错误的东西的方法,我就能以此为中心制作出真正绕过的谜题。

为了找出如何利用Etherscan的合同验证系统,必须验证几个合同。 我在Ropsten测试网络上引入了一些合同来折腾和验证它们。 很快,我们看到了以下界面。

选择了正确的设置,进入下一页。 在这里,我被要求提供合同源代码。

输入源代码后,点击了验证按钮。 果然,源代码现在附在合同里了。

我知道事情是怎么运作的,所以我可以嘲笑验证过程。 我首先尝试的是引进新合同,把foo改为bar,然后用原始源代码验证合同。 奇怪的是,Etherscan拒绝验证我的合同。

但是,当我手动比较双字节码的输出时,我发现很奇怪。 合同字节码必须是十六进制,但显然有些不是十六进制。

我知道将合同元数据附加到部署了solither的字节码上,但我从没想过它会如何影响合同的验证。 很明显,Etherscan正在扫描元数据的字节码,并将其替换为标签。 “这个区域内的东西什么都可以不同。 我们还在考虑同样的字节码。 ”

对于潜在的0日(零日)漏洞,这似乎是一条有前途的线索。 如果可以欺骗Etherscan,将元数据以外的东西解释为元数据,则可以调整在标记为{ipfs}的区域中展开的字节码,验证仍然是合法的字节码。

在创建事务中包括任何字节码的最简单方法是将其编码为构造函数的参数。 Solidity通过将ABI编码格式直接附加到创建事务数据中,对构造函数的参数进行编码。

但是,由于Etherscan很聪明,因此将构造函数的参数从所有类型的元数据嗅探器中排除。 可以看到构造函数的参数是斜体,表示与代码本身不同。

也就是说,必须以某种方式欺骗Solidity编译器,并发布字节序列,使其像嵌入的元数据一样进行控制。 但是,这似乎是一个很难解决的问题。 因为如果没有重大的编译器争论,就几乎无法控制索尔仁克使用的操作码和字节,之后源代码看起来就非常可疑了。

我思考了一会儿这个问题,直到意识到索契“几乎”发行任意一个字节其实非常简单

的。以下代码将导致 Solidity 发出 32 个字节的 0xAA。

bytes32 value = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;

受到鼓舞,我很快写了一个小合约,它将推送一系列常量,以便 Solidity 会发出与嵌入的元数据完全相似的字节码。

令我高兴的是,Etherscan 在我的合约中间标记了 满意的音响的存在。

我快速复制了预期的字节码,并用一些随机字节替换了 IPFS 满意的音响,然后部署了生成的合约。果然,Etherscan 像往常一样考虑了不同的字节业务,并允许我的合约得到验证。

有了这个合约,源代码建议在调用 example 时应该返回一个简单的字节对象。 但是,如果你真的尝试调用它,就会发生这种情况。

$ seth call 0x3cd2138cabfb03c8ed9687561a0ef5c9a153923f 'example'

seth-rpc: {"id":1,"jsonrpc":"2.0","method":"eth_call","params":[{"data":"0x54353f2f","to":"0x3CD2138CAbfB03c8eD9687561a0ef5C9a153923f"},"latest"]}

seth-rpc: error: code -32000

seth-rpc: error: message stack underflow (5 <=> 16)

我在 Etherscan 中成功发现了一个0day(零日)漏洞,现在我可以验证行为与源代码建议完全不同的合约。而现在,我只需要围绕它设计一个解密游戏。

2

一个错误的开始

显然,这个解密游戏将围绕这样一个想法,即 Etherscan 上看到的源代码并不意味着合约的实际行为方式。我还想确保玩家不能简单地直接重放交易,因此解决方案必须是每个地址唯一的。最好的方法显然是要求签名。

但是在什么情况下会要求玩家签署一些数据呢?我的第一个设计是一个具有单一公开函数的简单puzzle。玩家将使用一些输入调用该函数,对数据进行签名以证明他们提出了解决方案,如果输入通过了所有各种检查,那么他们将被标记为solver。然而,当我在接下来的几个小时内充实这个设计时,我很快就对事情的结果感到不满意了,它开始变得非常笨重和不优雅,我无法忍受在一个设计如此糟糕的puzzle上毁掉如此棒的0day(零日)漏洞想法。

不得不承认,我无法在周五前完成这件事,于是我决定睡一觉。

3

Pinball弹球难题

周末我继续尝试迭代我的初始设计,但没有取得更多的进展。就好像我现在的方法碰壁了,尽管我不想承认,但我知道如果我想要我满意的东西,我可能不得不重新开始。

最终,我发现自己从第一原理重新审视了这个问题。我想要的是一个解密游戏,玩家必须在其中完成各种知识检查。但是,我没有要求完成知识检查本身就是获胜的条件,相反,这可能是允许玩家选择的众多路径之一。也许玩家可以在整个puzzle中累积分数,利用这个漏洞可以获得某种奖励。赢的条件只是最高的分数,因此间接鼓励使用漏洞。

我回想起我去年设计的一个挑战,Lockbox,它迫使玩家构建一个单一的数据块,以满足六个不同合约的要求。合约会对相同的字节应用不同的约束,迫使玩家在构建有效载荷的方式上变得聪明。我意识到我想在这里做一些类似的事情,我会要求玩家提交一个单一的数据blob,我会根据满足特定要求的某些数据部分来奖励分数。

正是在这一点上,我意识到我基本上是在描述 pinboooll,这是我在 DEFCON CTF 2020 决赛期间面临的一个挑战。pinboooll 的噱头是俏皮的大象执行二进制文件时,执行会在控制流图上反弹,就像一个球在弹球机中反弹一样。通过正确构造输入,你将能够找到特定的代码部分并获得分数。当然,也有一个漏洞,但坦率地说,我已经忘记了它是什么,我也不打算再去寻找它。除此之外,我已经有了自己想要利用的漏洞。

由于我在处理的是一个运行当中的零日漏洞,我决定要尽快解决这个难题。最后,我花了几个小时让自己重新认识pinboooll的工作原理,并花了几天时间将其重新实现。这就解决了puzzle的支架问题,现在我只需要集成这个漏洞。

4

武装化一个零日漏洞

我让 Solidity 输出正确字节的方法一直是只加载几个常量,并让 Solidity 发出相应的 PUSH 指令。然而,这样的任意常数可能是一个巨大的危险信号,我想要一些能更好地融入其中的东西。 我还必须加载一行中的所有常量,这在实际代码中很难解释。

因为我真的只需要硬编码两个神奇的字节序列(0xa264...1220 和 0x6473...0033),所以我决定看看是否可以在它们之间夹入代码,而不是第三个常量。在已部署的合约中,我只需要用一些其他指令替换夹在中间的代码。

address a = 0xa264...1220;

uint x = 1 + 1 + 1 + ... + 1;

address b = 0x6473...0033;

经过一些实验,我发现这是可能的,但前提是启用了优化器。否则,Solidity 会发出过多的值清理代码。 这是可以接受的,所以我继续改进代码本身。

我只能在两个地址内修改代码,但是在最后看到一个悬空的地址会很奇怪,所以我决定在条件语句中使用它们。我还必须证明第二个条件的必要性,所以最后我投了一点分数奖励。我做了第一次有条件的检查,检查tx.origin是否匹配了硬编码的值,以给人们一个最初的印象,即没有必要进一步追求这个代码路径。

if (tx.origin != 0x13378bd7CacfCAb2909Fa2646970667358221220) return true;

state.rand = 0x40;

state.location = 0x60;

if (msg.sender != 0x64736F6c6343A0FB380033c82951b4126BD95042) return true;

state.baseScore += 1500;

现在源代码都准备好了,我必须编写实际的后门。我的后门需要验证玩家是否正确触发了漏洞利用,如果他们没有正确触发,则不给出任何提示就失败,如果他们成功触发,则奖励他们。我想确保漏洞不会被轻易重放,所以我决定只要求玩家签署自己的地址,并在交易中提交签名。为了增加乐趣,我决定要求签名位于交易数据中的偏移量0x44处,球通常会从这里开始。这将需要玩家了解 ABI 编码的工作原理,并手动将球数据重新定位到其他地方。

但是,在这里我遇到了一个大问题:根本不可能将所有这些逻辑都放入 31 字节的手写汇编中。幸运的是,经过一番考虑,我意识到我还有另外 31 个字节可以使用。毕竟,真正嵌入的元数据包含了另一个 Etherscan 也会忽略的 IPFS 满意的音响。

在打了一些代码高尔夫之后,我抵达了一个可以工作的后门。在第一个 IPFS 满意的音响中,我会立即弹出刚刚推送的地址,然后跳转到第二个 IPFS 满意的音响。在那里,我会满意的音响调用方,并部分设置memory/堆栈以调用 ecrecover。然后我会跳回第一个 IPFS 满意的音响,在那里我完成设置堆栈并执行调用。最后,我将分数乘数设置为等于 (msg.sender == ecrecover) * 0x40 + 1,这意味着不需要额外的分支。

在把后门编码成一定大小后,我在推特上发布了我的Rinkeby地址,以便从水龙头处获取一些测试网,然后向任何观看这条推文的人暗示可能会发生一些事情。接着,我部署了合约并对其进行了验证。

现在剩下要做的,就是等待有缘人发现隐藏在视线中的后门。

注:截至发稿时,来自Rocket Pool的软件开发者舒心的招牌 Wallmann已经解出了这个谜题,具体过程在这里:https://medium.com/@感动的篮球_71759/an-untrustworthy-pinball-machine-d9dcd07882c

此外,Etherscan开发者Caleb Lau也已经修复了该漏洞。

本文来自元宇宙之道,星球日报经授权转载。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。