GithubHelp home page GithubHelp logo

2022-12-forgeries-findings's Introduction

Forgeries Contest

Unless otherwise discussed, this repo will be made public after contest completion, sponsor review, judging, and two-week issue mitigation window.

Contributors to this repo: prior to report publication, please review the Agreements & Disclosures issue.


Contest findings are submitted to this repo

As a sponsor, you have three critical tasks in the contest process:

  1. Weigh in on severity.
  2. Respond to issues.
  3. Share your mitigation of findings.

Let's walk through each of these.

High and Medium Risk Issues

Please note: because wardens submit issues without seeing each other's submissions, there will always be findings that are duplicates. For all issues labeled 3 (High Risk) or 2 (Medium Risk), these have been pre-sorted for you so that there is only one primary issue open per unique finding. All duplicates have been labeled duplicate, linked to a primary issue, and closed.

Weigh in on severity

Judges have the ultimate discretion in determining severity of issues, as well as whether/how issues are considered duplicates. However, sponsor input is a significant criteria.

For a detailed breakdown of severity criteria and how to estimate risk, please refer to the judging criteria in our documentation.

If you disagree with a finding's severity, leave the severity label intact and add the label disagree with severity, along with a comment indicating your opinion for the judges to review. It is possible for issues to be considered 0 (Non-critical).

Feel free to use the question label for anything you would like additional C4 input on.

Please don't change the severity labels; that's up to the judge's discretion.

Respond to issues

Label each open/primary High or Medium risk finding as one of these:

  • sponsor confirmed, meaning: "Yes, this is a problem and we intend to fix it."
  • sponsor disputed, meaning either: "We cannot duplicate this issue" or "We disagree that this is an issue at all."
  • sponsor acknowledged, meaning: "Yes, technically the issue is correct, but we are not going to resolve it for xyz reasons."

