> For the complete documentation index, see [llms.txt](https://blog.diefunction.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://blog.diefunction.io/ctf/cyberyard-2025/reverse.md).

# REVERSE

## MouseTrap

### Description

APT27 targeted us and something was leaked can you help and identify what was leaked ?

### Goal

Decrypt the PCAP traffic to retrieve the flag.

### **Challenge**

{% file src="/files/UscEJCLRQQvEqmdDHBMW" %}

### **Static Analysis (IDA)**

Load `MouseTrap.exe` into IDA.

#### **Main function**

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2Fp6XiIGcVzh8Dn7rQMgoZ%2Fimage.png?alt=media&amp;token=9695c327-18ed-4ec5-9729-232094b04e0e" alt=""><figcaption></figcaption></figure>

#### **Anti-debugging**

* The calls `CreateDesktopA("LuckyMouse")` and `SwitchDesktop` move the process to a private desktop, which blanks your screen and hides any UI on a black/empty desktop.
* The call `NtSetInformationThread(..., 17)`, an anti-debug tactic that blocks first-chance exceptions and debugging events from reaching WinDbg/IDA.

To debug and analyze, I `NOP`’d these anti-debug sections.

#### **Patched version**

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2FZ4ghZ6M6N5ilF01Rp6aI%2Fimage.png?alt=media&amp;token=74a5f089-61b8-4aa3-8a38-b43a31859222" alt=""><figcaption></figcaption></figure>

### **Dynamic Analysis (Procmon)**

#### **Setup**

* Configure Procmon to monitor **MouseTrap.exe**.

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2F0INtQ461k3cPaBkiszcG%2Fimage.png?alt=media&amp;token=e5ae58c9-da19-4a5f-90e5-dbf5bcc7662b" alt=""><figcaption></figcaption></figure>

#### **Findings**

* Run the malware

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2Fld9d2U4ce96KxpiXUNGa%2Fimage.png?alt=media&amp;token=7a337203-4088-4c52-97d0-d60c592ee7f7" alt=""><figcaption></figcaption></figure>

The malware tries to open `C:\creds.txt`. If it doesn’t exist, it exits.

* Create **C:\creds.txt** and run again

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2FjmO4cZRSUT8cTLklsk8H%2Fimage.png?alt=media&amp;token=e6bd5cba-6859-4534-a91e-55a30978f211" alt=""><figcaption></figcaption></figure>

* Run the malware again

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2FxgCeKlKJXlLkKUR1z6Or%2Fimage.png?alt=media&amp;token=fe52574b-ec6e-49a0-8a15-e120a64fb4b4" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2FrbghPow124G8tmPnmX9Z%2Fimage.png?alt=media&amp;token=b0a73c62-a6ce-46ac-a50e-dd042ecc0ede" alt=""><figcaption></figcaption></figure>

The malware reads the file via `ReadFileEx`, encrypts the content, and attempts to connect to `127.0.0.1:13337` to send the encrypted data (the same traffic seen in the PCAP).

### **Dynamic Analysis (IDA/WinDbg)**

#### **Catching the crypto**

* Initial setup

Set a breakpoint on `kernel32!ReadFileEx`, continue, and configure the debugger not to break on exceptions.

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2FVGA7u6cCmjtXJK5q0LmT%2Fimage.png?alt=media&amp;token=a04aebd4-3714-4e61-97d6-5b5314c0d103" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2Ff6o9UwIM6nehk2pW4LSu%2Fimage.png?alt=media&amp;token=effd5765-a2fa-458e-8c41-d330492506da" alt=""><figcaption></figcaption></figure>

* After resuming execution, the **ReadFileEx** breakpoint triggered.

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2FHwK58mjGWjz54zTlWgUf%2Fimage.png?alt=media&amp;token=fb9fe6ea-9fd0-4e71-935f-3a7ead156c93" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2FnofkbyY7CdN7PCL9C44B%2Fimage.png?alt=media&amp;token=a81bc07d-993f-46c2-82ab-bd4fab5a7131" alt=""><figcaption></figcaption></figure>

Based on the stack arguments, **lpBuffer** is **0x005853B0** in `.data` (`dword_5853B0`); **0x005853B0** holds the content.

* Set a hardware read breakpoint on `dword_5853B0` and continue.

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2FXs0Vph5CeMwiYFFEMjQ3%2Fimage.png?alt=media&amp;token=897a5886-8ece-49e0-a443-51cc8adfc6c7" alt=""><figcaption></figcaption></figure>

* A read triggers inside a routine at `sub_401000` function (Encryption)

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2FcWPPLyRkwLMgCP3qqT5k%2Fimage.png?alt=media&amp;token=29642c01-c3e5-4414-bb90-f8d533af9536" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2F5ilhwELVF1SeABInCGEM%2Fimage.png?alt=media&amp;token=1f32ec54-33b3-4f6d-845a-fc49e80d26c9" alt=""><figcaption></figcaption></figure>

### **Identifying the algorithm**

* Search the constants used by the routine

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2FCsbjDwtUJjiTaHwglWTX%2Fimage.png?alt=media&amp;token=458027f3-85fe-4dcc-8ca7-2f9500f24fb7" alt=""><figcaption></figcaption></figure>

Searching these leads to [**SPC**](https://github.com/chsjiang/spc) (a tweakable Lai-Massey block cipher using a SipHash-like core). The implementation matches the public reference (SPC over 64-bit words, 128-bit key, 128-bit block, 56-bit tweak folded into a 64-bit word).&#x20;

### **Decrypt the PCAP Traffic**

* I reproduced the SPC round function in Python via **GPT 5** and used the key/tweak lifted from .data section.
* Export the TCP stream from the PCAP as hex and feed it to the script to obtain the plaintext.

<figure><img src="https://3314490488-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MbvhwyfSiuEoRfDEQp1%2Fuploads%2FWpawSXJxLV60PORB23U0%2Fimage.png?alt=media&amp;token=377bdb6c-bda6-48ff-8f86-d305620b48e0" alt=""><figcaption></figcaption></figure>

#### **Script**

```python
# solution.py
from binascii import unhexlify, hexlify

MASK64 = (1 << 64) - 1

def rotl(x, b): return ((x << b) & MASK64) | (x >> (64 - b))

def sipround(v0, v1, v2, v3):
    v0 = (v0 + v1) & MASK64
    v1 = v0 ^ rotl(v1, 13)
    v0 = rotl(v0, 32)
    v2 = (v2 + v3) & MASK64
    v3 = v2 ^ rotl(v3, 16)
    v0 = (v0 + v3) & MASK64
    v3 = v0 ^ rotl(v3, 21)
    v2 = (v2 + v1) & MASK64
    v1 = v2 ^ rotl(v1, 17)
    v2 = rotl(v2, 32)
    return v0, v1, v2, v3

def sha4(round_i, k0, k1, tw, X):
    v0 = 0x50726f736563636f ^ k0
    v1 = 0x43686f636f6c6174 ^ k1
    v2 = 0x01f32d1f4361f48e ^ k0
    v3 = ((round_i & 0xFF) << 56) ^ tw ^ k1
    v3 ^= X
    v0, v1, v2, v3 = sipround(v0, v1, v2, v3)
    v0 ^= X
    v2 ^= 16
    v0, v1, v2, v3 = sipround(v0, v1, v2, v3)
    v0, v1, v2, v3 = sipround(v0, v1, v2, v3)
    return (v0 ^ v1 ^ v2 ^ v3) & MASK64

def sigma(L):
    X = L & 0xffffffff
    return ((X << 32) | (((L >> 32) ^ X) & 0xffffffff)) & MASK64

def unsigma(L):
    X = (L >> 32) & 0xffffffff  # upper 32
    Y = L & 0xffffffff          # lower 32
    return (((Y ^ X) << 32) | X) & MASK64

def le64(b): return int.from_bytes(b, 'little')
def be64(x): return x.to_bytes(8, 'little')

# Keys/tweak lifted from .data segment
KEY  = bytes.fromhex("3d1790908e448599b0829e584cf2cd56")
TWEAK = bytes.fromhex("aa12de3344b4c6f8")

def decrypt_block(ct16, key=KEY, tweak=TWEAK):
    k0 = le64(key[:8]); k1 = le64(key[8:])
    tw = le64(tweak)
    L = le64(ct16[:8]); R = le64(ct16[8:])
    for i in (3, 2, 1, 0):
        L = unsigma(L)
        X = sha4(i, k0, k1, tw, L ^ R)
        L ^= X; R ^= X
    return be64(L) + be64(R)

def decrypt(hex_str, key=KEY, tweak=TWEAK):
    h = ''.join(hex_str.split()).lower()
    if len(h) % 32 != 0:
        raise ValueError("Ciphertext hex length must be a multiple of 32 (16 bytes per block).")
    data = unhexlify(h)
    out = bytearray()
    for off in range(0, len(data), 16):
        out += decrypt_block(data[off:off+16], key, tweak)
    return bytes(out)

def pretty_ascii(b):
    return ''.join(chr(x) if 32 <= x < 127 else '.' for x in b)

if __name__ == "__main__":
    ct_hex = "2f012a1e13a9a54b103424d8873cf1685e61ccc93f9dcae98e7b8f3ed0f862a66c72df99dcf517982af398a726d8d22f881635d66b8d0a5c67582f397a88f8b3131a5bab38430b60a068eaeb3638712f9376b89eb05adb31e8a8472f5060aab8ab880ed106e9d743cc34f7a593b25283bfcc23d8f03ddf79fa319ed355c85e9779ae9ee61878f0832c9b5825a664c8f922303edc220256435fdff0b650d7ea6c88f2961aa53da9f69bb7849f582c79794e5d6aa9bf2f3210013d5bb7c8a2b819dfa8a1ee0f1cf71172995895e5778796624178017ea89df5f949f7bf7a800a7a740d6070117f1742b41eb8cb5bc7f02b42a4476da7657deacf13147e1b879d40d20d0fd34c6d055aca1368b3803384c74d8108b1c58f14d77dc10b3e6d9a095aa9e13ec09f009a7c97e2cd9d883bab15de027e8ffe45552941f2f9e33e16b0f0afe3abbc865ff7c629ca794e4e1c159c53a0174a57f4f94b979e8551796a1418548916de67c13cf83400427edfcce65f41582ea54541f1dacf2a65b90607e123c2718f610e0ae2527ea40b3ad82a6692896d36b65a1e465bb7abcbaf7f44017f8d3535d4c1beeb3ed5be6a061fa3e6d9e656e910f7ed9a3080d7f044786c9a86e54ed1d87c03dde9c8b70703e6376aa65bcc02690a67cbaf1b0cbb3b944383bd87f6ab3f9aa2c29a3a2f260f844ee064ec3ed0e3c84c63979507e9ab3e7e94af9088d622aea6ca505048e859bef1138e5fa9115d1aab7249345aa0e356c781da361f8599a6ae63103f0c5f4754b2e8ce9b43c90e069aef908353b2db47722906bc097009d2fe651f1c9034d7782bd75a4a44050e46d7bcf93038ae1c1da11b5f07bbf8e1f56c26250b1b3636b5f884a5f0176bad7f1a1c74dad8c99e06f691686ce428e71b6cf62143c7e5b6aeb19fe68912bd8183287fec70205eba34fda3a873cdf753ac8389817610a93a04e5478de9a7246d157ac33033d12bb622f266ec6e332b816c87a4ff31adc191a5e7e548eed8ea93ddb5c77cf2ab8c1ab398bfd2ec86b3164fd546e41dbe3ec1054efb632c77fea51e6bd14c10185c8d963d84d9805faabc76bb5a1adbf6be795f4fc57d9bf3527cb0fa90afd1c1f83863161f33ea452c78c298ef1920bc54f7b24c4de64a3aa2312c338dc49efd3bb37029db5680049c85cbc803cc4ef1a013c435e529333f29faffe7fee727c86c08279e6d31005951c55dc571509ac997e24965caea41a3ebd6e99fa3280c0f499774abe2e85abb329a01f55692c12d6acc0bc81d870e584c58d5f3187b967bad9ceb02ab07a130f2864e7663926a1e8885ee76294eaf346e5c8cb0335bc58b1e6f47476448e081e25e09407e4ca6a7f00876843a1494e1c93778d23b7832d3a7d66fa69fb2589ba1f3af974d1dd651b5f7b718f924dc661ead149e9fbd6f11dced48e934f0"
    pt = decrypt(ct_hex)
    print("PT hex  :", hexlify(pt).decode())
    print("PT ascii:", pretty_ascii(pt))

```
