The Web3 Security Tool That CHANGES THE GAME — Glider Tutorial.
As you may know, Web3 security is a rapidly evolving field, continually surprising me with its innovations. There’s a perpetual stream of new tools, techniques, and attack classes to enhance your auditing and bug-hunting efforts. Glider is a new tool created by Hexens that will help you take your Web3 bug-hunting game to the next level, and in this article, I will show you how.
If you prefer to watch a video tutorial, I’ve got you covered:
$50,000 Web3 Bounty
Take, for instance, a recent discovery by Trust Security outlined in their January 14th article, “Permission denied — The story of an EIP that sinned” This article unveils a novel denial-of-service bug that Trust Security team identified, applicable to over 30 projects on Immunefi and several others outside its Bug Bounty program.
By leveraging this vulnerability, Trust Security earned $50,000 from 15 projects, illustrating the potential rewards awaiting for web3 bug hunters.
One Vulnerability to Hack Them All
Imagine you stumble upon an attack vector during an audit or through bug bounty programs. The next logical step is to scale your efforts. Given the similarity in codebases among many protocols, it’s feasible that a bug discovered in one protocol could have broader implications across similar ones. And that’s precisely where Glider comes into play.
What is Glider
With Glider, you can craft queries (like in CodeQL) to help you find bugs in deployed smart contracts on multiple blockchains.
Let me break it down for you: Let’s say you find a new bug with a specific part of a smart contract. You can describe what makes that bug unique, like its characteristics. Then, using Glider, you make a query based on those characteristics. Glider then looks through all the smart contracts on the blockchain to find ones that match your question.
This way, you can find other contracts that might have the same bug. Right now, Glider works with Ethereum test networks and Ethereum mainnet, but more blockchains might be added later.
Glider lets you find more bugs and scale up your bug bounty rewards easily. Imagine finding a vulnerability in one contract and then discovering it’s in 40 other contracts too! Instead of just disclosing the bug to one protocol, you can disclose it to 40 protocols and get way more rewards.
Get Johnny Time’s stories in your inbox
Join Medium for free to get updates from this writer.
Right now, Glider is still in beta, and it only works with Ethereum test networks and mainnet. The team is careful about letting people use it on mainnet because there are some black hat hackers who might try to use it to find vulnerabilities and exploit real protocols.
If you use the special link in the description below, they might give you access to the mainnet faster. So, give it a try and see what problems you can find on the blockchain! When you open Glider, you’ll see some pre-made queries that can help you get started.
How to use Glider?
- To get started with Glider, first, sign up for an account using this link.
- Think about the specific characteristics of the bug you want to investigate and find in multiple deployed smart contracts.
- Break down these characteristics into instructions and properties. Then, use Glider and CodeQL to construct a query based on these characteristics. Check out also the Glider Docs to learn how to craft awesome queries.
- Select the blockchain you want to scan, run your query, and let Glider’s engine do the rest. It will search through indexed contracts on the blockchain to find matches for your query pattern.
- Once you’ve identified potential vulnerabilities, report them to the protocols to secure funds and possibly earn bounties. You can find bounty programs on both Remedy and Immunefi. In case the protocol doesn’t have a bug bounty program, reach the protocol devs directly in a responsible safe manner.
Demo 1: selfdestruct
Let’s dive into a practical demonstration of Glider’s capabilities. With Glider, we can easily identify contracts that utilize the selfdestruct opcode, potentially exposing them to exploitation. In the left menu select the boilerplate selfdestruct query, then select the “Ethereum Kovan Testnet” network and run the query:
Let’s break down the query:
from glider import *
def query():
instructions = (Functions() # iterate over all functions
.without_properties([MethodProp.HAS_MODIFIERS]) # filter out the functions that have modifiers
.instructions() # level-down to instructions
.with_called_function_name('selfdestruct') # find the ones that call selfdestruct
.exec(100)) # take the first 100 results, if you remove the exec param you will get all of the results
return instructions- We iterate all over all the functions from all the contracts that Glider know on the Kovan testnet.
- We filter out functions with modifiers.
- We check within every function if it calls the
selfdestructopcode. - We limit the results to 100.
We can see that we got different results, some of them are not really exploitable since these are constructor functions or internal functions that can’t be accessed and executed by us. Let’s try to improve the query!
Demo 2: selfdestruct-advanced
Let’s try to improve our previous selfdestruct query. This time we will use a bit more sophisticated query:
from glider import *
def query():
instructions = (Functions() # iterate on all functions
.with_one_property([MethodProp.EXTERNAL, MethodProp.PUBLIC]) # get only public or external
.without_properties([MethodProp.HAS_MODIFIERS, MethodProp.IS_CONSTRUCTOR]) # filter out the ones that have modifiers or are constructors
.instructions() # level-down to the instructions of the filtered functions
.with_called_function_name('selfdestruct') # take the instructions that have a selfdestruct call
.exec(100)) # limit to first 100 for pagination
results =[] # prepare the resulting set
for i in instructions: # iterate over all the instructions that we got
if i.has_global_df(): # check that the instruction has a global DF (dataflow) meaning that its flown from msg.sender/tx.origin/ and other controllable sources
flag = True
for prev in i.previous_instructions(): # iterate over the instructions that come before the selfdestruct
if (prev.is_call() or prev.is_if()) and prev.has_global_df(): #check if its a call or if and if it has a global DF, then exclude it
#the idea is that if it has global DF that most likely we are getting checked as a caller
flag = False
break
if flag:
results.append(i)
return resultsLet’s break it down:
- We iterate through all the functions that are either
externalorpublic. - We filter out functions with modifiers and we also filter out constructor functions.
- We search for the
selfdestructopcode within the functions and limit the results to 100. - In the next for loop we make sure that there is no extra access control inside these functions that prevent us from executing it, we also make sure that the param that is being sent to the
selfdestructopcode is not stored in the contract but something we control externally such as:msg.senderortx.origin.
As you can see we got more “exploitable” examples, which are functions that can be called by us, they use the selfdestruct op code with an address parameter that we control.
Demo 3: ERC777 Reentrnacy Vulnerabilities
Let’s craft a query to identify contracts vulnerable to the ERC777 token reentrancy attack vulnerability. By the way, if you’re keen on mastering these vulnerabilities through hands-on exercises, check out the Smart Contract Hacking Course.
from glider import *
def query():
'''
The query is designed to find ERC777 reentrancy bugs in contracts, the pattern looks like this:
target.balanceOf();
target.transferFrom();
target.balanceOf();
'''
instructions = (Functions() # we iterate over all functions
.with_one_property([MethodProp.PUBLIC, MethodProp.EXTERNAL]) # filter only public and external
.with_modifiers_name_not(['nonReentrant','lock', 'onlyOwner']) # filter out the ones with these modifiers
.with_arg_type('address') # has an argument of type address
.instructions() # level-down to the instructions of the filtered functions
.with_called_function_name('balanceOf') # take the instructions that have a balanceOf call
.exec(1000)) # limit the result to the first 1000
results = [] #prepare a list that will include the results
for ins in instructions: # iterate over all of the instructions we got
for second in ins.next_instruction(): # take all of the instructions that come after our balanceOf instruction in the CFG graph
if second.is_call() and 'transferFrom' in second.callee_names() or 'safeTransferFrom' in second.callee_names():
# callee_names will return the list of called function in the instruction, e.g. require(foo()) will return, [require,foo]
for third in second.next_instruction(): # take all the instruction that come after the *transferFrom
if third.is_call() and 'balanceOf' in third.callee_names(): # check that we have a balanceOf once more
results.append(ins) # append to the resulting set
break
# there is a lot of ways to improve this query, can you come up with any ideas?
return resultsIn the query, we aim to identify cases where a contract interacts with an ERC777 token in a specific sequence:
- Initially, it invokes the
balanceOffunction. - Next, it attempts to transfer ERC777 tokens from the user to the contract using the
transferFromfunction. - Finally, it rechecks the contract balance using the
balanceOffunction.
Conclusion
The sky’s the limit when it comes to the possibilities that Glider opens up for scaling your findings. Go ahead, check it out, start playing with it for free, and level up your Web3 bug-hunting game!









