Read Only Reentrancy | Hack Solidity (0.8)
Summary
TLDRThis transcript details a step-by-step process of executing a read-only reentrancy attack on a smart contract, specifically targeting the Curve Finance pool. The speaker explains how to simulate the attack using Foundry, a framework for Ethereum development. The attack involves manipulating the contract's liquidity functions to exploit the timing discrepancy between function calls, allowing for an increased return on rewards based on an artificially inflated virtual price of the liquidity provider tokens.
Takeaways
- π A re-entrancy hack involves a malicious contract calling into a target contract, which then calls back into the original contract, creating a loop.
- π In a read-only re-entrancy scenario, the hack contract first interacts with a 'date contract' before fully completing its initial call to the target contract.
- π The script provides a step-by-step guide on how to simulate a read-only re-entrancy attack using Foundry on the main network without actually hacking a contract.
- π οΈ The example uses the Curve pool as a target for the simulated read-only re-entrancy attack.
- π The script outlines the installation and initialization process of Foundry, including the necessary commands.
- π The hack contract is created with specific Solidity code, including the interface for the Curve contract and the ERC20 interface.
- π The attack simulates adding liquidity to the Curve pool, logging the virtual price, and then removing liquidity to trigger the re-entrancy.
- π The script demonstrates how the virtual price can appear higher during the removal of liquidity due to the timing of the read-only re-entrancy.
- π― The exploit involves staking LP tokens into a target contract that rewards based on the virtual price returned by the Curve pool's 'get virtual price' function.
- π The script shows that by exploiting the timing of the read-only re-entrancy, more rewards can be obtained than in a normal transaction flow.
- π The script includes a detailed explanation of the Solidity code used for the hack contract and the target contract, including function definitions and interactions.
- π§ The script concludes by showing the results of the simulated attack, highlighting the difference in rewards obtained before and during the liquidation process.
Q & A
What is a re-entrancy hack?
-A re-entrancy hack is a type of smart contract exploit where a malicious contract calls into a target contract and then the target contract calls back into the original malicious contract. This back-and-forth interaction can be exploited to manipulate the state of the target contract or extract additional benefits from it.
How does a read-only re-entrancy attack differ from a regular re-entrancy attack?
-A read-only re-entrancy attack is more complex than a regular re-entrancy attack. In a regular re-entrancy attack, the malicious contract can modify the state of the target contract during the callback. However, in a read-only re-entrancy attack, the malicious contract cannot modify the state of the target contract during the callback, but it can still exploit the timing of the function calls to gain an advantage.
What is the purpose of the 'hack' contract in the provided script?
-The 'hack' contract in the script is designed to simulate a read-only re-entrancy attack on the Curve pool. It is used to demonstrate how an attacker might exploit a vulnerability in the Curve pool's smart contract to gain additional benefits while the 'remove liquidity' function is being executed.
What are the key functions of the Curve pool contract that are being exploited in the script?
-The key functions of the Curve pool contract being exploited are 'getVirtualPrice', 'addLiquidity', and 'removeLiquidity'. The 'getVirtualPrice' function returns the value of the shares, 'addLiquidity' is used to add tokens to the pool, and 'removeLiquidity' is used to withdraw tokens from the pool.
How does the 'hack' contract manipulate the timing of function calls to execute a read-only re-entrancy attack?
-The 'hack' contract first adds liquidity to the Curve pool and then logs the value of the shares using 'getVirtualPrice'. It then initiates the 'removeLiquidity' function, which triggers the attack. During the execution of 'removeLiquidity', before it finishes, the 'hack' contract sends a transaction back to itself, which triggers the 'receive' function. Inside this 'receive' function, the 'hack' contract can call 'getVirtualPrice' again, this time while 'removeLiquidity' is still executing, and exploit the temporarily inflated share value to gain more rewards.
What is the role of the 'Target' contract in the script?
-The 'Target' contract is a hypothetical contract that is used to demonstrate how a read-only re-entrancy attack can be exploited. It has functions to stake LP tokens, unstake LP tokens, and get rewards based on the value returned by 'getVirtualPrice'. The 'hack' contract interacts with this 'Target' contract to show how an attacker could manipulate the timing of function calls to get more rewards than they would under normal circumstances.
How does the 'hack' contract ensure it gets the correct amount of rewards during the attack?
-The 'hack' contract ensures it gets the correct amount of rewards by staking LP tokens into the 'Target' contract before initiating the 'removeLiquidity' function. Once 'removeLiquidity' is called and the 'receive' function is triggered, the 'hack' contract calls the 'getReward' function on the 'Target' contract. The rewards are calculated based on the temporarily inflated 'getVirtualPrice' value, which is higher than it would be if the 'removeLiquidity' function had completed execution.
What is the significance of the 'receive' function in the 'hack' contract?
-The 'receive' function in the 'hack' contract is crucial for the read-only re-entrancy attack. It is the entry point for the transaction sent back to the 'hack' contract during the execution of 'removeLiquidity'. This function allows the 'hack' contract to execute additional code while the 'removeLiquidity' function is still running, enabling the exploit to take advantage of the inflated share value.
How does the script demonstrate the difference in rewards before and after the read-only re-entrancy attack?
-The script demonstrates the difference in rewards by showing the 'getVirtualPrice' value before and during the 'removeLiquidity' function execution. The 'getVirtualPrice' value is higher during the execution because liquidity is being removed, which temporarily inflates the share value. The script then compares the rewards calculated using this inflated value (inside the 'receive' function) with the rewards calculated after the 'removeLiquidity' function has completed, showing that more rewards are obtained during the attack.
What is the purpose of the 'setup' function in the 'hack' contract?
-The 'setup' function in the 'hack' contract is used to stake LP tokens into the 'Target' contract before initiating the attack. This is a necessary step to ensure that the 'hack' contract has tokens in the 'Target' contract to calculate rewards from, which is a key part of the read-only re-entrancy exploit.
What is the role of the 'Forge' tool in the script?
-The 'Forge' tool is used to compile and test the smart contracts written in the script. It is a development utility that helps in building, deploying, and interacting with smart contracts on the Ethereum blockchain.
How does the script ensure that the exploit is executed on the main network?
-The script uses a fork of the main network to execute the exploit. This allows the developer to test the attack without actually interacting with the real main network, thus preventing any real-world consequences while still simulating a realistic environment.
Outlines
π Understanding Re-Entrancy Hacks
This paragraph introduces the concept of re-entrancy hacks, particularly focusing on how a malicious contract can manipulate a Target contract's state by making recursive calls. It explains the process of a re-entrancy attack, where the hack contract first interacts with the Target, then the Target calls back into the hack contract. The example given involves a read-only re-entrancy attack simulation using Foundry, a development framework for Ethereum. The speaker guides through the installation of Foundry and the initialization of a project, setting the stage for a practical demonstration of a read-only re-entrancy attack on a hypothetical smart contract.
π Coding the Re-Entrancy Attack
The speaker delves into the technical details of executing a read-only re-entrancy attack by coding a 'hack' contract. The process involves setting up the contract with specific addresses, defining functions for adding and removing liquidity from a pool, and exploiting the timing of function calls to manipulate the 'get virtual price' function. The speaker outlines the steps of adding liquidity to a pool, logging the virtual price, and then removing liquidity to trigger the attack. The explanation includes the preparation of necessary data structures and the intricate dance of function calls that lead to the exploit.
π― Targeting a Contract for Exploitation
This section describes the creation of a 'Target' contract that simulates a contract vulnerable to read-only re-entrancy attacks. The speaker outlines the functionalities of the Target contract, which includes staking and unstaking LP tokens and a reward system based on the virtual price. The speaker emphasizes the importance of the virtual price in the exploit, as it is used to calculate rewards. The Target contract's code is detailed, including the logic for stake, unstake, and get rewards functions, which are crucial for the exploit to work.
π Executing the Exploit
The speaker concludes the video script by integrating the previously discussed concepts into a full-fledged exploit. The hack contract is updated to include the Target contract, and a setup function is added to stake LP tokens into the Target. The main 'poem' function is then used to trigger the re-entrancy attack, demonstrating how the exploit can yield more rewards than legitimate interactions with the Target contract. The speaker runs a test on the main network to validate the exploit, showing the difference in rewards before and during the removal of liquidity, thus illustrating the impact of read-only re-entrancy attacks on smart contracts.
Mindmap
Keywords
π‘Re-entrancy Hack
π‘Target Contract
π‘Call Back
π‘Read-Only Reentrancy
π‘Solidity
π‘Foundry
π‘Curve Pool
π‘Liquidity Provider (LP) Token
π‘Virtual Price
π‘Add Liquidity
π‘Remove Liquidity
Highlights
Explaining the concept of re-entrancy hack where a contract calls a Target contract which then calls back into the original contract.
Describing the process of a re-entrant hack involving two calls to the Target contract and the execution sequence.
Introducing a read-only re-entrancy attack which is more complex and involves the hack contract calling a 'date contract' which then calls back.
Providing an example of a read-only re-entrancy attack on the Curve pool, executed as a simulation on the main network.
Installing and setting up Foundry for the purpose of writing smart contracts and simulating the attack.
Initializing a Foundry project and preparing the environment for writing code related to the re-entrancy attack.
Creating a new contract named 'hack' and defining its structure and purpose for the attack.
Setting up the addresses for the Curve pool and the liquidity provider (LP) token involved in the attack.
Writing the interface functions for the Curve contract and the ERC20 token standard to be used in the attack.
Describing the steps of the attack which include adding liquidity, logging the share value, and then removing liquidity to trigger the attack.
Explaining the timing of the attack where the 'remove liquidity' function sends Ether back before fully executing, allowing for the re-entrant call.
Writing the 'hack' contract's code to perform the attack by adding and removing liquidity from the Curve pool and exploiting the timing window.
Compiling the 'hack' contract and preparing to write a test to verify the effectiveness of the read-only re-entrancy attack.
Renaming the test file and contract to 'hack.test' and importing the necessary modules for the test environment.
Setting up the test function 'setUp' to initialize the 'hack' contract and prepare for the attack simulation.
Writing the 'testAll' function to execute the attack simulation and send a specified amount of Ether to trigger the attack.
Running the test on the main network using a fork URL from Alchemy API to simulate real network conditions.
Analyzing the logs to confirm that the 'get virtual price' is higher during the 'remove liquidity' execution, indicating a successful read-only re-entrancy.
Explaining the next step of exploiting another contract that depends on the 'get virtual price' value by calling it during the re-entrant attack.
Transcripts
in a re-entrancy hack a contract will
call a Target contract this Target
contract will call back into the caller
in this case the hack contract well the
first call has now finished this hack
contract will call back into the Target
contract this is re-engine C hack a
read-only re-entracy is a little bit
more complicated first the hack contract
will call into a contract we'll call
this contract date contract day will
call back into the caller in this case
it will be the hack contract at this
moment the call made in the first step
to contract day has not yet finished
while the call in the first step is not
yet finished this hack contract will
call into a Target contract the Target
contract will read some state from
contract day finish executing step 3
after the function call to the Target
contract finishes then the execution to
step 2 finishes and thus leader
execution to step 1 finishes let's take
a look at an example for this example
I'll be using Foundry so first let's
install it I'll install Foundry by
copying and pasting this command into my
terminal all of the command and the
codes that I'll be typing I'll put it up
on GitHub
the next step of installing Foundry is
to type Foundry up so I'll type Foundry
up
next I'll initialize The Foundry project
inside this directory by typing Forge
init okay once that command executed
successfully if I check the current
directory you can see some files that
Foundry put in and we're now ready to
write some code for the read-only
reentrc example for this example you'll
simulate a read-only reentrancy attack
on the curb sde pool and we'll be
executing this on the main Network so we
won't be actually hacking the contract
we will just be running a simulation
first I'll open source folder and then
remove this boilerplate code
and then I'll create a new contract
called hack dot so first I'll paste the
solidity version next I'll import
console.tool so that you'll be able to
console log some values inside a
transaction next I'll set the address
for the curb sde pool and the token that
you get when you deposit into this pool
I'll name it LP for liquidity provider
we're going to be hacking the curb
contract so I'll paste the interface for
the curb
for this example the functions that
we're going to be calling is get virtual
price add liquidity and remove liquidity
when you call this function get virtual
price it Returns the value of the shares
the higher the value of the shares the
more tokens that you'll get that is
locked inside the pool when you call the
function withdrawal we can add the
tokens to this curve pool by calling the
function add liquidity and to remove the
tokens from this pool we'll be calling
the function remove liquidity we're also
going to need the irc20 interface so
I'll paste it here okay so next let's
write the hack contract so I'll type
contract hack first let's set some
contact addresses I curve this will be
private constant and I'll name it pool
and this is equal to I curve this is the
interface that we defined above and the
address of the pool that we'll be
calling is St E4 I also set the irc20
for the lp tokens so ierc
25A constant LP token and this will be
irc20 for the lp this is the token that
we'll receive when we call the function
at liquidity on the purple next I'll
write the function called Palm this will
be external and payable since we will be
sending some leave this will be the main
function that we will be calling to
initiate the attack so what we're going
to be doing is to First add liquidity to
the curve pool next we'll log the value
of the shares by calling the function
get virtual price
log get virtual price and then we'll
remove
liquidity by calling this function we'll
be able to trigger the read-only
reagency attack
when we call the function remove
liquidity on the curb as the E4 and then
if I scroll down you can see here that
it sends the if back before the function
finishes executing so this means that
when we call the function remove
liquidity the code will execute and then
before it finishes executing all of the
code it will send some leave so this is
where we can do the reagency back inside
my code editor and back inside our
contract when we call the function
remove liquidity at some point it would
send the if back to this contract so
inside this contract I'll Define a
receive external payable and this is
where we will write our rest of our code
to execute the read-only reentrancy in
this example to show you the get virtual
price will be higher while we're still
executing remove liquidity inside here
we will log get virtual price again okay
let's write our code the first thing is
to add liquidity now if I scroll up to
call add liquidity on the curvepool
we'll need to prepare a array up to
specifying the amount of tokens to add
and
the minimum amount of LP tokens to make
so scroll down first we'll initialize
the inventory of two unit two
memory or call it amounts and this will
be equal to the first value represents
the amount of beef that we're going to
be sending so you'll be sending
message.value the second amount
represents the amount of stf that we're
sending we will be sending zero and then
we'll add liquidity by calling
pull dot add liquidity
passing in the amounts and the minimum
amount of LPS that will be minting we'll
say one and when we call this function
that liquidity since this is the if sde4
we'll have to also send the amount of
beef that is specified over here so say
value is message dot value when we call
this function that liquidity it Returns
the amount of LP tokens that were minted
so I'll say uint LP equals to and that
is ADD liquidity next we'll log the
virtual price the value of one share of
these LP tokens so I'll say
console.log then I'll type
before
remove LP
virtual price and then call the function
pool dot get virtual price and then
we'll remove liquidity let's scroll up
to call remove liquidity we'll need to
pass in the amount of LP tokens that
we're going to be burning and the
minimum amount of underlying token this
will be if and sde that we expect to
receive so scroll down we'll prepare a
uint array of size 2 unit 2 memory Min
amounts for this example I'll just say
minimum amounts at 0 U and zero uint
zero and then we'll call the function
remove pool Dot remove liquidity passing
in the amount of LP tokens that we're
going to be burning we'll withdraw all
of it so we'll pass in all of our LP
tokens and pass in minimum amounts when
we call this function remove liquidity
at some point it will send us back the
eve which will trigger the receive
function so inside here we'll log the
get virtual price so I'll copy this
paste it here and then say during remove
liquidity log the virtual price okay
this completes the first step of our
hack contract what we're trying to see
here is that while we're calling remove
liquidity we should see that get virtual
price is higher and by confirming that
get virtual price will be higher while
this part of the function is executing
we'll be able to write our exploit
inside this receive function so let's
first check the get virtual price is
actually higher inside this part of the
code so I'll open my terminal and then
I'll try to compile the contract by
typing Forge build now notice that the
contract didn't compile because there
was a contract in the test file that we
deleted so I'll go fix that right now
open the test and then remove the
Imports
and then remove all of the code inside
the test and let's try compiling again
okay our contract compiles let's now
write the test a rename counter to
hack.t.soul and also rename the test
contract to hack test instead of
importing a counter we'll import the
hack contract and then inside our test
contract I'll also import the console
from Forge STD so copy this
paste it here and then first we'll
initialize the hack contract so I'll
type hack public hack and then we'll
write the function to set up the test
function set up public then we'll
initialize the hack contract pack is
equal to new hack next we'll write a
function to test our poem function
function test all
public and then we'll call the function
hack dot poem when we call this function
let's send hundred thousand if so I'll
say value 100
000 times 18. okay that's execute the
test so when we call the function Palm
we should see that get virtual price
will be higher inside the receive
function compared to what we get when we
call the function get virtual price
inside the pawn so I'll open my terminal
clear the logs and then we'll be running
a test on the main Network I'll copy the
address for the fork URL that I got from
Alchemy API I'll paste this command
sending the fork URL to the Alchemy API
and then we'll execute the test by
calling Forge test Dash I'll put in Four
B's BBB and this will print out of other
logs when we run the test then we'll say
fork URL is Fork URL from what we set
above and then hit enter
okay our test finished executing and you
can see that there is a lot of logs this
is because if I scroll up I put in Four
B's and this means that it would print a
lot of logs for this execution what I am
interested is in the virtual price
before removing liquidity and the
virtual price during removing liquidity
let's take a look before removing
liquidity it is this amount that you see
over here and during when we're removing
liquidity you can see that the virtual
price has gone up by a little bit ecf5
over here and you see a 9 over here what
this means is that if we target another
contract that depends on Virtual price
and then call that contract while we're
removing liquidity we'll be able to
exploit that contract that will be the
next step okay I'm back in my hack
contract and what we're going to do next
is write an example contract to Target a
contract to exploit inside the function
received so first I'll write a Target
contract that we'll be using for this
example so I'll say contract
Target and let's imagine that this
Target contract you'll be able to State
the lp tokens and you'll get some kind
of rewards where the rewards is
calculated based on the value returned
by get virtual price so first I'll copy
these to contract addresses and instead
of LP token here I'll name it token I'll
rename it to token so let's imagine that
this contract has three functions a
function to stake the tokens
a function to unstake the lp tokens and
some kind of function to get rewards
based on the amount of tokens that we
stick function get viewed okay let's
fill in the details so stake it will
take in the amount of LP tokens to State
this will be external and when we call
this function let's say that it
transfers the token transfer from from
message dot sender to this contract
address this for the amount amount from
the input
and then let's say that it keeps track
of the amount of tokens that is state so
create a mapping
from address to uint this will be public
I'll name a balance up then when we
stake it will update the balance of for
message dot sender incremented by amount
okay scrolling down when we on stake
we'll be able to unstick the amount this
will be external when we on state we'll
first update the balance of message dot
sender decremented by amount and then
we'll transfer the token token dot
transfer to message dot sender for the
amount okay the last function we'll
write is get rewards so this will be
external
returns let's say that it just Returns
the amount of tokens amount of rewards
tokens that we earn we'll set the amount
of rewards token that we earn is we
multiply the amount of state by the
value of shares and the value of shares
we get it by calling get virtual price
on the curvepool so say unit
reward is equal to balance of message
dot sender times get virtual price pool
dot get virtual price now both the lp
tokens and get virtual price has 18
decimals so what we'll have to do is
divide by 10 to the 18. if we multiply
balance all which has 18 decimals and
get virtual price which has 18 decimals
this multiplication now has 36 decimals
so to get it back down to 18 decimals
again we'll have to divide by 10 to the
18. and then we'll have some code to
transfer the reward go to transfer
reward but for this example we'll just
omit it
escape code to transfer reward and for
this example we'll just return the
amount of reward that was calculated
okay let's try compiling this contract
clear the logs and then type Forge build
look at the contract compiled
successfully so let's move on to write
some exploit inside the hack contract so
scrolling down to the hack contract
first I'll store the Target contract
inside the hack contract so I'll say
Target
private immutable Target and then Define
a Constructor Constructor we'll take in
the address of the target
and then we'll set the target target
equal to
Target address of Target
now what we're going to do is before we
call the function poem we'll stake some
LP tokens into the Target contract and
then afterwards we call the function
poem this will eventually call remove
liquidity which we'll call the receive
function inside here we know that get
virtual price is overpriced so inside
here we'll call the function get reward
On Target and we should get more rewards
than what we should have if we didn't do
the exploit okay so let's do that so
first I'll create a function called
set up this will be external
payable and inside here we'll deposit
we'll stake some tokens into the Target
contract so first we'll need to add
liquidity copy this paste it here and
then we will transfer this LP token over
to the Target contract so LP token Dot
approve address of the target
for the amount LP and then call Target
Dot take LP next we'll execute the pawn
function and this will execute remove
liquidity which will execute receipt and
inside here let's get the reward by
calling uint reward is equal to Target
dot get keyboard and then for this
example we'll log the amount of rewards
that we earned if we had called get
reward inside the receive function so
say console.log
reward
reward and then we'll also compare this
amount of rewards that we would have got
compared to how much reward we will get
if we call it after the read-only
reentrancy is done so if we did not
execute our read-only reentrency how
much reward will we get
I'll copy these two code
and then paste it here
okay let's update our test so back
inside the hack test contract I know
that we'll need to deploy the Target
contract so Target public
Target and then we'll deploy the Target
contract first Target
is equal to new Target
and now the hack contract takes in the
address of the target so inside the
Constructor I'll pass in the address of
the
Target and then before we call the
function Palm you'll need to call the
function setup to stake some of our LP
tokens into the Target contract so here
hack not
setup and when we call this function
setup we'll get some LP tokens from the
pool from the curb SD E4 and then stake
it so let's send some if so I'll say
value we'll send 10 if 10 times 10 to
the 18. okay let's try compounding
contract I'll open the terminal clear
the logs and then type Forge build
and a contract compiles so let's now run
the test again
and our test was successful let's check
the logs scroll up
before we remove liquidity get virtual
price return some amount during the lp
is being removed so this will be the
code that was executed inside the
receive function get virtual price was
slightly higher and when we call the
function get reward we would have gotten
this much amount whereas after remove
liquidity is done executing and then we
call get reward we get a slightly thus
amount this means that if we were to
call get reward while the liquidity was
being removed then we would have gotten
more rewards than calling get reward
after recalled remove liquidity that was
an example of read-only reentrancy where
we wrote Our hack contract and then we
called the curb SD info we called it by
calling the function remove liquidity
and then while the remove liquidity is
executing it transferred some if and
then inside here it executed the code
inside the receive which allowed us to
call the Target contract get keyboard
get reward calculated the amount of
rewards by calling get virtual price
inside the curve pool at this point the
virtual price is slightly higher than
what it should be so by the time all of
this finished executing we were able to
get more rewards than what we would have
if we did not execute the read-only
re-engency
Browse More Related Video
CS2107 Padding Oracle Attack
Get +1ETH a day | My team created a bot that works thanks to ChatGPT
DDoS Attack Explained | How to Perform DOS Attack | Ethical Hacking and Penetration Testing
AI Smart Contract | Step-by-step instructions
HOW to use MITRE ATT&CK Navigator in SOC Operations with Phishing Use Case Explained
Why You Choose the Wrong Liquidity ICT Concepts | SIMPLE 3 Step ICT Strategy
5.0 / 5 (0 votes)