Workshop big update based on feedback from day 1

main
six 2022-10-06 22:59:37 +02:00
parent 596e8dec08
commit bca95b66e0
9 changed files with 148 additions and 45 deletions

View File

@ -5,9 +5,15 @@ Workshop for "web3" bridge hacking at Hacktivity 2022
#### Introduction #### Introduction
- Web3 vs web2 hacking, concepts / workshop topology - Web3 vs web2 hacking, concepts / workshop topology
- Who interacted with dApps/SCs before?
- Who codes Solidity?
- Who codes Rust?
- Who used a bridge before?
- Who is the cryptographer?
#### Environment setup, system requirements #### Environment setup, system requirements
- Any browser for Ethereum, Remix - Any browser for Ethereum, Remix
- Python3
- Substrate, Rust nightly - Substrate, Rust nightly
#### Scenario 1: Token on two chains, mint using receipt #### Scenario 1: Token on two chains, mint using receipt
@ -19,8 +25,9 @@ Workshop for "web3" bridge hacking at Hacktivity 2022
#### Scenario 2: Signature forgery (any chain) #### Scenario 2: Signature forgery (any chain)
- Deploy SC on Ethereum chain - Deploy SC on Ethereum chain
- Compile Substrate with EVM - Compile Substrate with EVM
- Deploy SC - Deploy SC on Substrate chain (so it is different from core)
- Test ECDSA signature forgery exploit from one to other - Test ECDSA signature forgery exploit from one to other
- Test same issue with WASM/ink!
## Resources ## Resources
@ -32,6 +39,7 @@ https://ethereum.org/en/developers/docs/standards/tokens/erc-20/
https://git.hsbp.org/six/eth_keygen https://git.hsbp.org/six/eth_keygen
#### Scenario 2 #### Scenario 2
https://cryptoctf.org/2022/09/11/writeup-of-flag-submission-forgery-by-si/
https://github.com/paritytech/substrate-contracts-node https://github.com/paritytech/substrate-contracts-node
https://docs.substrate.io/quick-start/ https://docs.substrate.io/quick-start/
https://github.com/substrate-developer-hub/substrate-front-end-template https://github.com/substrate-developer-hub/substrate-front-end-template
@ -39,4 +47,18 @@ https://github.com/paritytech/ink
https://github.com/paritytech/substrate/blob/master/primitives/core/src/ecdsa.rs https://github.com/paritytech/substrate/blob/master/primitives/core/src/ecdsa.rs
https://use.ink/getting-started/setup https://use.ink/getting-started/setup
https://medium.com/block-journal/introducing-substrate-smart-contracts-with-ink-d486289e2b59 https://medium.com/block-journal/introducing-substrate-smart-contracts-with-ink-d486289e2b59
https://github.com/paritytech/contracts-ui
https://contracts-ui.substrate.io/?rpc=wss://rpc.shibuya.astar.network
https://substrate.io/developers/playground/ https://substrate.io/developers/playground/
https://security.stackexchange.com/questions/200682/is-it-possible-to-fake-ecdsa-signatures
## Solidity Hacking Homework
1. Crypto Wojak - Who is the admin?
2. Sminem - Set the password
3. Crypto Wojak - Make your tries count 2 or more
4. Crypto Wojak - Make your tries count 2 and get the answer
5. HODLer - Deposit ether twice with the same address
6. Crypto Wojak - Execute selfdestruct()
+1 Sminem - Create a signature to prove you are Satoshi (see js folder)

View File