(Note: please don't use sponsor disputed for a finding if you think it should be considered of lower or higher severity. Instead, use the label disagree with severity and add comments to recommend a different severity level -- and include your reasoning.)

Add any necessary comments explaining your rationale for your evaluation of the issue. Note that when the repo is public, after all issues are mitigated, wardens will read these comments.

QA and Gas Reports

For low and non-critical findings (AKA QA), as well as gas optimizations: all warden submissions in these three categories should now be submitted as bulk listings of issues and recommendations:

  • QA reports should include all low severity and non-critical findings, along with a summary statement.
  • Gas reports should include all gas optimization recommendations, along with a summary statement.

For QA and Gas reports, we ask that you:

  • Leave a comment for the judge on any reports you consider to be particularly high quality. (These reports will be awarded on a curve.)
  • Add the sponsor disputed label to any reports that you think should be completely disregarded by the judge, i.e. the report contains no valid findings at all.

Once labelling is complete

When you have finished labelling findings, drop the C4 team a note in your private Discord backroom channel and let us know you've completed the sponsor review process. At this point, we will pass the repo over to the judge to review your feedback while you work on mitigations.

Share your mitigation of findings

Note: this section does not need to be completed in order to finalize judging. You can continue work on mitigations while the judge finalizes their decisions and even beyond that. Ultimately we won't publish the final audit report until you give us the ok.

For each finding you have confirmed, you will want to mitigate the issue before the contest report is made public.

As you undertake that process, we request that you take the following steps:

  1. Within your own GitHub repo, create a pull request for each finding.
  2. Link the PR to the issue that it resolves within your contest findings repo.

This will allow for complete transparency in showing the work of mitigating the issues found in the contest. Do not close the issue; simply label it as resolved. If the issue in question has duplicates, please link to your PR from the open/primary issue.

2022-12-forgeries-findings's People

Contributors

code423n4 avatar c4-staff avatar c4-judge avatar captainmangoc4 avatar kartoonjoy avatar

Watchers

Ashok avatar  avatar

2022-12-forgeries-findings's Issues

QA Report

See the markdown file with the details of this report here.

QA Report

See the markdown file with the details of this report here.

Inadequate Access Control in makeNewDraw() Function

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDrawFactory.sol#L38-L51

Vulnerability details

Impact

One potential impact of this vulnerability is that an attacker could create many VRFNFTRandomDraw contracts, potentially causing resource exhaustion on the Ethereum network. This could potentially lead to increased gas fees and decreased performance for users of the network.

Proof of Concept

The makeNewDraw() function in the VRFNFTRandomDrawFactory contract does not have the onlyOwner modifier, meaning that anyone can call it and create new VRFNFTRandomDraw contracts. This could potentially lead to spamming or other abuses if the contract is not carefully managed.

Tools Used

Mythril to conduct a static analysis of the contract.This will flag any functions that are not marked onlyOwner but should be, allowing the developer to fix the issue before deployment.

Recommended Mitigation Steps

One possible mitigation for this vulnerability would be to add the onlyOwner modifier to the makeNewDraw() function, as shown below:

function makeNewDraw(IVRFNFTRandomDraw.Settings memory settings)
external
onlyOwner
returns (address)
{
address admin = msg.sender;
// Clone the contract
address newDrawing = ClonesUpgradeable.clone(implementation);
// Setup the new drawing
IVRFNFTRandomDraw(newDrawing).initialize(admin, settings);
// Emit event for indexing
emit SetupNewDrawing(admin, newDrawing);
// Return address for integration or testing
return newDrawing;
}

This change would restrict the ability to create new 'VRFNFTRandomDraw' contracts to the contract owner, preventing potential spamming or other abuses.

QA Report

See the markdown file with the details of this report here.

QA Report

See the markdown file with the details of this report here.

unauthorized access vulnerability

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDrawFactoryProxy.sol#L8-L12

Vulnerability details

Impact

This vulnerability could potentially allow an attacker to set themselves as the initial owner of the underlying contract and gain control over it. This could have significant consequences, depending on the functionality of the contract and the value of the assets it manages.

Proof of Concept

a smart contract function is not marked as internal or private, allowing it to be called by anyone. In the case of the IVRFNFTRandomDrawFactory.initialize() function, this could allow an attacker to set themselves as the initial owner of the underlying contract and gain control over it.

Tools Used

Mythril

Recommended Mitigation Steps

To mitigate this vulnerability, the 'IVRFNFTRandomDrawFactory.initialize()' function should be marked as 'internal' or 'private' to prevent unauthorized access. Additionally, the contract should implement access control checks, such as the 'onlyOwner' modifier, to ensure that only the contract owner can call sensitive functions. Finally, the function should be checked for reentrancy to prevent attackers from calling it multiple times and gaining unauthorized access.

Admin can withdraw NFT even when request.drawTimelock is not over

Lines of code

https://github.com/0xigami/vrf-nft-raffle/blob/main/src/VRFNFTRandomDraw.sol#L304

Vulnerability details

Impact

It was observed that if a winner is chosen and request.drawTimelock is still remaining, still Admin can withdraw the NFT. This will cause winner to get nothing

Proof of Concept

  1. Admin has started new draw using startDraw function. Lets say settings.recoverTimelock is X
function startDraw() external onlyOwner returns (uint256) {
        // Only can be called on first drawing
        if (request.currentChainlinkRequestId != 0) {
            revert REQUEST_IN_FLIGHT();
        }

        // Emit setup draw user event
        emit SetupDraw(msg.sender, settings);

        // Request initial roll
        _requestRoll();

        // Attempt to transfer token into this address
        try
            IERC721EnumerableUpgradeable(settings.token).transferFrom(
                msg.sender,
                address(this),
                settings.tokenId
            )
        {} catch {
            revert TOKEN_NEEDS_TO_BE_APPROVED_TO_CONTRACT();
        }

        // Return the current chainlink request id
        return request.currentChainlinkRequestId;
    }
  1. A winner is chosen by chainlink using the fulfillRandomWords function

  2. Assume User A has won

  3. settings.recoverTimelock (X) time has passed, but request.drawTimelock is still left

  4. Admin simply calls lastResortTimelockOwnerClaimNFT function which transfer the NFT back to Admin even though request.drawTimelock is not reached and winner was already present

function lastResortTimelockOwnerClaimNFT() external onlyOwner {
        // If recoverTimelock is not setup, or if not yet occurred
        if (settings.recoverTimelock > block.timestamp) {
            // Stop the withdraw
            revert RECOVERY_IS_NOT_YET_POSSIBLE();
        }

        // Send event for indexing that the owner reclaimed the NFT
        emit OwnerReclaimedNFT(owner());

        // Transfer token to the admin/owner.
        IERC721EnumerableUpgradeable(settings.token).transferFrom(
            address(this),
            owner(),
            settings.tokenId
        );
    }

Recommended Mitigation Steps

Admin should not be allowed to withdraw if request.drawTimelock is not over.

function lastResortTimelockOwnerClaimNFT() external onlyOwner {
require(request.drawTimelock < block.timestamp && request.hasChosenRandomNumber, "Cannot withdraw now");
...
}

Risk of front-run attack on `VRFNFTRandomDraw.initialize'

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/82a5fc7540249325ae48498720d40d943ee17e27/src/VRFNFTRandomDraw.sol#L75

Vulnerability details

Risk of front-run attack on `VRFNFTRandomDraw.initialize'

The initialize function is public and has no ckecks on who is goint to execute it. So it has a risk of front-run attack where a malicious agent could the the deployed address and imidiately run initialize turning himself a owner, after deploy. Even though this contract is being cloned, there is a original implementation deployed.

Proof of Concept

VRFNFTRandomDraw.sol#L75

Recommended Mitigation Steps

To avoid that, the initialize function should check, in any appropriate manner for the owner or deployer (something like onlyOwner). Where on the constructor the address of the deployer is stored and then checked on initialize, before executing it.

QA Report

See the markdown file with the details of this report here.

control over the contract's admin account

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L75-L138

Vulnerability details

Impact

One potential vulnerability in the contract is that the initialize function can only be called once during contract deployment, but it does not check whether the contract has already been initialized. This means that if the contract is already initialized, calling initialize again could potentially overwrite the existing settings with new ones. This could be exploited by an attacker who has control over the contract's admin account to change the contract's settings without the consent of the other users. An attacker who has control over the contract's admin account could exploit this vulnerability to change the contract's settings without the consent of the other users. This could potentially allow the attacker to manipulate the raffle or alter the contract's behavior in other ways.

Proof of Concept

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L75-L85

to

function initialize(address admin, Settings memory _settings)
public
initializer
{
// Check if the contract has already been initialized
if (settings.drawBufferTime > 0) {
revert("Contract has already been initialized");
}

// Set new settings
settings = _settings;

// Check values in memory:
if (_settings.drawBufferTime < HOUR_IN_SECONDS) {
    revert REDRAW_TIMELOCK_NEEDS_TO_BE_MORE_THAN_AN_...
}

// Set the admin
ownableUpgradeable.initialize(admin);

}

Tools Used

Mythril , Securify

Recommended Mitigation Steps

To fix this vulnerability, the initialize function should include a check to see if the contract has already been initialized. If it has, the function should revert the transaction and prevent the settings from being overwritten. Here is an example of how this could be implemented:

function initialize(address admin, Settings memory _settings)
public
initializer
{
// Check if the contract has already been initialized
if (settings.drawBufferTime > 0) {
revert("Contract has already been initialized");
}

// Set new settings
settings = _settings;

// Check values in memory:
if (_settings.drawBufferTime < HOUR_IN_SECONDS) {
    revert REDRAW_TIMELOCK_NEEDS_TO_BE_MORE_THAN_AN_...
}

// Set the admin
ownableUpgradeable.initialize(admin);

}

This change will ensure that the contract's settings can only be initialized once, and cannot be changed by an attacker who has control over the admin account.

QA Report

See the markdown file with the details of this report here.

Lack the onlyOwner for initialize() function

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/main/src/VRFNFTRandomDrawFactory.sol#L31

Vulnerability details

Impact

initializer can only guarantee this function will be called only once, but we need to control the access about who will call this function. Right now anyone can call this function and then you can't modify it.

Proof of Concept


    function initialize(address _initialOwner) initializer external {
        __Ownable_init(_initialOwner);
        emit SetupFactory();
    }

We need to add onlyOwner here.

Tools Used

Manual reading

Recommended Mitigation Steps

we need to add onlyOwner here.

Unsecured contract upgrade vulnerability

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDrawFactory.sol#L14

Vulnerability details

Impact

in the contract VRFNFTRandomDrawFactory where the owner can upgrade the contract implementation without additional security measures is considered a high risk. An attacker who gains control of the owner's account could potentially compromise the contract and steal funds or NFTs.

Proof of Concept

vulnerability in the VRFNFTRandomDrawFactory contract,
An attacker gains control of the contract owner's account.
The attacker uses the contract's _authorizeUpgrade() function to upgrade the contract implementation to a malicious or vulnerable implementation.
The attacker then uses the newly upgraded contract to steal funds or NFTs from the contract.

Tools Used

Mythril or Oyente

Recommended Mitigation Steps

One potential mitigation for this vulnerability would be to add a timelock to the contract, requiring the owner to wait a certain amount of time before upgrading the contract implementation. This would provide an additional layer of security by preventing an attacker from immediately upgrading the contract after gaining control of the owner's account. Additionally, the contract could be modified to require a multisig, where multiple parties must agree on an upgrade before it can be executed. This would make it more difficult for an attacker to compromise the contract.

VRFNFTRandomDraw contract can be useless

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/main/src/VRFNFTRandomDraw.sol#L77

Vulnerability details

Impact

if the VRFNFTRandomDraw contract is meant to be ran without proxy it will be useless 100% and less to happens as well if used as implimentation

Proof of Concept

Contract VRFNFTRandomDraw can be useless at some point as it uses the initializer modifier twice, the first in the constructor

constructor(VRFCoordinatorV2Interface _coordinator)
        VRFConsumerBaseV2(address(_coordinator))
        initializer
    {

and secondly in the initializer,

function initialize(address admin, Settings memory _settings)
        public
        initializer
    {
        

this method is used to prevent calls that are not from delegatecall, means this contract is meade to be an implimentation however the proxy may have an initializer as well and this will makes the call to VRFNFTRandomDraw>initialize checks the initialized int and it will reverts, so there will be no settings set and the whole "Draw with NFT Tickets" logic will be unavailable

Tools Used

Manual

Recommended Mitigation Steps

call the initiailize function under the contstructor if you mean to run it directly without proxy, if you intend to run it within proxy use this logic :

address contract _this = address(this)

...

modifier check() {
  require(_this != address(this),"call only from delegatecall");
}

...
// use that modifier later

to avoid storage entanglement between the proxy and VRFNFTRandomDraw contract on the initialized bool

Agreements & Disclosures

Agreements

If you are a C4 Certified Contributor by commenting or interacting with this repo prior to public release of the contest report, you agree that you have read the Certified Warden docs and agree to be bound by:

To signal your agreement to these terms, add a 👍 emoji to this issue.

Code4rena staff reserves the right to disqualify anyone from this role and similar future opportunities who is unable to participate within the above guidelines.

Disclosures

Sponsors may elect to add team members and contractors to assist in sponsor review and triage. All sponsor representatives added to the repo should comment on this issue to identify themselves.

To ensure contest integrity, the following potential conflicts of interest should also be disclosed with a comment in this issue:

  1. any sponsor staff or sponsor contractors who are also participating as wardens
  2. any wardens hired to assist with sponsor review (and thus presenting sponsor viewpoint on findings)
  3. any wardens who have a relationship with a judge that would typically fall in the category of potential conflict of interest (family, employer, business partner, etc)
  4. any other case where someone might reasonably infer a possible conflict of interest.

implement any access control checks

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDrawFactoryProxy.sol#L8-L21

Vulnerability details

Impact

The contract VRFNFTRandomDrawFactoryProxy has a serious vulnerability: it does not implement any access control checks, such as the onlyOwner modifier. This means that any function can be called by any address, including functions that should only be accessible by the contract owner. An attacker could potentially exploit this to gain unauthorized access to sensitive functions, such as the IVRFNFTRandomDrawFactory.initialize() function.

Proof of Concept

To demonstrate the impact of this vulnerability, we can create a simple proof-of-concept exploit. First, we'll deploy the VRFNFTRandomDrawFactoryProxy contract. Then, we'll call the IVRFNFTRandomDrawFactory.initialize() function from a non-owner account. This should not be possible, but because the contract does not have any access control checks, the function will be executed successfully.

Tools Used

Mythril

Recommended Mitigation Steps

To mitigate this vulnerability, the contract should implement access control checks using the onlyOwner modifier. For example:

contract VRFNFTRandomDrawFactoryProxy is ERC1967Proxy {
constructor(address _logic, address _defaultOwner)
ERC1967Proxy(
_logic,
abi.encodeWithSelector(
IVRFNFTRandomDrawFactory.initialize.selector,
_defaultOwner
)
)
{}

// Only allow the contract owner to call this function
modifier onlyOwner() {
    require(msg.sender == owner);
    _;
}

// Require the onlyOwner modifier for the initialize() function
function initialize(address _defaultOwner) public onlyOwner {
    // Code for the initialize() function
}

}

Owner should not be able to renounce ownership

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/ownable/OwnableUpgradeable.sol#L114-L116

Vulnerability details

Impact

The VRFNFTRandomDraw contract inherits from the OwnableUpgradable contract which gives the contract owner the ability to renounce ownership of the contract as seen here https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/ownable/OwnableUpgradeable.sol#L114-L116.

The VRFNFTRandomDraw contract heavily relies on the owner to perform critical operations including calling startDraw (), redraw() and also lastResortTimelockOwnerClaimNFT () functions. If the ownership of the contract at some point is resigned then these operations would not be possible

Proof of Concept

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/ownable/OwnableUpgradeable.sol#L114-L116

Tools Used

Recommended Mitigation Steps

Consider removing the ability of the owner to renounce ownership via the resignOwnership () function

No storage gap for upgradeable contract might lead to storage slot collision

Lines of code

https://github.com/code-423n4/2022-12-forgeries/tree/main/src/VRFNFTRandomDrawFactoryProxy.sol#L8

Vulnerability details

Description

For upgradeable contracts, there must be storage gap to "allow developers to freely add new state variables in the future without compromising the storage compatibility with existing deployments". Otherwise it may be very difficult to write new implementation code. Without storage gap, the variable in child contract might be overwritten by the upgraded base contract if new variables are added to the base contract. This could have unintended and very serious consequences to the child contracts.

Refer to the bottom part of this article: https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable

Proof Of Concept

However, the contract doesn't contain a storage gap. The storage gap is essential for upgradeable contract because "It allows us to freely add new state variables in the future without compromising the storage compatibility with existing deployments". See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps for a description of this storage variable. While some contracts may not currently be sub-classed, adding the variable now protects against forgetting to add it in the future.

contract VRFNFTRandomDrawFactoryProxy is ERC1967Proxy {

https://github.com/code-423n4/2022-12-forgeries/tree/main/src/VRFNFTRandomDrawFactoryProxy.sol#L8

Recommended Mitigation Steps

Recommend adding appropriate storage gap at the end of upgradeable contracts such as the below. Please reference OpenZeppelin upgradeable contract templates.

    uint256[50] private __gap;

Ownership can be lost

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/ownable/OwnableUpgradeable.sol#L36
https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/ownable/OwnableUpgradeable.sol#L114
https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L173
https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L203
https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L304

Vulnerability details

Impact

No one can recover NFT from the VRFNFTRandomDraw.sol contract, no one can request randomness seed from chainlink

Proof of Concept

The owner plays a fairly important role in the current implementation, as we can see in the VRFNFTRandomDraw.sol

src\VRFNFTRandomDraw.sol:
  172      /// @return chainlink request id
  173:     function startDraw() external onlyOwner returns (uint256) {
  174          // Only can be called on first drawing

  202      /// @dev Only callable by the owner
  203:     function redraw() external onlyOwner returns (uint256) {
  204          if (request.drawTimelock >= block.timestamp) {

  303      /// @dev Only callable by the owner
  304:     function lastResortTimelockOwnerClaimNFT() external onlyOwner {
  305          // If recoverTimelock is not setup, or if not yet occurred

Note that the modifier onlyOwner comes from the contract

OwnableUpgradeable

Which has a function:

/// @notice Resign ownership of contract
/// @dev only callably by the owner, dangerous call.
function resignOwnership() public onlyOwner {
	_transferOwnership(address(0));
}

this function will very unlikely being called in normal circumstances,

If the admin is compromised, the hacker can toy the project and call the resignOwnership to lock NFT in the VRFNFTRandom.sol contract if the winner is not capable of claiming the NFT and external intergration that replies on the new randomness seed comes can fail because no owner can the redraw function to request new randomness seed.

Tools Used

Manual Review

Recommended Mitigation Steps

We recommend the project just remove this funciton to prevent ownership loss.

QA Report

See the markdown file with the details of this report here.

Hardcoded gas limit in VRFNFTRandomDraw.sol

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L22
https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L162

Vulnerability details

Impact

Hardcoded gas limit can revert transaction when fullfilling randomness.

Proof of Concept

the gas limit in the VRFNFTRandomDraw.sol is hardcoded:

/// @notice Our callback is just setting a few variables, 200k should be more than enough gas.
uint32 immutable callbackGasLimit = 200_000;

This variable later is used:

// Request first random round
request.currentChainlinkRequestId = coordinator.requestRandomWords({
	keyHash: settings.keyHash,
	subId: settings.subscriptionId,
	minimumRequestConfirmations: minimumRequestConfirmations,
	callbackGasLimit: callbackGasLimit,
	numWords: wordsRequested
});

However, if we look into the chainlink VRF

https://docs.chain.link/getting-started/intermediates-tutorial/

uint32 callbackGasLimit: The limit for how much gas to use for the callback request to your contract’s fulfillRandomWords function. It must be less than the maxGasLimit on the coordinator contract. Adjust this value for larger requests depending on how your fulfillRandomWords function processes and stores the received random values. If your callbackGasLimit is not sufficient, the callback will fail and your subscription is still charged for the work done to generate your requested random values.

Clearly, hardcoding the gas limit is not a good practice because the gas cost of the transaction can change, in that case, the callback function is not able to complete the call.

Tools Used

Manual Review

Recommended Mitigation Steps

We recommend the project estimate the gas offchain and then pass in the gas limit dynamically to avoid hardcoded gas.

Lack of Access Control in VRFNFTRandomDrawFactory Contract

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDrawFactory.sol#L55-L60

Vulnerability details

Impact

One potential impact of this vulnerability is that an attacker could call the makeNewDraw() function to create a new drawing contract with arbitrary settings, potentially allowing them to manipulate the outcome of the raffle. The attacker could also call the _authorizeUpgrade() function to upgrade the contract to a malicious implementation that they control, allowing them to further exploit the contract.

Proof of Concept

The VRFNFTRandomDrawFactory contract has a critical vulnerability because it does not implement any access control checks, such as the onlyOwner modifier. This means that any function can be called by any address, including functions that should only be accessible by the contract owner. An attacker could potentially exploit this to gain unauthorized access to sensitive functions, such as the IVRFNFTRandomDrawFactory.initialize() function.

Tools Used

Mythril and Oyente

Recommended Mitigation Steps

To mitigate this vulnerability, the onlyOwner modifier should be added to all functions that should only be accessible by the contract owner. This will ensure that only the contract owner can call these functions, preventing unauthorized access. For example, the _authorizeUpgrade() function should be modified as follows:

function _authorizeUpgrade(address newImplementation)
internal override onlyOwner {
// existing code here
}

the initialize() function should also be protected by the onlyOwner modifier to prevent unauthorized access.

QA Report

See the markdown file with the details of this report here.

Usage of transferFrom is discouraged, use safeTransferFrom whenever possible.

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/main/src/VRFNFTRandomDraw.sol#L295
https://github.com/code-423n4/2022-12-forgeries/blob/main/src/VRFNFTRandomDraw.sol#L315
https://github.com/code-423n4/2022-12-forgeries/blob/main/src/VRFNFTRandomDraw.sol#L187

Vulnerability details

Impact

Usage of transferFrom is discouraged, use safeTransferFrom whenever possible.

Proof of Concept

See this document https://immutablesoft.github.io/ImmutableEcosystem/docs/IERC721Upgradeable.html#transferfrom

transferFrom
Transfers tokenId token from from to to. WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. Requirements:

from cannot be the zero address.
to cannot be the zero address.
tokenId token must be owned by from.
If the caller is not from, it must be approved to move this token by either {approve} or {setApprovalForAll}. Emits a {Transfer} event.

There are multiple places using transferFrom, better to use safeTransferFrom instead.

Tools Used

Manual reading

Recommended Mitigation Steps

Use safeTransferFrom whenever possible.

The owner can brick the contest making it to never end.

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L203-L225
https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L141-L168

Vulnerability details

Impact

The contract allows for the owner to redraw () the contest in cases where the winner has not yet claimed the NFT within the specified timelines. This is possible via the redraw () function as shown here: https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L203-L225.

The redraw () function checks that the drawTimelock is passed then deletes the request and calls _requestRoll () function.
The _requestRoll () function only updates the request.drawTimelock as seen here: https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L141-L169.

The recoverTimelock is not updated at all. Therefore, it is very possible that the redraw process updates the .drawBufferTime to be higher than the recoverTimeLock allowing the owner to withdraw the NFT before the redrawal period is finished.

SCENARIO

  1. The contract is initialized and the _settings.drawBufferTime is set to 3 weeks and the _settings.recoverTimelock is set to 1 month.
  2. After 3 weeks, the NFT is not claimed and the owner calls the redraw () function which would update the drawTimelock for further 3 weeks.
  3. After a month which is 4 weeks, the _settings.recoverTimelock period would have elapsed and the owner can now call the lastResortTimelockOwnerClaimNFT() .
  4. When the owner recovers the NFT via the lastResortTimelockOwnerClaimNFT() it will transfer the NFT to them.

Therefore, the contest cannot be redrawn again, neither can the winner claim their NFT. This would mean that the contest can never end.

Proof of Concept

  1. https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L203-L225.
  2. https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L141-L169
  3. https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L304-L320

Tools Used

Recommended Mitigation Steps

Consider updating the recoveryTImelock during redraw

Risk of front-run attack on `VRFNFTRandomDrawFactory.initialize'

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/82a5fc7540249325ae48498720d40d943ee17e27/src/VRFNFTRandomDrawFactory.sol#L31

Vulnerability details

Risk of front-run attack on `VRFNFTRandomDrawFactory.initialize'

The initialize function is external and has no ckecks on who is goint to execute it. So it has a risk of front-run attack where a malicious agent could the the deployed address and imidiately run initialize turning himself a owner, after deploy.

Proof of Concept

VRFNFTRandomDrawFactory.sol#L31

Recommended Mitigation Steps

To avoid that, the initialize function should check, in any appropriate manner for the owner or deployer (something like onlyOwner). Where on the constructor the address of the deployer is stored and then checked on initialize, before executing it.

QA Report

See the markdown file with the details of this report here.

QA Report

See the markdown file with the details of this report here.

Admin privilege - A single point of failure can allow a hacked or malicious owner use critical functions in the project

Lines of code

https://github.com/code-423n4/2022-12-forgeries/tree/main/src/VRFNFTRandomDraw.sol#L173
https://github.com/code-423n4/2022-12-forgeries/tree/main/src/VRFNFTRandomDraw.sol#L203
https://github.com/code-423n4/2022-12-forgeries/tree/main/src/VRFNFTRandomDraw.sol#L304
https://github.com/code-423n4/2022-12-forgeries/tree/main/src/VRFNFTRandomDrawFactory.sol#L55
https://github.com/code-423n4/2022-12-forgeries/tree/main/src/ownable\OwnableUpgradeable.sol#L79
https://github.com/code-423n4/2022-12-forgeries/tree/main/src/ownable\OwnableUpgradeable.sol#L102
https://github.com/code-423n4/2022-12-forgeries/tree/main/src/ownable\OwnableUpgradeable.sol#L114
https://github.com/code-423n4/2022-12-forgeries/tree/main/src/ownable\OwnableUpgradeable.sol#L128

Vulnerability details

Description

The owner role has a single point of failure and onlyOwner can use critical a few functions.

owner role in the project:
Owner is not behind a multisig and changes are not behind a timelock.

Even if protocol admins/developers are not malicious there is still a chance for Owner keys to be stolen. In such a case, the attacker can cause serious damage to the project due to important functions. In such a case, users who have invested in project will suffer high financial losses.

Impact

Hacked owner or malicious owner can immediately use critical functions in the project.

Proof Of Concept

173: function startDraw() external onlyOwner returns (uint256) {

https://github.com/code-423n4/2022-12-forgeries/tree/main/src/VRFNFTRandomDraw.sol#L173

203: function redraw() external onlyOwner returns (uint256) {

https://github.com/code-423n4/2022-12-forgeries/tree/main/src/VRFNFTRandomDraw.sol#L203

304: function lastResortTimelockOwnerClaimNFT() external onlyOwner {

https://github.com/code-423n4/2022-12-forgeries/tree/main/src/VRFNFTRandomDraw.sol#L304

55: function _authorizeUpgrade(address newImplementation)
        internal
        override
        onlyOwner
    {

https://github.com/code-423n4/2022-12-forgeries/tree/main/src/VRFNFTRandomDrawFactory.sol#L55

79: function transferOwnership(address _newOwner)
        public
        notZeroAddress(_newOwner)
        onlyOwner
    {

https://github.com/code-423n4/2022-12-forgeries/tree/main/src/ownable\OwnableUpgradeable.sol#L79

102: function safeTransferOwnership(address _newOwner)
        public
        notZeroAddress(_newOwner)
        onlyOwner
    {

https://github.com/code-423n4/2022-12-forgeries/tree/main/src/ownable\OwnableUpgradeable.sol#L102

114: function resignOwnership() public onlyOwner {

https://github.com/code-423n4/2022-12-forgeries/tree/main/src/ownable\OwnableUpgradeable.sol#L114

128: function cancelOwnershipTransfer() public onlyOwner {

https://github.com/code-423n4/2022-12-forgeries/tree/main/src/ownable\OwnableUpgradeable.sol#L128

Recommended Mitigation Steps

Add a time lock to critical functions. Admin-only functions that change critical parameters should emit events and have timelocks.
Events allow capturing the changed parameters so that off-chain tools/interfaces can register such changes with timelocks that allow users to evaluate them and consider if they would like to engage/exit based on how they perceive the changes as affecting the trustworthiness of the protocol or profitability of the implemented financial services.

Allow only multi-signature wallets to call the function to reduce the likelihood of an attack.

https://twitter.com/danielvf/status/1572963475101556738?s=20&t=V1kvzfJlsx-D2hfnG0OmuQ

Reference

The status quo regarding significant centralization vectors has always been to award M severity, in order to warn users of the protocol of this category of risks. See here for list of centralization issues previously judged.

VRFNFTRandomDraw.sol#FulfillRandomWords should not revert

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L230
https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L242
https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L236

Vulnerability details

Impact

VRFNFTRandomDraw.sol#FulfillRandomWords should not revert but can revert.

Proof of Concept

From the security consideration of the chainlink VRF:

https://docs.chain.link/vrf/v2/security/#fulfillrandomwords-must-not-revert

fulfillRandomWords must not revert
If your fulfillRandomWords() implementation reverts, the VRF service will not attempt to call it a second time. Make sure your contract logic does not revert. Consider simply storing the randomness and taking more complex follow-on actions in separate contract calls made by you, your users, or an Automation Node.

However in the current implementation, there are a lot of case that revert the transaction FulfillRandomWords:

/// @notice Function called by chainlink to resolve random words
/// @param _requestId ID of request sent to chainlink VRF
/// @param _randomWords List of uint256 words of random entropy
function fulfillRandomWords(
	uint256 _requestId,
	uint256[] memory _randomWords
) internal override {
	// Validate request ID
	if (_requestId != request.currentChainlinkRequestId) {
		revert REQUEST_DOES_NOT_MATCH_CURRENT_ID();
	}

	// Validate number of words returned
	// Words requested is an immutable set to 1
	if (_randomWords.length != wordsRequested) {
		revert WRONG_LENGTH_FOR_RANDOM_WORDS();
	}

	// Set request details
	request.hasChosenRandomNumber = true;

	// Get total token range
	uint256 tokenRange = settings.drawingTokenEndId -
		settings.drawingTokenStartId;

	// Store a number from it here (reduce number here to reduce gas usage)
	// We know there will only be 1 word sent at this point.
	request.currentChosenTokenId =
		(_randomWords[0] % tokenRange) +
		settings.drawingTokenStartId;

	// Emit completed event.
	emit DiceRollComplete(msg.sender, request);
}

Tools Used

Manual Review

Recommended Mitigation Steps

We recommend the project just accept the randomness number and store the number and handle the sanity check of the number in another function to make sure the FulfillRandomWords does not revert.

The Owner can interrupt the contest

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L75-L120

Vulnerability details

Impact

When the VRFNFTRandomDraw contract is initialized, two important time related variables are set up:

  1. _settings.drawBufferTime - which is time until a re-drawing can occur if the selected user cannot or does not claim the NFT, essentially the time a user is given to claim the NFT.
  2. _settings.recoverTimelock - which is block.timestamp that the admin can recover the NFT the time that the NFT can be recovered by the owner.

During contract initialization, the _settings.drawBufferTime is restricted to be more than an hour and less than a month as seen here: https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L83-L88.
On the other hand, the _settings.recoverTimelock is restricted to be at least more than a week and less than a year as seen here: https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L90-L98.

As a result, it is possible for the _settings.recoverTimelock to be less than the _settings.drawBufferTime ie the the _settings.drawBufferTime = 3 weeks and _settings.recoverTimelock = 1 week.
The lastResortTimeLockOwnerClaimNFT () function where the owner is allowed to recover the NFT only checks that the _settings.recoverTimelock is reached as seen here:
https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L304-L320.

Therefore, an owner can call the lastResortTimeLockOwnerClaimNFT () function to reclaim the NFT even when the contest has not yet ended affecting the entire contest.

Proof of Concept

  1. https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L75-L120
  2. https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L304-L320.
  3. https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L90-L98.
  4. https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L83-L88.

Tools Used

Recommended Mitigation Steps

Consider adding the following check _settings.recoverTimelock > _settings.drawBufferTime

Usage of transferFrom is discouraged, use safeTransferFrom whenever possible.

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/main/src/VRFNFTRandomDraw.sol#L295
https://github.com/code-423n4/2022-12-forgeries/blob/main/src/VRFNFTRandomDraw.sol#L187
https://github.com/code-423n4/2022-12-forgeries/blob/main/src/VRFNFTRandomDraw.sol#L315

Vulnerability details

Impact

Usage of transferFrom is discouraged, use safeTransferFrom whenever possible.

Proof of Concept

See this document https://immutablesoft.github.io/ImmutableEcosystem/docs/IERC721Upgradeable.html#transferfrom

transferFrom
Transfers tokenId token from from to to. WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. Requirements:

from cannot be the zero address.
to cannot be the zero address.
tokenId token must be owned by from.
If the caller is not from, it must be approved to move this token by either {approve} or {setApprovalForAll}. Emits a {Transfer} event.

For safeTransferFrom, if to refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. So we'd better to use this safeTransferFrom.

Tools Used

Manual reading

Recommended Mitigation Steps

Use safeTransferFrom whenever possible.

QA Report

See the markdown file with the details of this report here.

loss of control over the contract

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDrawFactory.sol#L14-L35

Vulnerability details

One potential vulnerability that has been identified in this contract is that the initialize() function, which sets the contract's initial owner, is marked as initializer and can therefore only be called once, during contract deployment. This means that if the contract is deployed with the wrong initial owner, there is no way to change it later. This could potentially lead to loss of control over the contract if the initial owner loses access to their account or is malicious.

Impact

If the contract is deployed with an incorrect initial owner, it could be very difficult or even impossible to regain control of the contract. This could result in significant losses, as the contract may hold significant value in the form of NFTs or other assets.

Proof of Concept

To demonstrate this vulnerability, one could deploy the contract with an incorrect initial owner and then attempt to call functions that are marked onlyOwner. These functions should revert, indicating that the incorrect initial owner does not have the necessary privileges to execute them.

Tools Used

Mythril or Oyente

Recommended Mitigation Steps

To address this vulnerability, the 'initialize()' function should be removed and a new '_initialize()' function should be added that is not marked 'initializer'. This function can then be called by the contract creator to set the initial owner during contract deployment, but can also be called later if necessary to change the initial owner. This will allow the contract owner to be changed if necessary, mitigating the risk of loss of control over the contract.

Owner can require randomness seed multiple times before the previous random request is settled.

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L173
https://github.com/code-423n4/2022-12-forgeries/blob/fc271cf20c05ce857d967728edfb368c58881d85/src/VRFNFTRandomDraw.sol#L203

Vulnerability details

Impact

Owner can require randomness seed multiple times before the previous random request is settled.

Proof of Concept

From the chainlink VRF security consideration:

https://docs.chain.link/vrf/v2/security/#do-not-re-request-randomness-even-if-you-dont-get-an-answer-right-away

Doing so would give the VRF service provider the option to withhold a VRF fulfillment, if it doesn’t like the outcome, and wait for the re-request in the hopes that it gets a better outcome, similar to the considerations with block confirmation time.

it may take some time (more than the buffer time) for the randomness seedness callback to settle,

in the current implementation, the owner can call startDraw to request a randomness seed.

then request.drawTimelock is set:

// Setup re-draw timelock
request.drawTimelock = block.timestamp + settings.drawBufferTime;

after the drawTimelock, the ower can call the redraw again

function redraw() external onlyOwner returns (uint256) {
	if (request.drawTimelock >= block.timestamp) {
	revert TOO_SOON_TO_REDRAW();
	
	 // Reset request
	delete request;

	// Re-roll
	_requestRoll();
}

but it is possible that request A is out, and the randomness requiest is pending and exceed buffer period, the owner can redraw() and clean the old request state and issue a new randomness request, at this time the old request A attemps to call fulfillRandomWords, which can revert in _requestId != request.currentChainlinkRequestId

/// @notice Function called by chainlink to resolve random words
/// @param _requestId ID of request sent to chainlink VRF
/// @param _randomWords List of uint256 words of random entropy
function fulfillRandomWords(
	uint256 _requestId,
	uint256[] memory _randomWords
) internal override {
	// Validate request ID
	if (_requestId != request.currentChainlinkRequestId) {
		revert REQUEST_DOES_NOT_MATCH_CURRENT_ID();
	}

not only the owner's multiple require of randomness violates the security consideration: do-not-re-request-randomness-even-if-you-dont-get-an-answer-right-away, it also violates the fullfillRandomWords callback must not revert (submitted a seperate issue)

Tools Used

Manual Review

Recommended Mitigation Steps

We recommend the project rethink the design and not let owner issue multiple randomness request before the previous one is settled.

OwnableUpgradeable contract may leads to insecure set of owner

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/main/src/ownable/OwnableUpgradeable.sol#L18

Vulnerability details

Impact

the OwnableUpgradeable contract sets the owner and pending owner at slot0 and slot1, when any other contract inherits from it and set an implementation, the implementation if there's stored addresses are slot0 and slot1 those can collides with the owner and pendingowner of OwnableUpgradeable contract

Proof of Concept

see "" :

abstract contract OwnableUpgradeable is IOwnableUpgradeable, Initializable {
    ///                                                          ///
    ///                            STORAGE                       ///
    ///                                                          ///

    /// @dev The address of the owner
    address internal _owner;

    /// @dev The address of the pending owner
    address internal _pendingOwner;

deploy this contract :

contract test is OwnableUpgradeable {
  // some upgradable logics

contract example  {
  address bidder;
  // some other logice, anyone can become bidder

test is test of OwnableUpgradeable clone, Set the example contract as implementation for test, in the example set yourself as bidder
in the test make a call to the implimentation, you'll see the owner has changed to msg.sender

Tools Used

Manual

Recommended Mitigation Steps

Recommend adding appropriate storage gap on the OwnableUpgradeable contract

VRFNFTRandomDraw contract can have issues to work properly

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/main/src/VRFNFTRandomDraw.sol#L75
https://github.com/code-423n4/2022-12-forgeries/blob/main/src/VRFNFTRandomDraw.sol#L173

Vulnerability details

Impact

if admin of nft initialize the function then sells the nft or disapprove the VRFNFTRandomDraw contract ,the VRFNFTRandomDraw contract will not be used anyore as startDraw and reDraw will always reverts and no one will be able to ClaimNFT

Proof of Concept

The contract can make a Draw logic by the initialize function, the caller gives the setting tuple and the admin address, which must be the owner of _settings.tokenId (see 126,134 lines) if all set the __Ownable_init(admin); call will succeed and the draw settings will be set, however it does not move that nft from the admin yet !, if the admin sells that nft or borrow it ...etc he will not be able to call startDraw() and reDraw() function anymore :

 // Attempt to transfer token into this address
        try
            IERC721EnumerableUpgradeable(settings.token).transferFrom(
                msg.sender,
                address(this),
                settings.tokenId
            )
        {} catch {
            revert TOKEN_NEEDS_TO_BE_APPROVED_TO_CONTRACT();
        }

then the whole contract will be useless and not able to proceed for users to participate and win

Tools Used

Manual

Recommended Mitigation Steps

Call the startDraw function on the initialize() function, or make the NFT transfer to address(this) at the initialize() function

QA Report

See the markdown file with the details of this report here.

reentrancy attacks

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/main/src/VRFNFTRandomDrawFactoryProxy.sol#L8-L12

Vulnerability details

Impact

the contract uses the abi.encodeWithSelector() function to encode the IVRFNFTRandomDrawFactory.initialize() function call when calling the ERC1967Proxy constructor. However, this function is not checked for reentrancy, which means it could be vulnerable to reentrancy attacks. An attacker could exploit this vulnerability to call the IVRFNFTRandomDrawFactory.initialize() function multiple times, potentially allowing them to set themselves as the owner of the underlying contract multiple times.

Proof of Concept

the contract uses the abi.encodeWithSelector() function to encode the IVRFNFTRandomDrawFactory.initialize() function call when calling the ERC1967Proxy constructor. However, this function is not checked for reentrancy, which means it could be vulnerable to reentrancy attacks. An attacker could exploit this vulnerability to call the IVRFNFTRandomDrawFactory.initialize() function multiple times, potentially allowing them to set themselves as the owner of the underlying contract multiple times.

Tools Used

Mythril can be used to perform static analysis on the contract and identify potential vulnerabilities, such as the lack of internal or private visibility for the IVRFNFTRandomDrawFactory.initialize() function and the potential for reentrancy attacks.

Oyente,

Recommended Mitigation Steps

it is recommended to mark the IVRFNFTRandomDrawFactory.initialize() function as internal or private to prevent unauthorized access. Additionally, the contract should implement proper access control checks, such as the onlyOwner modifier, to prevent unauthorized access to sensitive functions. Finally, the abi.encodeWithSelector() function should be checked for reentrancy to prevent reentrancy attacks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.