Using Gnosis Protocol as a security layer for Gnosis Safe

One of the most challenging aspects of providing a secure wallet environment for the user is: does a user understand what they are signing when approving a transaction.

Let’s make a thought experiment - you are a hacker and you have been able to hack one of the centrally hosted interfaces that e.g. our Safe apps use. Users might regularly use those Safe apps. Now you have the opportunity to alter the transaction that is generated when the user is performing a specific action.

The question is: would the user detect this hack when the “confirm transaction modal” pops up with the payload. Here is an example from the balancer pool management app:

I picked a function to add GNO to a GNO/ETH pool.

Here is the modal that pops up when approving the transaction:

By just looking at this screen IT IS NOT POSSIBLE to tell whether or not the transaction is malicious.

You would at least need to check into the approve-transaction that a) it is approving the right token (GNO) and much more importantly the right spender. Approving a malicious spender is equivalent to losing all approved tokens.

Screen Shot 2021-09-01 at 10.23.55

My first assumption is that >90% of users will not check the spender address but even those that do - what will they actually check? They will open the address on Etherscan:

And seeing something like this would satisfy them. Now - it is trivial to deploy a “verified” contract that is called BActions.

Next, you would need to check the other function as it could easily be another swap function that could e.g. use other already approved tokens and potentially send the output amount to another address.

My claim is that >99% of all users would not detect a malicious transaction that would approve a malicious BActions contract instead of the real one.

So the question is, can we do better? Can we provide information about the effect of the transaction that is digestible for a user? To me, it becomes clear that digesting all the sometimes complex steps a transaction is doing during its execution is not feasible. However - digesting the final desired state change is possible. In this concrete example: what the user really wants to do: Simply convert GNO tokens into GNO/ETH(80/20) Balancer LP tokens.

The exact same transaction could be done via GnosisProtocol. A key advantage is the simplicity of authorizing the transaction:

Only the marked data points are security-relevant and could be presented in a very digestible way. The interface could have a list of known tokens but even without, the user would only need to check:
Is the sellToken address actually the token I want to sell, are the amounts correct, is the buyToken address correct.

The concrete execution path (including the risk of approving an unknown contract like BAction) would in this model be outsourced to professional “solvers”.

On a side note: MEV protection and paying gas costs from the tokens the safe holds instead of ETH held by the signers are two nice side effects for Safe users of using GP.

Remarks on modules and higher security:
Besides making the transaction effect easier digestible it is also possible to in general limit the damage that can be done with such a transaction. It might be useful to have a module that allows to “only trade” and those trades might require lower security settings (1/n instead of m/n). Or a DAO might want to allow performing trades faster than the full DAO process for transactions with arbitrary consequences.

Generating arbitrary orders can still drain all ERC20 tokens in 2 ways:
a) it is possible to buy a fake token
b) it is possible to specify a “receiver” field.

However - if it is checked that a) the buy token is from a somehow curated whitelist of only “real/ liquid” tokens and the receiver is 0x00000 (which is interpreted as receiver = sender) the damage that can be done is limited to the costs that can occur from trading trough slippage and fees.
Those costs could be further capped by:
a) checking on-chain price oracles that make sure: value (sell token amount + fee)* 1.x < value buy token
b) limiting the number of trades that can be done e.g. per day


I really like the idea! Maybe a more low hanging fruit would be to provide a “simulate in tenderly” link. The issue there could be that the tx will only simulate successfully once there are enough signers.

One workaround for that could be to use the simulateDelegateCall from the StorageAccessible library to simulate execution without owner check.

1 Like

Another alternative is the ganache-mainnet forking method. It’s so nice, as it bypasses the signature check. Hence, one can bundle the approval-tx from the necessary owners - without their PK - and the transaction to be checked and then simulate the execution

That’s a really great demo of the problems of approving smart contract transactions.

What about this solution to the problem:
There is a third party that has to cosign transactions for them to be executed. The user goes to some user interface and initiates a transaction. The payload to approve the transaction shows up and the user is not able to detect if it is malicious or not but signs it. But for the transaction to be executed a third party Transaction Security Service has to also sign the transaction.

They could then try to detect malicious transactions using lots of different tools like data analytics / machine learning / keeping whitelists of contracts / etc. If everything looks good, they sign and the transaction gets executed. If it doesn’t, the service could hold off and send a report to the user. Or you could do things like escalate and have someone manually analyze the transaction.

It seems like this could provide for a good balance between user experience and security. Of course, there would be some trust involved and you wouldn’t be guaranteed to catch all malicious transactions. But over time, such a service could become more sophisticated and precise.

1 Like