@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2022-10-06T20:00:39.755Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36" etag="bC8fl9ZiBndQcjIvywAN" version="20.4.0" type="device"><diagram id="fPFYDeu9YJaB2W5Fxku6" name="Page-1">7VfbcpswEP0aHp3hEhz7Mb6kyTSepnEmaZ4yMsigjkBUCF/69V2BBKiYNO4lk077YrNHq5W0e/YILG+a7N5xlMULFmJquXa4s7yZ5brj0xH8SmBfAb47roCIk7CCnAZYkq9YgbZCCxLi3HAUjFFBMhMMWJriQBgY4pxtTbc1o+aqGYpwB1gGiHbRBxKKuEJHvt3gl5hEsV7ZsdVIgrSzAvIYhWzbgry55U05Y6J6SnZTTGXudF6qeRc9o/XGOE7FSyZc5Veb2SD/8PgxDO5D7C0G2fvBqdqb2OsD4xDOr0zGRcwiliI6b9AJZ0UaYhnVBqvxuWYsA9AB8DMWYq+KiQrBAIpFQtUo3hHxSU4/8ZX12BqZ7VTk0thrIxV8X01yfW0/tgebeaWlJ+YCcXEumQBAQFGek0DDF4TqPVV5kIfvTa+CclbwAD+TU01TxCMsnvHzahJA82CWYNg2zOOYIkE25j6QonFU+6mpcDK0bzlkjKQib0W+kQA4qIZ0NXP3pn3R4++Nvef84aHagbZaR2mgkolHsNJRa24QLVQeLu/ubix3SCGhkxU3KDv8UsgemqxZKgZ5SbpzcHDcbFcWVo/DUyT/H5bLg6y/RivQLoOpiJIolbwBEmAOwAZzQUAdztVAQsKwagoMK6NVGU/STtUBgvsTy5+p7amWcNyacTIg3h2SMhWsEZA2F/ubuUsoFd0+8Ya2YxRz4FbmcZTrcGTgHQ6rI7D1OodOMGnze4jS4UnNEV3tuYgxx0UCbiuUg2xBgmNEUu0I69a+bYL1IfPbqSyIvUxAQOB/CmXlCG6ejucCkvT0QET8dIsDTDLRZR2lcI9J+mxjIvAyQ6WwbOEmNXnYy5WOPvWW3xmajX+mzG1zqzkjhcXtG+3U7ueDUc9ji+f/hXfP2Ru/e9xXunt+qfCO+1/ef0Le/R/Iu+MOR39C3h3ffAN4LXHvsqQj7stilYP6Ctyn7o0ab0GIYXR+v/jXlN/33pryd1/vOpWdwNcg6OGLSlUn2c4428D3In9JOS/hWHA2mQhEqaSwzVLJI1YSpaRRftKpXHMDOa9SPe/7F/bxgfKdHSqfc3z5wGw+SKs+br7qvfk3</diagram></mxfile>

BIN
docs/topology.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

72
js/i_am_satoshi.js 100644
View File

