Skip to content

Commit

Permalink
remove reentry attack poc
Browse files Browse the repository at this point in the history
  • Loading branch information
chappjc committed Feb 8, 2023
1 parent 17e757f commit 132aefd
Show file tree
Hide file tree
Showing 5 changed files with 9 additions and 847 deletions.
182 changes: 0 additions & 182 deletions client/asset/eth/nodeclient_harness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import (
"decred.org/dcrdex/dex/encode"
dexeth "decred.org/dcrdex/dex/networks/eth"
swapv0 "decred.org/dcrdex/dex/networks/eth/contracts/v0"
"decred.org/dcrdex/dex/testing/eth/reentryattack"
"github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
Expand Down Expand Up @@ -2189,187 +2188,6 @@ func testApproveGas(t *testing.T) {
fmt.Printf("=========== gas for approve: %d ==============\n", gas)
}

func TestReplayAttack(t *testing.T) {
cl, is := ethClient.(*nodeClient)
if !is {
t.Skip("TestReplayAttack not configured for RPC")
}
err := cl.unlock(pw)
if err != nil {
t.Fatal(err)
}

txOpts, err := cl.txOpts(ctx, 1, defaultSendGasLimit*5, nil, nil)
if err != nil {
t.Fatalf("txOpts error: %v", err)
}

cl.addSignerToOpts(txOpts)

// Deploy the reentry attack contract.
_, _, reentryContract, err := reentryattack.DeployReentryAttack(txOpts, cl.ec)
if err != nil {
t.Fatal(err)
}
if err := waitForMined(10, false); err != nil {
t.Fatal(err)
}

originalContractBal, err := cl.addressBalance(ctx, ethSwapContractAddr)
if err != nil {
t.Fatal(err)
}

var secretHash [32]byte
// Make four swaps that should be locked and refundable and one that is
// soon refundable.
for i := 0; i < 5; i++ {
var secret [32]byte
copy(secret[:], encode.RandomBytes(32))
secretHash = sha256.Sum256(secret[:])

if i != 4 {
inLocktime := uint64(time.Now().Add(time.Hour).Unix())

txOpts, err := cl.txOpts(ctx, 1, ethGases.SwapN(1), nil, nil)
if err != nil {
t.Fatalf("txOpts error: %v", err)
}
_, err = simnetContractor.initiate(txOpts, []*asset.Contract{newContract(inLocktime, secretHash, 1)})
if err != nil {
t.Fatalf("unable to initiate swap: %v ", err)
}

if err := waitForMined(10, false); err != nil {
t.Fatal(err)
}
continue
}

intermediateContractVal, _ := cl.addressBalance(ctx, ethSwapContractAddr)
t.Logf("intermediate contract value %d", dexeth.WeiToGwei(intermediateContractVal))

inLocktime := time.Now().Add(-1 * time.Second).Unix()
// Set some variables in the contract used for the exploit. This
// will fail (silently) due to require(msg.origin == msg.sender)
// in the real contract.
txOpts, err := cl.txOpts(ctx, 1, defaultSendGasLimit*5, nil, nil)
if err != nil {
t.Fatalf("txOpts error: %v", err)
}
_, err = reentryContract.SetUsUpTheBomb(txOpts, ethSwapContractAddr, secretHash, big.NewInt(inLocktime), participantAddr)
if err != nil {
t.Fatalf("unable to set up the bomb: %v", err)
}
if err = waitForMined(10, false); err != nil {
t.Fatal(err)
}
}

txOpts, err = cl.txOpts(ctx, 1, defaultSendGasLimit*5, nil, nil)
if err != nil {
t.Fatalf("txOpts error: %v", err)
}
txOpts.Value = nil
// Siphon funds into the contract.
tx, err := reentryContract.AllYourBase(txOpts)
if err != nil {
t.Fatalf("unable to get all your base: %v", err)
}
spew.Dump(tx)
if err = waitForMined(10, false); err != nil {
t.Fatal(err)
}
receipt, err := waitForReceipt(ethClient, tx)
if err != nil {
t.Fatal(err)
}
spew.Dump(receipt)

if err = waitForMined(10, false); err != nil {
t.Fatal(err)
}

originalAcctBal, err := cl.addressBalance(ctx, simnetAddr)
if err != nil {
t.Fatal(err)
}

// Send the siphoned funds to us.
txOpts, err = cl.txOpts(ctx, 1, defaultSendGasLimit*5, nil, nil)
if err != nil {
t.Fatalf("txOpts error: %v", err)
}
tx, err = reentryContract.AreBelongToUs(txOpts)
if err != nil {
t.Fatalf("unable to are belong to us: %v", err)
}
if err = waitForMined(10, false); err != nil {
t.Fatal(err)
}
receipt, err = waitForReceipt(ethClient, tx)
if err != nil {
t.Fatal(err)
}

gasPrice, err := feesAtBlk(ctx, cl, receipt.BlockNumber.Int64())
if err != nil {
t.Fatalf("feesAtBlk error: %v", err)
}
bigGasUsed := new(big.Int).SetUint64(receipt.GasUsed)
txFee := new(big.Int).Mul(bigGasUsed, gasPrice)
wantBal := new(big.Int).Sub(originalAcctBal, txFee)

acctBal, err := cl.addressBalance(ctx, simnetAddr)
if err != nil {
t.Fatal(err)
}

// If the exploit worked, the test will fail here, with 4 ether we
// shouldn't be able to touch drained from the contract.
delta := new(big.Int).Sub(originalAcctBal, acctBal)
wantDelta := new(big.Int).Sub(originalAcctBal, wantBal)
diff := new(big.Int).Abs(new(big.Int).Sub(wantDelta, delta))
if dexeth.WeiToGwei(diff) > receipt.GasUsed { // See TestContract notes.
delta := new(big.Int).Sub(originalAcctBal, acctBal)
wantDelta := new(big.Int).Sub(originalAcctBal, wantBal)
diff := new(big.Int).Sub(wantDelta, delta)
t.Logf("unexpected balance change of account. original = %d, final = %d, expected %d",
dexeth.WeiToGwei(originalAcctBal), dexeth.WeiToGwei(acctBal), dexeth.WeiToGwei(wantBal))
t.Fatalf("actual change = %d, expected change = %d, a difference of %d",
dexeth.WeiToGwei(delta), dexeth.WeiToGwei(wantDelta), dexeth.WeiToGwei(diff))
}

// The exploit failed and status should be SSNone because initiation also
// failed.
swap, err := simnetContractor.swap(ctx, secretHash)
if err != nil {
t.Fatal(err)
}
state := dexeth.SwapStep(swap.State)
if state != dexeth.SSNone {
t.Fatalf("unexpected swap state: want %s got %s", dexeth.SSNone, state)
}

// The contract should hold four more ether because initiation of one
// swap failed.
contractBal, err := cl.addressBalance(ctx, ethSwapContractAddr)
if err != nil {
t.Fatal(err)
}

wantBal = new(big.Int).Add(originalContractBal, dexeth.GweiToWei(4))
balDiff := new(big.Int).Abs(new(big.Int).Sub(contractBal, wantBal))
if dexeth.WeiToGwei(balDiff) > receipt.GasUsed {
wantDiff := new(big.Int).Sub(originalContractBal, wantBal)
actualDiff := new(big.Int).Sub(originalContractBal, contractBal)
t.Logf("balance before = %d, expected balance after = %d, actual balance after = %d",
dexeth.WeiToGwei(originalContractBal), dexeth.WeiToGwei(wantBal), dexeth.WeiToGwei(contractBal))
t.Fatalf("wanted diff = %d, actual diff = %d, a difference of %d",
dexeth.WeiToGwei(wantDiff), dexeth.WeiToGwei(actualDiff), dexeth.WeiToGwei(balDiff))
}
}

// This test can be used to test that the resubmission of ETH redemptions after
// they have been overridden by another transaction works properly. Just replace
// the app seed and nonce. Also uncomment the lines in the function which will
Expand Down
10 changes: 9 additions & 1 deletion dex/testing/eth/reentryattack/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
## Reentry Contract Creation
## Reentry Attack Proof-of-Concept and Avoidance

The proof-of-concept Solidity contracts are no longer in the repository, but
they may be found at the following past revisions:

- https://github.com/decred/dcrdex/blob/5632a241faaa1d0ef25505731284337cbd29096d/dex/testing/eth/reentryattack/ReentryAttack.sol
- https://github.com/decred/dcrdex/blob/5632a241faaa1d0ef25505731284337cbd29096d/dex/testing/eth/reentryattack/VulnerableToReentryAttack.sol

## Contract Creation

Have `solc` and `abigen` installed on your system and run from this directory:

Expand Down
62 changes: 0 additions & 62 deletions dex/testing/eth/reentryattack/ReentryAttack.sol

This file was deleted.

92 changes: 0 additions & 92 deletions dex/testing/eth/reentryattack/VulnerableToReentryAttack.sol

This file was deleted.

Loading

0 comments on commit 132aefd

Please sign in to comment.