code-423n4 / 2023-01-opensea-findings

0 stars 0 forks source link

Exploring the Vulnerabilities of Seaport: A Technical Analysis of a Fake Signature Attack on Non-Fungible Tokens #64

Closed code423n4 closed 1 year ago

code423n4 commented 1 year ago

Lines of code

https://github.com/ProjectOpenSea/seaport-js/blob/68f4ddd715435d90c63851a313eccc5553103ecd/src/seaport.ts#L398 https://github.com/ProjectOpenSea/seaport-js/blob/68f4ddd715435d90c63851a313eccc5553103ecd/src/seaport.ts#L645

Vulnerability details

Impact

This finding aims to provide a comprehensive analysis of the sc4m trend, which emerged in August 2022, and has since been a prevalent issue in the WEB3 space. Despite efforts to combat this phenomenon, bad actors continue to engage in illicit activities, including the theft of assets valued in the millions of dollars. The primary focus of this finding is to examine the technical mechanisms employed by these actors in the process of stealing assets from Opensea, utilizing code examples to provide a detailed understanding of the scam process. Additionally, this study will explore various potential solutions to this horrific issue.

Proof of Concept

This part aims to provide a comprehensive examination of the technical mechanisms employed in the replication of a fake signature scam, with a specific focus on non-fungible tokens (NFTs). However, it is important to note that the techniques and methodologies described here can also be applied to other types of digital assets, such as ERC20 tokens and WETH, which possess the approval functionality in their smart contracts and are approved to the Conduit (in this case, the OpenSea Conduit).

1) Connection with Wallet & Analyze of Assets

The demo website is really easy. It going to have only one button for wallet connect. After the tap, we are connecting his wallet, creating the web3 provider. When we have an address, our goal is to get all NFTs that victim has on it's wallet. Opensea made it really easy for us, we are going to use their free API. This is the example of GET request we gonna send:

The website is designed to be user-friendly, featuring a single button for wallet connection. Upon tapping the button, users will connect their wallet, creating a web3 provider. The next step in the process is to retrieve information on all non-fungible tokens (NFTs) present in the victim's wallet. The OpenSea platform offers a convenient and accessible API that allows for easy retrieval of this information. The demonstration of the GET request that will be sent to the API is located below.

When we have a full list of NFTs the victim has, lets sort them.

Lest move on to the sorting part:

1) Creation of a fake executable order

The order object is a complex structure that consists of various parameters. We will begin by replicating these parameters starting with the "counter" parameter. The counter parameter is relatively simple to replicate, as it can be obtained by calling the "getCounter" method in the Seaport contract (0x00000000006c3852cbEf3e08E8dF289169EdE581). This method will return the current counter value, which is a necessary component of the order object.

After obtaining the "counter" parameter, the next step is to generate the "offer" and "consideration" arrays. These arrays are crucial components of the order object, as they represent the assets that the victim is allegedly giving to the attacker and the assets that the attacker is offering in return. The process of creating these arrays is relatively straightforward. Using the NFTs obtained in the previous step, we will create two objects for each NFT. These objects will contain specific information about the NFTs, such as their unique identifiers. An example of the structure of these arrays is provided below.

It is necessary to create the "startTime" and "endTime" parameters. These parameters represent the time period during which the fake order for transferring NFTs is valid. The next step is to set the "totalOriginalConsiderationItems" parameter, which will be equal to the length of the "consideration" array that we generated previously. Additionally, the "orderType" parameter is set to 2, which is the appropriate value for this type of transaction. The "offerer" parameter is set to the victim's address, as the fake order is being created on their behalf.

Certain parameters, such as "zone", "zoneHash" and "conduitKey" were taken from an example published on the Seaport's GitHub repository. These values are assumed to be constant, and do not require further manipulation.

Finally, we need to generate a "salt" value, which is a unique value that is used to randomize the order hash and ensure the integrity of the signature. This is the example of my code:

Finally, the order is completed. Let's proceed to the next step.

3) Replication of EIP712 Seaport Signature

In my experimentation, I attempted to generate an EIP712 signature from scratch using various domain parameters and other necessary inputs, as I initially believed that the Seaport-SDK library would somehow detect any fake parameters passed to it and prevent the creation of malicious signatures. However, upon further examination, I discovered that the Seaport-SDK library employs similar underlying mechanisms to generate the EIP712 signatures and does not include any specific safeguards against the creation of bad signatures.

