Reverse Engineer Java Native Interfaces (JNI)

2022-08-21 - christian - java, linux, reverse engineering, software development

Der Hersteller einer Appliance stellt für Aufgaben wie den RAID Rebuild Tools in Form von Update Paketen bereit, welche verschlüsselt sind.

Das Unix Werkzeug file liefert direkt den Dateityp:

$ file update_tool-RAID_Init.update
update_tool-RAID_Init.update: POSIX tar archive (GNU)

In dem tar Archiv befindet sich eine install Datei:

$ file install.enc
install.enc: openssl enc'd data with salted password

Ok, verschlüsselt.

Der Service welcher das Update durchführt, ist teilweise in Java und teilweise in C programmiert. Der Java Service enthält neben vielen Open Source Komponenten auch ein updater.jar Package. jar-Packages sind simple ZIP Dateien, die man einfach entpacken kann.

In dem Package enthalten ist eine Java Klasse mit dem Namen PackageUtils.class. Klingt so, als ob das die Klasse sein könnte, welche sich um das Entpacken von Updates kümmert.

Da die Nutzung von Decompilern bei Software welche einem nicht gehört verboten ist, versuchen wir es erst einmal mit dem Unix Tool strings.

	genAesKey
passwordForEncrypt
	updaterBinDir
...
encrypt the Key from OS
Lo0ksLikeAPassw0rdNumber0ne
encrypt the Key from OS failure
Unarchiving update package file with new key failed.
Try to unarchive update package file with old key.
An0therPassw0rdTwo
 encrypt the Key from OS failure.
...
openssl enc -d %s -k %s -in '%s' | tar xzf - -C '%s'
-aes-128-cbc
...
#com/example/updatemanager/UpdateJni
getUpdaterKey1

Nice, zwei Strings welche wie Passwörter aussehen und der OpenSSL Befehl mit dem die Entschlüsselung gemacht wird. Leider sind die Passwörter nicht für das Update Paket: bad decrypt

Im genannten updaterBinDir, diese Variable findet sich auch im Shell Script welches den Update Service startet, befindet sich ein Programm mit dem Namen genAesKey. Führt man dieses aus, erhält man folgende Meldung:

Usage: ./genAesKey key passphrase

Also sind die beiden Passwörter in PackageUtils.class eigentlich die Passphrases zur Entschlüsselung des AES Keys, welcher wahrscheinlich am Ende das Update Package entschlüsseln kann.

Mit UpdateJni ist aber noch eine weitere Java Klasse genannt. Auch hier können wir uns mal die Strings anschauen:

updaterGetCert
(I)Ljava/lang/String;
getUpdaterKey1
()Ljava/lang/String;
	versionupgrader
loadLibrary
updaterStart
updaterStop

Hier steckte ich dann erst Mal fest. Nach langer Sucherei fing ich dann an mit find und grep -r das Dateisystem nach den Strings zu durchsuchen und voilà: /home/updater/app/lib/libversionupgrader.so

Auch hier wieder der strings output:

Java_com_example_updatemanager_UpdateJni_updaterStart
Java_com_example_updatemanager_UpdateJni_updaterGetCert
Java_com_example_updatemanager_UpdateJni_updaterStop
UPDATER_KEY1 0x01
UPDATER_PKGDECKEY 0x04

Ok, WTF, wieso sind Java Funktionen (?) in einer Linux Library referenziert?

Ein bisschen gegoogle später die Lösung: Mit System.loadLibrary("versionupgrader") können Funktionen in der Library libversionupgrader.so aus Java heraus aufgerufen werden!

Nach mehreren Tagen des Rumprobierens hier die richtige Implementierung:

package com.example.updatemanager;

import java.util.List;

public class UpdateJni
{
    public static native boolean updaterStart();
    public static native boolean updaterStop();
    private static native String updaterGetCert(final int p0);

    static {
        System.loadLibrary("versionupgrader");
    }

    public static void main(String[] args) {
        updaterStart();
        System.out.println(updaterGetCert(4));
        updaterStop();
    }
}

Im strings Output bedeutet das (I), dass die Funktion ein Argument vom Typ Integer erwartet. Das Ljava/lang/String, dass die Funktion einen String zurück gibt.

Die Konstante im strings Output der Linux Library (UPDATER_PKGDECKEY 0x04) liefert den Integer, welcher an die Funktion übergeben werden muss.

$ javac com/example/updatemanager/UpdateJni.java
$ java com/example/updatemanager/UpdateJni
DEC1F5A1E5EAAA7DD539BBCFCEB1BB18

Bäm! Es funktioniert!

$ ./genAesKey DEC1F5A1E5EAAA7DD539BBCFCEB1BB18 Lo0ksLikeAPassw0rdNumber0ne
5A81937CD1FBB6A32C2DB9BDB2AAE5CB

$ openssl enc -md md5 -d -aes-128-cbc -k 5A81937CD1FBB6A32C2DB9BDB2AAE5CB -in install.enc > install.unencrypted
$ file install.unencrypted
install.unencrypted: gzip compressed data, last modified: Wed Jun 11 00:54:04 2019, from Unix, original size modulo 2^32 163840

Bäm Nr. 2! Das Archiv lässt sich entschlüsseln.

Fazit

Die Crypto in Linux Libraries auszulagern hat hier absolut nichts gebracht. Man konnte den Aufruf einfach (mehrere Tage Arbeit) nachbauen und ausführen.

Wieder eine ganze Menge gelernt, Danke dafür an die Autoren. 👍


More


serverless.industries BG by Carl Lender (CC BY 2.0)
ec9c07d2 2022-09-11 14:05
Mastodon