Re-using signed messages via Metamask to leak your JWT token on "Web3.0" websites to use on API requests

Re-using signed messages via Metamask to leak your JWT token on "Web3.0" websites to use on API requests

I've been quiet a bit since the new year began and I haven't been as active on social media lately, sorry! I've been working around the clock building and running BugBountyHunter and helping others start their journey in bug bounties, whilst also raising our 5 month son! (Who has 2 teeth already, ouch!). We recently updated our guides and added new ones and there's still lots more to come, so if you haven't checked us out for a while why not check us out! I have recently got a new camera setup and I am working to bring out lots more video content and hosting more live streams so keep your eyes pealed!

Become a bug bounty hunter - Learn about web application vulnerabilities and how to find them on bug bounty programs |
BugBountyHunter is a training platform created by bug bounty hunter zseano designed to help you learn all about web application vulnerabilities and how get involved in bug bounties. Begin participating from the comfort of your own home.

With that said I did find some time to have a play with some "web3.0" websites, most specifically NFT related web applications and I found some interesting issues. I recently done a talk for InfoSec Writeups ( on these but this post will outline one interesting issue I found with signing a message via MetaMask on the Ethereum blockchain to authenticate on web applications. The fact these programs pay considerably more than "web2.0" programs gives me an incentive to learn the technology more. If you're also interested in this then I highly recommend checking out and giving them a follow on twitter. They're the go-to place for finding resources and learning material as well as programs.

So how does signing a message on MetaMask work?

You can sign a message with your Ethereum wallet and this is meant to be a "secure" way of proving this is who you say you are. You signed this message. On response a signature is provided to the web application and they are able to validate your claims and authenticate you. Sounds pretty straight forward right?

Let's look at an example (website redacted). When logging into the website below I am prompted to sign a message verifying my identity:

(Yay, it doesn't cost me any ETH! Of course I will sign this!)

Upon signing the message, I will be logged in with my ethereum address and my signed message is converted into a "signature" which is exchanged for a JWT token. This is used to authenticate me on their web application.

This JWT token is used on API calls and is used to identify me making the API request, such as when updating your account information. It tells the web application who I am and which profile to update. Sounds pretty straight forward, so can we find an issue with this flow at all?

Let's just obtain the signature ourselves

So here's the problem. We can initiate a request for the user to sign this message on OUR website and in return obtain the signature used in exchange for their JWT token. The origin doesn't matter AT ALL, as long as the message is signed. So as you can see below, I am requesting the user to sign this message from MY website, rather than

The message contents are the same and this is what is key. That's all the web application is looking for - the contents being the same and they're validating the fact the user "signed" this, and a unique signature was generated for this message based on their address. This is how they verify it's you.

Upon signing the message, I will receive the users signature and public address as they are on MY website and this can be used on the request outlined above to retrieve a users JWT token.

The second issue is, when performing an action such as editing your profile, you were then again required to sign another message in order to create another signature. Except.... we can make ANY API calls on their behalf from any JWT token we generated.

Example: If the user signed the message, "I want to login" and a signature was captured, and then went to edit their profile, they'd need to sign, "I want to update my username to xyz" and a new signature used.

Except, this wasn't required for the attacker. As they captured a valid JWT token, the API calls would succeed regardless on WHAT the signature was for. There was no need to request the user to re-sign the message! On a particular website I had access to modify their profile and any NFT information they had listed. No access to user funds directly though.

One click PoC:


But users wouldn't blindly click 'SIGN' on their crypto wallets, RIGHT?

Read more:

Be careful what you sign.

Testing as a hunter

Go check out some Immunefi programs and test how the web applications work. Here's the code you can use to generate signatures for signed messages on metamask: (modify message etc!)

<script src=""></script>

var web3 = new Web3();
if (window.ethereum) {
    web3 = new Web3(window.ethereum);
    try {
        window.ethereum.enable().then(function() {
            var address = web3.eth.getAccounts().then(accounts => {
             	   var msg = "message-to-sign";
             	   web3.eth.personal.sign(msg, accounts[0], function (err, result) {
             	   	alert("Signature:"+result+"\nETH address:"+accounts[0]); }
    } catch (e) {}

The fix for developers

It seems like a sort of "CSRF" vulnerability affecting "web3.0" websites, because the fix is the same as preventing CSRF on "web2.0" websites! Instead of signing a STATIC message that the attacker can generate, require the user to sign a message with a unique identifier that an attacker can not access (unless used with another bug such as XSS maybe but that's for another post in the future haha). This will prevent an attacker from being able to sign messages on their site which leads to the attacker being able to use the signature to authenticate on your website. Signing a static message seems a bad approach.

I am not sure if it's possible to verify the Origin: on where the message was signed, but I will update this post once I dive into the tech more and understand how it works, but it seems ideal that if the Origin of the signed message does not match your website then the signature will be invalid.

Secondly, if you want users to re-sign messages to generate new signatures for API calls, ensure the JWT token used IS for this API call. Don't accept ANY valid JWT token for all API calls.

Further testing

I have basic knowledge when it comes to how smart contracts work, and my knowledge is extremely low when it comes to understanding how to look for vulnerabilities in them. All of my testing currently has been based on my knowledge of web applications and how a user interacts with websites using their Ethereum address.  

I do have full plans to explore more about smart contracts and how they work, because as much as people hate NFTs and Crypto, I really don't think the technology is going anywhere, and with big bounties up for grabs, it could be worth spending some time understanding how they work.

I mentioned it above but I gave a talk over at discussing some more findings on NFT websites and how you can use your knowledge on web app security to begin your journey into "web3.0". I will make this video public in due time!

IWCon2022 - Infosec WriteUps Virtual Conference
Infosec writeup is organizing the Grand Virtual Conference and Networking Event. Organized by the Internet’s largest & most loved Infosec Publication.

That's all for now. I am also going to be getting back into bug bounties as i've recently re-activated my bugcrowd account and I look forward to also trying to catching Jonathan Bouman on a certain program! ;)

Cya out there on the playing field!

Take care and hack the planet