Obviously collisions in a hash function like
keccak256 are practically impossible. Would never claim otherwise. However, the issue here is most of the execTransaction calldata space is fairly fixed from one transaction to the other. Here's the first one (the hack TX `calldata`):
solidity
0x6a76120200000000000000000000000096221423681a6d52e184d440a8efcebb105c7242000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000b2b2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000bdd077f651ebe7f7b3ce16fe5f2b025be296951600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Here's a legitimate transaction on the same proxy for a call (using the same function;
execTransaction`) with the same nested `transferToken call embedded within it. The biggest differences here are the recipient of the execTransaction struct, the token contract being invoked and the amount to be sent as well as the nonce / _nonce. solidity
0x6a761202000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f84c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000ee5b5b923ffce93a870b3104b7ca09c3db80047a00000000000000000000000000000000000000000000000000003691d6afc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Between these two transactions, there's 44 bytes of variability. So 439 out of 489 bytes between these transactions are identical. Beyond that, the number of identical (repetitious) bytes in the two transactions.
Both
calldata strings present the following repetitious, low-entry strings:1. Both
calldata have identical function signatures to begin their strings. Following this calldata are 24 repeating zeroes. 2. The 20-byte address is encoded in the
calldata past that point and for both transactions that portion is different. 3. Afterward, there are 125 repeating zeroes consecutively in both sets of
calldata up to the next element in the calldata. 4. There is a
uint8 value in the code (represents a nibble due to typing). This one byte in the hack transaction differs from what we see in legitimate transactions. 5. Following this, there are another 60 bytes of nulls (zeroes) until the next data element is reached. This
data element that gets reached is the safeTxGas. In the hack transaction, this value is 0xb2b2 and in the legitimate one its 0xf84c (however, there are plenty of transactions where that value is 0xb2b1, which would shrink the byte difference down to just a nibble or 1-byte versus 2 full bytes at this juncture).