# Trace a crypto wallet: an OSINT CTF walkthrough

Published: 2026-11-12 · By HackerGPT Team
Canonical: https://hackergpt.org/blog/crypto-wallet-osint-ctf/

> A hands-on blockchain OSINT walkthrough: start from a CTF wallet address in CyberGym's JavaScript, read its history on a public block explorer, and pivot to the contracts it created, the challenges it played and the code it now delegates to.

**TL;DR:** To trace a crypto wallet in an OSINT CTF, start from the address, read its full transaction history on a public block explorer, and pivot on four things: who funded it, what it created, what it called, and who created those contracts. On CyberGym's NFT Takeover wallet those four pivots turned one address into a map of every on-chain challenge in the shop, and showed what happens to a wallet whose seed phrase is public.

This walkthrough is for CTF players who have met a wallet address in a challenge and do not know what to do with it next. You will use nothing but a public explorer API, `curl` and `jq`, against the testnet wallet that [CyberGym](https://cybergym.hackergpt.app), our hosted OWASP Juice Shop, plants for its NFT Takeover challenge. Trace only wallets a CTF gives you or that you own; a real person's wallet is not practice material, and the last section says where we stopped and why.

## What is blockchain OSINT in a CTF?

It is reconnaissance on a public ledger: every transaction on Ethereum and its testnets is readable by anyone, so a single address is a lead you can follow without breaking anything. A CTF hands you an address, or hides one, and asks a question the ledger can answer: who funded this wallet, which contract did it deploy, where did the tokens go.

The skill being tested is the same one investigators use on mainnet: turning an address into relationships. What changes in a CTF is the target. It is a wallet built to be studied, usually on a testnet such as [Sepolia](https://ethereum.org/en/developers/docs/networks/#sepolia), where the coins have no value.

CyberGym has four challenges that touch a chain. Its own `/api/Challenges` endpoint lists them as NFT Takeover (2 stars), Mint the Honey Pot (3), Wallet Depletion (6) and Web3 Sandbox (1). Our article on the [leaked seed phrase](https://hackergpt.org/blog/leaked-seed-phrase-nft-takeover/) covers how NFT Takeover is solved. This one starts where that ends: with the wallet's address, and what the ledger says about it.

## How do I find the wallet address to trace?

Read the target's own code first. A web3 front end has to know which addresses it talks to, so they ship to every visitor in the JavaScript bundle. On CyberGym:

```bash
curl -s https://cybergym.hackergpt.app/main.js | grep -oE '0x[0-9a-fA-F]{40}' | sort -uf
```

```text
0x0f933ab9fCAAA782D0279C300D73750e1311EAE6
0x8343d2eb2B13A2495De435a1b15e85b98115Ce05
0xf4817631372dca68a25a18eb7a0b36d54f3dbcf7
```

Three addresses, and context tells them apart. `0x8343…Ce05` is printed on the `/#/juicy-nft` page as the owner of the shop's soul-bound token. `0xf481…dbcf7` sits in an OpenSea link for that token on Polygon's Mumbai testnet. `0x0f93…EAE6` is the shop's Ether donation address on mainnet: a real project wallet, not a challenge, so it is out of scope and we leave it alone. Deciding what not to trace is part of the method.

The other challenges live in lazy-loaded chunks. Juice Shop 19.1.1 loads them by number, and two of them carry addresses:

```bash
for c in 388 675; do curl -s https://cybergym.hackergpt.app/$c.js | grep -oE '[A-Za-z]+="0x[0-9a-fA-F]{40}"'; done
```

```text
P="0x41427790c94E7a592B17ad694eD9c06A02bb9C39"
A="0x36435796Ca9be2bf150CE0dECc2D8Fab5C4d6E13"
F="0x860e3616aD0E0dEDc23352891f3E10C4131EA5BC"
T="0x413744D59d31AFDC2889aeE602636177805Bd7b0"
```

The surrounding code names the network: both chunks ask the browser wallet to switch to chain id `0xaa36a7`, the Sepolia test network. The same reading habit finds credentials in bundles too, as our walkthrough of [hardcoded credentials in a JavaScript bundle](https://hackergpt.org/blog/hardcoded-credentials-javascript-bundle/) shows.

## How do I trace a crypto wallet on a block explorer?

Open the address on an explorer for the right chain and read its whole history, oldest first. Etherscan is the familiar choice; its API now needs a key. [Blockscout](https://docs.blockscout.com/api-reference/addresses/get-transactions-for-an-address) runs a public Sepolia instance whose API answers without one, which makes every step below reproducible.

Start with a summary:

```bash
API=https://eth-sepolia.blockscout.com/api/v2
W=0x8343d2eb2B13A2495De435a1b15e85b98115Ce05
curl -s $API/addresses/$W | jq '{is_contract, proxy_type, implementations, coin_balance}'
curl -s $API/addresses/$W/counters
```

```json
{
  "is_contract": true,
  "proxy_type": "eip7702",
  "implementations": [
    { "address_hash": "0xD9Ba25F35A5E0421AA5486B7D867680bB8E11d94", "name": null }
  ],
  "coin_balance": "0"
}
{
  "transactions_count": "628",
  "token_transfers_count": "322",
  "gas_usage_count": "29959276",
  "validations_count": "0"
}
```

The first surprise is right there. An address derived from a seed phrase is a plain account, yet the explorer calls it a contract with a `proxy_type` of `eip7702`. Hold that thought; it is the last pivot.

The list endpoint returns 50 transactions per page, newest first. Pull them all into a file so you can query them offline:

```bash
next=""; : > txs.jsonl
while :; do
  page=$(curl -s "$API/addresses/$W/transactions?$next")
  echo "$page" | jq -c '.items[]' >> txs.jsonl
  next=$(echo "$page" | jq -r '.next_page_params // empty | to_entries | map("\(.key)=\(.value)") | join("&")')
  [ -z "$next" ] && break; sleep 0.3
done
wc -l txs.jsonl
```

That gave 628 lines, one per transaction, on 12 November 2026. Everything after this point is `jq` on that file.

![Map of pivots from CyberGym's NFT Takeover wallet to its funder, the token contract it created, four challenge contracts and their deployer](https://hackergpt.org/blog/crypto-wallet-osint-ctf/wallet-pivots.webp) *Every arrow is one query against a public explorer. Players' addresses are left out; the deployer is where the trace stops.*

## What did the wallet's history reveal?

Four pivots, each one a question you can ask of any address.

### Pivot 1: who funded it, and what did it do first?

```bash
jq -s 'sort_by(.timestamp) | .[:2][] | {timestamp, from: .from.hash, to: .to.hash, created: .created_contract.hash, value}' txs.jsonl
```

```json
{
  "timestamp": "2023-07-12T10:10:48.000000Z",
  "from": "0x0408…21BE",
  "to": "0x8343d2eb2B13A2495De435a1b15e85b98115Ce05",
  "created": null,
  "value": "100000000000000000"
}
{
  "timestamp": "2023-07-12T10:12:00.000000Z",
  "from": "0x8343d2eb2B13A2495De435a1b15e85b98115Ce05",
  "to": null,
  "created": "0xf4817631372dCa68A25A18eB7a0B36d54f3DBCf7",
  "value": "0"
}
```

Addresses that belong to neither the wallet nor the challenge are shortened in the output shown here; the explorer has them in full.

The wallet received 0.1 SepoliaETH (values are in wei, 10^18 per ether) and, 72 seconds later, deployed a contract. That contract's address is the one in the shop's OpenSea link: the wallet created its own soul-bound token.

### Pivot 2: why is the address the same on two chains?

Because a contract created with `CREATE` gets an address computed only from the sender and the sender's nonce, not from the chain. [ethers documents it](https://docs.ethers.org/v6/api/address/#getCreateAddress) as the Keccak-256 hash of the RLP-encoded sender and nonce. You can check it yourself:

```bash
npm install ethers@6
node -e "const { getCreateAddress } = require('ethers'); console.log(getCreateAddress({ from: '0x8343d2eb2B13A2495De435a1b15e85b98115Ce05', nonce: 0 }))"
```

```text
0xf4817631372dCa68A25A18eB7a0B36d54f3DBCf7
```

So the token the shop links on Mumbai is, by construction, this wallet's first deployment there too. That matters because Mumbai no longer exists: Polygon retired it in favour of Amoy in April 2024, per [Alchemy's deprecation notice](https://www.alchemy.com/blog/polygon-mumbai-testnet-deprecation). When the chain a challenge points to is gone, the same deployer on another chain can still answer the question.

### Pivot 3: what did the wallet call?

```bash
jq -r --arg w "$W" 'select(.from.hash==$w) | .to.hash // "(contract creation)"' txs.jsonl | sort | uniq -c | sort -rn | head -6
```

```text
 242 0x860e3616aD0E0dEDc23352891f3E10C4131EA5BC
 159 0x413744D59d31AFDC2889aeE602636177805Bd7b0
  93 0x36435796Ca9be2bf150CE0dECc2D8Fab5C4d6E13
  39 0x41427790c94E7a592B17ad694eD9c06A02bb9C39
  19 (contract creation)
   6 0x8343d2eb2B13A2495De435a1b15e85b98115Ce05
```

Every one of the top four is an address from CyberGym's chunks: the Bee Haven faucet, the Wallet Depletion bank, the BeeToken and the Honey Pot NFT. The method selectors, looked up in the [OpenChain signature database](https://openchain.xyz/signatures), are `withdraw(uint8)` on the faucet, `approve(address,uint256)` on the token and `mintNFT()` on the Honey Pot. The wallet still holds 281 BEE and 36 Honey Pot NFTs.

This was the finding we did not expect. Once NFT Takeover is solved, its seed phrase is common knowledge among players, and the history reads like a shared account: 20 different addresses have sent it test ETH that arrived, and it is used to play the *other* on-chain challenges, presumably by players who would rather not fund a wallet of their own. Its busiest month was October 2024, with 136 transactions. A wallet with a public key is not one person's wallet any more.

### Pivot 4: who created the contracts it called?

```bash
for a in 0x36435796Ca9be2bf150CE0dECc2D8Fab5C4d6E13 0x860e3616aD0E0dEDc23352891f3E10C4131EA5BC \
         0x41427790c94E7a592B17ad694eD9c06A02bb9C39 0x413744D59d31AFDC2889aeE602636177805Bd7b0; do
  curl -s $API/addresses/$a | jq -r '[.hash, (.name // "-"), .creator_address_hash] | @tsv'
done
```

```text
0x36435796Ca9be2bf150CE0dECc2D8Fab5C4d6E13	BeeToken	0x91496d9d9E4a014981cCF9740908ef527DBE9BF2
0x860e3616aD0E0dEDc23352891f3E10C4131EA5BC	-	0x91496d9d9E4a014981cCF9740908ef527DBE9BF2
0x41427790c94E7a592B17ad694eD9c06A02bb9C39	The Enchanted Honey Pot	0x91496d9d9E4a014981cCF9740908ef527DBE9BF2
0x413744D59d31AFDC2889aeE602636177805Bd7b0	-	0x91496d9d9E4a014981cCF9740908ef527DBE9BF2
```

One creator for all four. Shared deployers are the strongest cheap link on a chain: contracts that look unrelated in the front end belong to one operator. In a CTF that is often the answer to "who is behind this token?"

## What is EIP-7702, and why does this wallet look like a contract?

[EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) lets an ordinary account point at contract code through a signed authorization, so calls to the account run that code. It went live on Sepolia with the Pectra upgrade on 5 March 2025, per the [Ethereum Foundation's testnet announcement](https://blog.ethereum.org/2025/02/14/pectra-testnet-announcement). Type-4 transactions carry those authorizations:

```bash
jq -c 'select(.type==4) | {timestamp, from: .from.hash, method}' txs.jsonl
```

```json
{"timestamp":"2026-10-05T10:20:48.000000Z","from":"0x8319…ACAC2","method":"0x987ff31c"}
{"timestamp":"2026-03-04T10:56:24.000000Z","from":"0x8343d2eb2B13A2495De435a1b15e85b98115Ce05","method":null}
{"timestamp":"2026-03-04T10:48:36.000000Z","from":"0x8343d2eb2B13A2495De435a1b15e85b98115Ce05","method":null}
{"timestamp":"2026-03-04T10:48:24.000000Z","from":"0x8343d2eb2B13A2495De435a1b15e85b98115Ce05","method":null}
{"timestamp":"2025-07-10T11:45:36.000000Z","from":"0x0c8b…face","method":"0x4ed8de18"}
```

The first one, on 10 July 2025, was sent by a different address. Its authorization is signed by the wallet's own key, which anyone who knows the seed phrase can produce, and it delegates to a contract that the same sender had deployed. The call in the same transaction is `0x4ed8de18`, which the signature database reads as `forwardAllEth()`.

The internal transactions show what that code does. On 31 July 2025 someone sent the wallet 0.05 SepoliaETH, and in the same transaction the wallet passed all of it on to the address that set up the delegation. In 2026 incoming ETH went to a different recipient after the wallet was re-delegated. Plain transfers sent with a 30,000 gas limit now fail with "Out of gas", because a transfer into this account runs the delegate's code. That is how a leaked key is drained on any chain: not one theft, but code that waits for every deposit.

![Timeline of the NFT Takeover wallet on Sepolia from its 2023 funding to EIP-7702 delegations in 2025 and 2026](https://hackergpt.org/blog/crypto-wallet-osint-ctf/timeline.webp) *Wallet events from the Sepolia explorer; the Mumbai and Pectra dates from their announcements.*

## Where should a wallet trace stop?

At the edge of the question you were asked. The deployer `0x9149…9BF2` created the challenge contracts, and its own history goes on long after them, into activity that has nothing to do with the CTF. We did not follow it, and we did not try to tie it, the funder or any player's address to a person. The funder `0x0408…21BE` also sent ETH to the deployer in September 2024; that link is enough to say the two sit close to the same project, and the challenge needs nothing more.

The rule we worked to:

- **Trace the addresses the challenge gives you**, and contracts and deployers that belong to the challenge.
- **Do not publish other people's addresses** that merely interacted with it. Players are pseudonymous, not anonymous.
- **Do not attribute a wallet to a person** unless the CTF asks for exactly that, with a fictional persona.
- **Stay off real victims and sanctioned wallets.** A testnet CTF is where you practise; mainnet attribution is professional casework with its own legal rules.

If you need to know what is public about your own addresses and accounts, run the same checks on yourself; our guide to [OSINT on yourself](https://hackergpt.org/blog/osint-on-yourself/) covers the off-chain side. The [crypto analysis tools in HackerGPT](https://hackergpt.org/blog/hackergpt-crypto-analysis/) cover wallet history, token checks and sanctions screening when you work your own cases, and [CTF Mode](https://hackergpt.org/features/ctf-mode/) walks a challenge like this one step by step, explaining each pivot as it goes.

## Key takeaways

- Start from the target's own code: front ends ship every wallet and contract address they use.
- Pull the full transaction history into a file once, then answer questions with `jq` rather than clicking through pages.
- Ask four questions of every address: who funded it, what it created, what it called, and who created those contracts.
- A `CREATE` address depends only on sender and nonce, so the same deployer gives the same contract address on every EVM chain.
- Decide the scope before you pivot, and stop when the next hop would be about a person rather than the challenge.

## FAQ

### How do I trace a crypto wallet address?

Open the address on a block explorer for the right chain and read its full history, oldest first. Note who funded it, which contracts it created, which contracts it called and who deployed those. Each answer is a new address to repeat the process on, within the scope of your question.

### Is it legal to trace a crypto wallet?

Reading a public blockchain is reading public data, and block explorers exist to make it readable. What you do with the result is where the limits sit: attributing a wallet to a person, publishing it or acting on it can raise privacy and legal questions. Practise on CTF wallets and your own addresses.

### Which block explorer should I use for a CTF?

Use one that indexes the challenge's chain, including testnets. Etherscan's Sepolia site is the familiar choice, and its API needs a key. Blockscout runs public instances for Sepolia and other networks whose API answers without a key, which suits scripted tracing.

### Why does a wallet show up as a contract on the explorer?

Since the Pectra upgrade, EIP-7702 lets an ordinary account delegate to contract code. Explorers then show the account with code and a delegate address. On a wallet whose key is public, anyone can set that delegation, and forwarding code can drain every new deposit.

### Do OSINT CTFs use real cryptocurrency?

Well-designed ones do not. They use testnets such as Sepolia, where the coins have no market value, and planted wallets whose keys may be public by design. If a challenge points you at a mainnet wallet with real funds, read its rules before you go further.

## Sources

- [OWASP Juice Shop](https://owasp.org/www-project-juice-shop/) and its [companion guide, Sensitive Data Exposure](https://pwning.owasp-juice.shop/companion-guide/latest/part2/sensitive-data-exposure.html)
- [Ethereum networks: Sepolia](https://ethereum.org/en/developers/docs/networks/#sepolia), ethereum.org
- [Blockscout API: get transactions for an address](https://docs.blockscout.com/api-reference/addresses/get-transactions-for-an-address)
- [ethers v6: getCreateAddress](https://docs.ethers.org/v6/api/address/#getCreateAddress)
- [OpenChain signature database](https://openchain.xyz/signatures)
- [EIP-7702: Set Code for EOAs](https://eips.ethereum.org/EIPS/eip-7702)
- [Pectra testnet announcement](https://blog.ethereum.org/2025/02/14/pectra-testnet-announcement), Ethereum Foundation, 14 February 2025
- [Polygon Mumbai testnet deprecation](https://www.alchemy.com/blog/polygon-mumbai-testnet-deprecation), Alchemy, 2024

Practise the trace on [CyberGym](https://cybergym.hackergpt.app), or run it on your own site's wallets with [HackerGPT](https://hackergpt.app).
