[CTF NDH 2018 Quals] Write-Up – Reverse : SoStealthy

03
Apr
2018
  • Google Plus
  • LinkedIn
  • Viadeo
Posted by: Yann C.  /   Category: / / / NDH2k18 / Reverse-Engineering   /   No Comments

Write-up of the challenge “Reverse – SoStealthy” of Nuit du Hack 2018 CTF qualifications.

The weekend of 03/31/2018 is pre-qualification for the Nuit du Hack 2018 as a Jeopardy CTF. Having had the opportunity and the time to participate with some colleagues and friends, here’s a write-up resolution of the challenges which we could participate.

  • Category: Reverse
  • Name: SoStealthy
  • Description : During an incident response, we captured the network traffic from a suspected compromised host. Could you help us reverse the installed malware?
  • File : suspicious.pcap (5.80MB – 7b34f24ad1a87204bc5b1aa4044013c270e171d89d07c1eab0f24e9e2cc5498b)
  • Points : 150

For this challenge a suspicious PCAP file is provided. There are several HTTP requests in it. We begin by extracting HTTP objects from Wireshark :

Wireshark extract HTTP objets

Wireshark extract HTTP objets

There is a lot of objects. Analyze them :

HTTP object list

HTTP object list

A “favicon” file is suspicious, with some code :

Suspicious favicon

Suspicious favicon

This file begins with an XML header then a JavaScript code loads an ActiveX payload base64 encoded. It’s definitively suspicious.

JScript header

JScript header

JScript footer

JScript footer

We cleaned the XML header and associated footer. This script can now be run as JScript via Windows binaries “cscript.exe” or “wscript.exe” :

JScript cleaned

JScript cleaned

When launched, a GUI tells us to provide a “magic word” :

Say the magic word

Say the magic word

The flag is the magic word, go reverse it.

Several ways without success were tried, like decompiling the script through IDA, or trying to debug it with “cscript //X” and VisualStudio. Dump the memory for post-analysis too…

Then the name of James Forshaw and the DotNetToJscript tool inform us about the technology in place : .Net and how the initial binary was embeded in the JScript file. The embeded file is certainly a PE (Portable Executable) file, dig into this :

PE file contains an “MZ” header. But in the base64 decoded, the MZ header isn’t at the offset “0” :

PE header

PE header

PE header identification

PE header identification

We cleaned the header before the “MZ” and save the output file as “.exe” :

Clean header to get PE file

Clean header to get PE file

This binary with valid PE header is now ready to be decompiled through dnSpy :

We can now see the .Net operation and the XOR function :

XOR in .NET

XOR in .NET

    // Token: 0x06000005 RID: 5 RVA: 0x0000226C File Offset: 0x0000046C
    private bool MeeBish0iotho9biBuJi(string magicWord)
    {
        for (int i = 0; i < this.Tai8Aip0ua3ULi6zo1je.Length; i++)
        {
            uint num = (uint)(magicWord[i] ^ this.Tai8Aip0ua3ULi6zo1je[i]);
            bool flag = (ulong)num != (ulong)((long)this.az5nieghahj0Iekah0ph[i]);
            if (flag)
            {
                return false;
            }
        }
        return true;
    }

The XOR key is in a decimal-array format :

XOR key

XOR key

    private int[] az5nieghahj0Iekah0ph = new int[]
    {
        21,
        91,
        20,
        0,
        126,
        0,
        61,
        24,
        2,
        82,
        7,
        17,
        88,
        22,
        18,
        21,
        114,
        117,
        15,
        80,
        59,
        24
    };

The input “magic word” string is xored with the last 22 bytes of the base64 payload itself :

22 last bytes of it's own code

22 last bytes of it’s own code

Run the ActiveX with his own code in input

Run the ActiveX with his own code in input

These 22 last bytes are :

FkKEJ5dGVbXSkIAAAACgsA

The XOR key in decimal-array is :

key = [21,91,20,0,126,0,61,24,2,82,7,17,88,22,18,21,114,117,15,80,59,24]

To find the “magic word”, just do a XOR between the key and the last 22 bytes :

def xor_strings(xs, ys):
 return "".join(chr(ord(x) ^ ord(y)) for x, y in zip(xs, ys))

last22bytes = "FkKEJ5dGVbXSkIAAAACgsA"
xorKey = [21,91,20,0,126,0,61,24,2,82,7,17,88,22,18,21,114,117,15,80,59,24]

xorKeyStr = ""
for c in xorKey:
 xorKeyStr = xorKeyStr + str(unichr(c))

print "Magic Word : NDH{" + xor_strings(last22bytes, xorKeyStr) + "}"

Run :

XOR to get the flag

XOR to get the flag

 

Flag : NDH{S0_E45Y_T0_B3_ST34L7HY}

This challenge will have resisted us a good part of the night! Congrats to Estelle, Martin, Georges and Timothée for this one 🙂

Greeting to the whole team ! 🙂

  • Google Plus
  • LinkedIn
  • Viadeo
Yann C.

About the Author : Yann C.

Consultant en sécurité informatique et s’exerçant dans ce domaine depuis le début des années 2000 en autodidacte par passion, plaisir et perspectives, il maintient le portail ASafety pour présenter des articles, des projets personnels, des recherches et développements, ainsi que des « advisory » de vulnérabilités décelées notamment au cours de pentest.