@ -0,0 +1,72 @@
'use strict';
// original: https://gist.github.com/indutny/8d0f5376ee643962a9f0
// npm install --save bn.js
// npm install elliptic
// npm install bcoin
// npm install secp256k1
const BN = require('bn.js');
const elliptic = require('elliptic');
const bcoin = require('bcoin');
const ecdsa = new elliptic.ec('secp256k1');
let message = new BN(
'7a05c6145f10101e9d6325494245adf1297d80f8f38d4d576d57cdba220bcb19', 'hex');
var key = new Buffer('0411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3', 'hex');
var sig = '304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09';
// const signature = new bcoin.ecdsa.signature(new Buffer(sig, 'hex'));
// console.log(signature);
var signature = {
r: new BN('4e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41', 'hex'),
s: new BN('181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d09', 'hex')
};
const point = ecdsa.curve.pointFromX(signature.r);
point.precompute(256);
function trick(message, signature, i) {
const n = new BN(
'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16);
const p = new BN(
'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', 16);
const nRed = BN.red(n);
const pRed = BN.red(p);
// NOTE: Could be using GLV values for speed
let lambda = new BN(i);
const point2 = point.mul(lambda);
let beta = point2.x.redMul(point.x.redInvm()).fromRed();
lambda = lambda.toRed(nRed);
beta = beta.toRed(pRed);
// NOTE end
const originalR = signature.r;
const r = originalR.toRed(pRed).redMul(beta).fromRed();
const nBeta = r.toRed(nRed).redMul(originalR.toRed(nRed).redInvm());
const common = lambda.redInvm().redMul(nBeta);
const s = signature.s.toRed(nRed).redMul(common).fromRed();
return {
signature: { r: r, s: s },
message: message.toRed(nRed).redMul(nBeta).fromRed()
};
}
for (let i = 2; i < 100; i++) {
const item = trick(message, signature, i);
console.log(JSON.stringify([
new Buffer(item.message.toArray()).toString('hex'),
new Buffer(new bcoin.ecdsa.signature(item.signature).toDER()).toString('hex')
]) + ',');
// ecdsa.verify(item.message, item.signature, key)
}

View File

@ -1,7 +1,6 @@
#!/usr/bin/python3 #!/usr/bin/python3
# Author: six # Author: six
# Made for: CCTF during BsidesBUD 2022 # Made for: CCTF during BsidesBUD 2022 -> Updated for Hacktivity 2022 web3 hacking Workshop
# require(owner() == ecrecover(keccak256(abi.encodePacked(this, tokenId)), v, r, s), "Should be signed correctly"); # require(owner() == ecrecover(keccak256(abi.encodePacked(this, tokenId)), v, r, s), "Should be signed correctly");
# https://privatekeys.pw/keys/ethereum/1 # https://privatekeys.pw/keys/ethereum/1
@ -10,20 +9,14 @@ from web3.auto import w3
from eth_account.messages import encode_defunct from eth_account.messages import encode_defunct
from flask import Flask, request from flask import Flask, request
from base64 import b64encode
app = Flask(__name__) app = Flask(__name__)
b64 = '''<html><body><pre>Lets play a game... what is the private key if: html = '''<html><body><pre>Use /get_a_receipt
SignedMessage(messageHash=HexBytes('0xd65fc3b188dd92cfcb2a193a50840c1b782030fb06c5eee3125dadc48b9042ee'), r=93061353422229139783272046072373682100846510432479107335894930000050943813187, s=18897300799783892124841480819482900155126442998358954848148962214377508383077, v=28, signature=HexBytes('0xcdbedc050cdf4e1a236535365e1563312905fc47aa8238a8ab06decfbb31e64329c77e43945949494800d0c432d037867ea10aad935abf98c63ae2befaf929651c'))
If you send the correct private key as argument to /verify?p=<ARG> in HEX format (without 0x), then you will get the CCTF flag which lets you in to the next yacht event + swag at BsidesBUD.
</pre></body></html>''' </pre></body></html>'''
@app.route('/') @app.route('/')
def hello(): def hello():
return b64 return html
@app.route('/verify', methods=['GET']) @app.route('/verify', methods=['GET'])
def search(): def search():

View File

@ -1,5 +1,5 @@
#!/usr/bin/python3 #!/usr/bin/python3
# Eth ECDSA key generator, signer and arg recovery for web3 hacking # Eth ECDSA key generator, signer and arg recovery for web3 hacking // Modified for Hacktivity workshop
# Author: six # Author: six
# References: https://web3py.readthedocs.io/en/stable/web3.eth.account.html?highlight=sign#sign-a-message # References: https://web3py.readthedocs.io/en/stable/web3.eth.account.html?highlight=sign#sign-a-message
# Ethereum Private Keys Directory -> https://privatekeys.pw/keys/ethereum/1 # Ethereum Private Keys Directory -> https://privatekeys.pw/keys/ethereum/1
@ -84,7 +84,7 @@ parser.add_argument("-s", "--sol", help="Print solidity code for ecrecover", act
args = parser.parse_args() args = parser.parse_args()
if args.dgen: if args.dgen:
forv = generate("Anything","0000000000000000000000000000000000000000000000000000000000000001") # hex|0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf forv = generate("Anything","0000000000000000000000000000000000000000000000000000000000000006") # hex|0xE57bFE9F44b819898F47BF37E5AF72a0783e1141
gen_vrs(forv) gen_vrs(forv)
sys.exit() sys.exit()
if args.rgen: if args.rgen:
@ -107,6 +107,6 @@ if args.sol:
print_sol_recovery() print_sol_recovery()
sys.exit() sys.exit()
forv = generate("Anything","0000000000000000000000000000000000000000000000000000000000000001") # hex|0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf forv = generate("Anything","0000000000000000000000000000000000000000000000000000000000000006") # hex|0xE57bFE9F44b819898F47BF37E5AF72a0783e1141
gen_vrs(forv) gen_vrs(forv)
print("\nNo arguments were provided. You can use --help.") print("\nNo arguments were provided. You can use --help.")

View File

@ -1,5 +1,8 @@
#!/usr/bin/python3 #!/usr/bin/python3
# Author: SI # Author: SI
# Reference 1: https://cryptoctf.org/2022/09/11/writeup-of-flag-submission-forgery-by-si/
# Reference 2: https://polygonscan.com/address/0x36a1424da63a50627863d8f65c0669da7347814a
# Reference 3: https://gist.github.com/chjj/4fe8f5b2b489e89e6ed4
from eth_account.account import to_standard_signature_bytes from eth_account.account import to_standard_signature_bytes
from eth_keys import keys from eth_keys import keys
@ -33,8 +36,10 @@ def forge(public_key, a = 0, b = 1):
return '0x' + to_bytes(z).rjust(32, b'\0').hex(), '0x' + eth_signature_bytes.hex() return '0x' + to_bytes(z).rjust(32, b'\0').hex(), '0x' + eth_signature_bytes.hex()
hsh = '0xe50051a0af89748fe098cef3b163b6dc586a664e726791bb2a582ad364f42683' #hsh = '0xe50051a0af89748fe098cef3b163b6dc586a664e726791bb2a582ad364f42683'
sig = '0x2bdbc1826efc039719a28a9f4dbab9f4a2692d83de478300261a0e49019b63ee67c202ecc4ebdf82693da47824ac4fcf21f793400d85696034c4de9537c6ce491b' #sig = '0x2bdbc1826efc039719a28a9f4dbab9f4a2692d83de478300261a0e49019b63ee67c202ecc4ebdf82693da47824ac4fcf21f793400d85696034c4de9537c6ce491b'
hsh = '0xbb272d3dc886fccf69f92cd7cb622501c02627c045fb38053f78af2dca68e188'
sig = '0x30410a2d097af2b27ba3d789ce151c6aed0590f71b4c7b67d4ae91f56659f2297c3a2f8155d9fb9d30682e599b31012b51cb0928578e97f2f3f0c306597d2eec1c'
pub = recover_public_key(hsh, sig) pub = recover_public_key(hsh, sig)
addr = pub.to_checksum_address() addr = pub.to_checksum_address()

View File

@ -1,9 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// Challenges inspired by CCTF // Challenges inspired by CCTF
pragma solidity ^0.8.17; pragma solidity ^0.8.17;
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
contract SafuDotERC20 is AccessControlUpgradeable { contract SafuDotERC20 {
uint256 public max_supply; uint256 public max_supply;
mapping (address => uint256) internal amountToAddress; mapping (address => uint256) internal amountToAddress;
@ -18,7 +17,7 @@ contract SafuDotERC20 is AccessControlUpgradeable {
mapping(address => uint256) public calls; mapping(address => uint256) public calls;
mapping(address => uint256) public tries; mapping(address => uint256) public tries;
//bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); address public bridge;
mapping(address => bool) public minter; mapping(address => bool) public minter;
// Homework 1 // Homework 1
@ -29,8 +28,10 @@ contract SafuDotERC20 is AccessControlUpgradeable {
max_supply = 1000000; max_supply = 1000000;
amountToAddress[msg.sender] = max_supply - 100000; amountToAddress[msg.sender] = max_supply - 100000;
amountOfBridge = max_supply - amountToAddress[msg.sender]; bridge = 0xE57bFE9F44b819898F47BF37E5AF72a0783e1141;
amountToAddress[bridge] = max_supply - amountToAddress[msg.sender];
mint_amount = 10000; mint_amount = 10000;
minter[msg.sender] = true; minter[msg.sender] = true;
} }
@ -38,33 +39,38 @@ contract SafuDotERC20 is AccessControlUpgradeable {
amountToAddress[msg.sender] = amountToAddress[msg.sender] + mint_amount; amountToAddress[msg.sender] = amountToAddress[msg.sender] + mint_amount;
} }
function mintWithReceipt( function mintWithReceipt(bytes32 _message, bytes memory _signature) public {
address recipient, require(recoverSigner(_message, _signature) == bridge, "Not signed with the correct key.");
uint256 amount, //require(minter[msg.sender] == true, "Not minter");
uint256 uuid,
uint8 v,
bytes32 r,
bytes32 s
) public {
bytes32 payloadHash = keccak256(abi.encode(recipient, amount, uuid));
bytes32 hash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash));
//require(!_receipts[hash], "Receipt already used"); // Dumb without it. //require(!_receipts[hash], "Receipt already used"); // Dumb without it.
_checkSignature(hash, v, r, s); amountToAddress[bridge] = amountToAddress[bridge] - mint_amount;
require(amountToAddress[bridge] >= 0);
mint(); mint();
//_receipts[hash] = true; //_receipts[hash] = true;
} }
function _checkSignature( function recoverSigner(bytes32 _ethSignedMessageHash, bytes memory _signature) public pure returns (address) {
bytes32 hash, (bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);
uint8 v, return ecrecover(_ethSignedMessageHash, v, r, s);
bytes32 r,
bytes32 s
) internal view {
address signer = ecrecover(hash, v, r, s);
require(minter[signer] == true, "Not minter");
} }
function splitSignature(bytes memory sig) public pure returns (bytes32 r, bytes32 s, uint8 v){
require(sig.length == 65, "Invalid signature length");
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := byte(0, mload(add(sig, 96)))
}
}
function myBalance() external view returns (uint256) {
return amountToAddress[msg.sender];
}
function othersBalance(address _other) external view returns (uint256) {
return amountToAddress[_other];
}
function transfer(uint256 _value, address _toAddress) external { function transfer(uint256 _value, address _toAddress) external {
require(amountToAddress[msg.sender] - _value >= 0, 'Oooops'); require(amountToAddress[msg.sender] - _value >= 0, 'Oooops');
@ -77,7 +83,6 @@ contract SafuDotERC20 is AccessControlUpgradeable {
return sent; return sent;
} }
// Easypeasy // Easypeasy
function adminChange(address _newAdmin) external returns (bool) { function adminChange(address _newAdmin) external returns (bool) {
admin = _newAdmin; admin = _newAdmin;
@ -90,7 +95,7 @@ contract SafuDotERC20 is AccessControlUpgradeable {
correct_password = _password; correct_password = _password;
} }
// Homework 5 - Final // Homework 6 - Final
function su1c1d3(address payable _addr, string memory _password) external { function su1c1d3(address payable _addr, string memory _password) external {
require(msg.sender == admin, 'You are not the central admin!'); require(msg.sender == admin, 'You are not the central admin!');
require(keccak256(abi.encodePacked(correct_password)) == keccak256(abi.encodePacked(_password)), 'Very sekur.'); require(keccak256(abi.encodePacked(correct_password)) == keccak256(abi.encodePacked(_password)), 'Very sekur.');
@ -113,7 +118,12 @@ contract SafuDotERC20 is AccessControlUpgradeable {
return answer; return answer;
} }
function deposit() public payable {} // Homework 5
fallback() external payable {} mapping(address => bool) address_contributed;
receive() external payable {} function deposit() public payable {
require(address_contributed[msg.sender] != true, "Not working.");
address_contributed[msg.sender] = true;
}
fallback() external {}
//receive() external {}
} }