Contrarily, other libraries like Uniswap's permit2-sdk provides a mechanism to detect any discrepancies in the inputs and throws an error, which would prevent potential scammers from creating malicious signatures. However, creating the signature from scratch would require a significant amount of additional effort and resources.

The code that you ca see below allows me to successfully execute a Replay Attack. Take a careful look on it:

In this step of the finding, the forged Seaport signature is obtained by providing the order for signature to the victim without providing the counter value and passing this value only after the signature has been made. This is a crucial step in the process as it allows the attacker to manipulate the counter value.

At this point, the order object has been fully created, and it contains all the necessary values and inputs, including the signature and the current counter value. The order object can be saved as a JSON file, which can be executed manually later. For instance, it can be used to create an automated system that sends these orders in a Telegram group, etc. However, as long as the counter value is not incremented or the victim haven't revokes the approval, the order object will remain valid.

This method is not ideal for the attacker as it requires manual intervention and is not fully automated. Therefore, the I will proceed to make modifications to the process in order to achieve a fully automated process.

In order to automate the process of executing ready (signed) orders, it is necessary to utilize another wallet, referred to as the "initiator wallet". The primary function of this wallet is to execute the order at the same moment when it is created. To achieve this, a new instance of the Seaport class must be created using this new wallet, and the "fulfillOrder" method must be called. This method requires the order object and the address of the wallet that will initiate the execution.

It is important to note that the Seaport library is relatively new and may encounter errors during the execution of this process. However, through repeated experimentation, I have identified the best conditions that minimize the occurrence of errors. For example, the initiator & recipient wallets must have a non-zero balance, as there may be underlying processes that require this.

The results of this finding are a fully functional website that can be used to steal assets approved to Opensea. This is a significant vulnerability as Opensea is the most popular NFT platform and a large number of people in the web3 community have their NFTs approved to it. This vulnerability is detrimental to the adoption of web3 as many people are leaving the field after being scammed. Therefore, my finding is of great importance and should not be ignored.

Tools Used

The replication of this fake signature attack required extensive use of the Seaport-SDK and thousands of hours spent on Etherscan and in Visual Studio Code. All of these can be achieved in pure JavaScript. The underlying concept of the attack may appear to be relatively simple, but the most challenging aspect of this research was the implementation of the attack. This process involved bringing all the various components together and finding solutions for the numerous small bugs by rough experimentation. The process of coding and integrating all the pieces together was a time-consuming and tedious task, but it was necessary in order to successfully replicate the attack.

Recommended Mitigation Steps

I am aware that various solutions have been proposed to address the issue of fake signature attacks, such as marking assets as stolen to prevent scammers from selling them. However, I personally believe that this approach is not suitable as it undermines the very principle of decentralization. It is really bad when web3 companies like Opensea solving problems using web2 methods.

Another approach that has been implemented is the button for incrementation the counter, but this is also not an effective solution as scammers have developed sophisticated codes that automatically retrieve the nonce and use Seaport-SDK to quickly steal assets. Exactly as I showed you previously.

The most reliable solution currently available is for users to revoke all their approvals, but this approach is not ideal as it negatively impacts the user experience. The current contracts need to be optimized for quick interactions and approvals need to be minimized. Therefore, a new approach is needed to solve this problem.

After considerable thought and research, I have come to the conclusion that the best solution is the implementation of an unordered counter like the one used in Uniswap's contract called Permit2. This approach helps to prevent signature forging, the Replay Attack and is a more reliable solution compared to the others. However, it would require a significant amount of code re-writing to implement this solution in Seaport.

I wish you good luck in finding the best solution to this complex problem. I am confident that with the team of skilled developers at your disposal, a solution can be found. If you need assistance, please do not hesitate to contact me. I am open to new opportunities and would be willing to collaborate on the development of an effective solution.

0age commented 1 year ago

contested; this touches on tons of out-of-scope areas and the "crucial step in the process" implies that the counter is not a component of the signature, which is false

HickupHH3 commented 1 year ago

In this step of the finding, the forged Seaport signature is obtained by providing the order for signature to the victim without providing the counter value and passing this value only after the signature has been made. This is a crucial step in the process as it allows the attacker to manipulate the counter value.

Agree that the POC boils down to this fact, which isn't true. Also, this "vulnerability" is out of scope of the contest because it has very little to do with the Seaport contracts (more to do with the SDK).

For completion, as long as you can get the owner to sign a malicious order, assets can be stolen.

c4-judge commented 1 year ago

HickupHH3 marked the issue as unsatisfactory: Out of scope