webfreehard

Trust Issues

tjctf

Task: a custom DNSSEC resolver and admin bot had to be abused to redirect trust-issues.tjc.tf to an attacker host. Solution: poison the resolver cache through the upstream parameter and SQL injection, then bypass DNSSEC verification with a skipped fake RRSIG and capture the flag from the bot URL.

$ ls tags/ techniques/
resolver_cache_poisoningsql_injection_via_upstream_jsondnssec_verification_bypassadmin_bot_flag_exfiltration

$ cat /etc/rate-limit

Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.

Trust Issues — TJCTF

Challenge summary

This challenge looked cryptographic at first: the bundled nameserver used custom ECDSA signing on P-521 with algorithm 17, and the obvious first idea was to attack nonce generation. That path was a dead end for us.

The real solution was a web/logic chain in the DNS resolver. By poisoning its cache and exploiting a DNSSEC verification flaw, we made the admin bot resolve trust-issues.tjc.tf to our own HTTPS host and leak the flag in the URL.

Final captured flag:

tjctf{trust_n0_on3_ev3r}

Source review

Relevant files from trust-issues.zip:

  • admin-bot.js
  • dnsresolver/app.py
  • dnsresolver/dnssec.py
  • website/app.py
  • website/templates/index.html
  • nameserver/app.py

1. The admin bot already gives us the exfiltration primitive

admin-bot.js is the key to the whole challenge:

urlRegex: /^https:\/\/dnsresolver-[a-f0-9]*\.tjc\.tf\//, const response = await fetch(url + '?name=trust-issues.tjc.tf&type=A'); const json = await response.json(); const data = json.data; await page.goto('https://' + data + '?flag=' + flag, ...);

So the bot does three things:

  1. Accepts only a dnsresolver-*.tjc.tf URL.
  2. Fetches that resolver with ?name=trust-issues.tjc.tf&type=A.
  3. Takes json.data and browses to https://<data>?flag=<flag>.

That means we do not need XSS, cookie theft, or browser tricks. If we can make the resolver return our hostname, the bot sends us the flag directly.

2. The website reflects the flag from the query string

website/app.py and website/templates/index.html show that the site simply renders request.args.get("flag"):

return render_template('index.html', flag=request.args.get('flag', None))

and:

<p>{{ flag }}</p>

So the flag is intentionally transported via the URL parameter. Once the bot is redirected to our host, the flag arrives in the request line as:

https://our-host/?flag=tjctf{...}

Why the crypto path was a red herring for the actual solve

nameserver/app.py signs DNSSEC records with algorithm 17 on P-521 and uses:

...

$ grep --similar

Similar writeups