---
# System prepended metadata

title: Flare-On 11 Challenge 5 - sshd
tags: [Flare-On, "Reverse Engineering\_"]

---

# **Flare-On 11 Challenge 5 - sshd**

> Our server in the FLARE Intergalactic HQ has crashed! Now criminals are trying to sell me my own data!!! Do your part, random internet hacker, to help FLARE out and tell us what data they stole! We used the best forensic preservation technique of just copying all the files on the system for you.

This challenge left me quite confused because I didn't know what to do at first. After some searching, I finally got a clue.

## Coredump
I found the `sshd.core.93794.0.0.11.1725917676` file inside the `var/lib/systemd/coredump/`

```
$ file sshd.core.93794.0.0.11.1725917676
sshd.core.93794.0.0.11.1725917676: ELF 64-bit LSB core file, x86-64, version 1 (SYSV), SVR4-style, from 'sshd: root [priv]', real uid: 0, effective uid: 0, real gid: 0, effective gid: 0, execfn: '/usr/sbin/sshd', platform: 'x86_64'
```

This is a core dump file, specifically the core dump of the sshd process. A core dump is created when a program (in this case, `sshd`) crashes or encounters a serious error, and it contains a snapshot of the program's memory at the time of the crash. It can be useful for developers or system administrators to diagnose and analyze the cause of the crash.

In addition, there are some other noteworthy details:
* From 'sshd: root [priv]': This core dump was generated by the sshd process, which is an SSH service for the root user (a process running with administrative privileges).
* Real uid: 0, Effective uid: 0, Real gid: 0, Effective gid: 0: This shows that the process is running as root (uid = 0 and gid = 0).
* Execfn: '/usr/sbin/sshd': The executable for this process is sshd, the SSH service.

After much analysis with no result, I eventually figured out how to set up the environment with Docker.

```
sudo docker image import ssh_container.tar ssh_container
sudo docker container run -d -it --name sshd_instance ssh_container bash
sudo docker attach sshd_instance
```

Next, I performed debugging on the `sshd.core.93794.0.0.11.1725917676` file.

![image](https://hackmd.io/_uploads/ByZBWqenkl.png)

I used the `bt` (backtrace) command to check why the process crashed.

![image](https://hackmd.io/_uploads/Skev-5l3kx.png)

We can see that the program tried to access an invalid memory address. The crash occurred at `0x00007f4a18c8f88f` within the `liblzma.so.5` library.

## **Liblzma**

I proceeded to analyze `liblzma` (specifically `liblzma.so.5.4.1`) with IDA and found the location of the backdoor.

![image](https://hackmd.io/_uploads/S15Gr5lnkx.png)

The program checks if it is running with root privileges (`!getuid()`) and if the value at `*a2` is `-985630136`.

If both conditions are met, the program triggers the backdoor by executing shell code that has been decrypted using the ChaCha20 algorithm. Specifically:

* First, the algorithm is initialized at `sub_93F0`

    ![image](https://hackmd.io/_uploads/S1O9q5xnye.png)
    
* Then, it retrieves `0x0F96` bytes from `unk_23960`.

    ![image](https://hackmd.io/_uploads/B1mFi5lhke.png)
    
    ![image](https://hackmd.io/_uploads/B13Kicx3kl.png)

* Finally, it decrypts the retrieved data at `sub_9520` and executes it.

At this point, I needed to decrypt the shell code to see what it would do. First, we need to find the `key` and `nonce`. The key starts at `rbp+0x4` and the nonce starts at `rbp+0x24`.

![image](https://hackmd.io/_uploads/Hky26qlhJl.png)

![image](https://hackmd.io/_uploads/BkGy09ln1l.png)

## **Shellcode**

After obtaining the shell code, I analyzed it with IDA. After some time, I gathered the following information:

![image](https://hackmd.io/_uploads/rk9NGslhkg.png)

* First, through `sub_CD2` -> `sub_A93`, the program initializes the ChaCha20 algorithm with the constant `"expand 32-byte K"`. The difference here is the character K.

    ![image](https://hackmd.io/_uploads/H10vXsg31l.png)

    * Therefore, we can conclude that the key will start at `rbp-0x1278` and the nonce will start at `rbp-0x1258`.

* Then, it decrypts the information at `rbp-0x1148` using ChaCha20 via `sub_D49`.

At this point, I just needed to extract the data and use the ChaCha20 algorithm with the constant `"expand 32-byte K"` to decrypt it. However, the result seemed incorrect.

After referring to a few Write-ups, I realized where I went wrong. Specifically, we know that at the time of the crash, the program was executing a `call` instruction from `sub_9820`. Due to the fact that the library does not contain `"RSA_public_decrypt "`, `v10` becomes NULL, which results in a segmentation fault.

![image](https://hackmd.io/_uploads/SyNbyFZ2ke.png)

At this point, the current frame address is `0x7ffcc6601ea0`. From `sub_9820` to `sub_DC2`, there are two `call` instructions and six `push` instructions.

![image](https://hackmd.io/_uploads/HJCc_n-3Jl.png)        

![image](https://hackmd.io/_uploads/Hy9zWFW3Jl.png)        

![image](https://hackmd.io/_uploads/Hyfw-YZn1g.png)        

![image](https://hackmd.io/_uploads/SJqY-F-n1l.png)         

Therefore, the `rbp` at this point should be 0x7ffcc6601ea0 - 8 * 8 = 0x7ffcc6601e60.

## **Get flag**
![image](https://hackmd.io/_uploads/HJaqVKbnkg.png)

```python=
import struct

def rotate(v, c):
    return ((v << c) & 0xffffffff) | (v >> (32 - c))

def quarter_round(state, a, b, c, d):
    state[a] = (state[a] + state[b]) & 0xffffffff
    state[d] ^= state[a]
    state[d] = rotate(state[d], 16)

    state[c] = (state[c] + state[d]) & 0xffffffff
    state[b] ^= state[c]
    state[b] = rotate(state[b], 12)

    state[a] = (state[a] + state[b]) & 0xffffffff
    state[d] ^= state[a]
    state[d] = rotate(state[d], 8)

    state[c] = (state[c] + state[d]) & 0xffffffff
    state[b] ^= state[c]
    state[b] = rotate(state[b], 7)

def chacha20_block(key, counter, nonce, sigma_bytes):
    assert len(key) == 32
    assert len(nonce) == 12
    assert len(sigma_bytes) == 16

    key_words = struct.unpack('<8L', key)
    nonce_words = struct.unpack('<3L', nonce)
    sigma_words = struct.unpack('<4L', sigma_bytes)

    state = [
        *sigma_words,
        *key_words,
        counter,
        *nonce_words
    ]

    working_state = state.copy()

    for _ in range(10):  # 20 rounds = 10 double rounds
        # column rounds
        quarter_round(working_state, 0, 4, 8, 12)
        quarter_round(working_state, 1, 5, 9, 13)
        quarter_round(working_state, 2, 6,10, 14)
        quarter_round(working_state, 3, 7,11, 15)
        # diagonal rounds
        quarter_round(working_state, 0, 5,10, 15)
        quarter_round(working_state, 1, 6,11, 12)
        quarter_round(working_state, 2, 7, 8, 13)
        quarter_round(working_state, 3, 4, 9, 14)

    output = [(working_state[i] + state[i]) & 0xffffffff for i in range(16)]
    return b''.join(struct.pack('<L', word) for word in output)

def chacha20_encrypt(key, nonce, counter, plaintext, sigma=b"expand 32-byte k"):
    ciphertext = b""
    for i in range(0, len(plaintext), 64):
        block = chacha20_block(key, counter, nonce, sigma)
        keystream = block[:min(64, len(plaintext) - i)]
        chunk = bytes([a ^ b for a, b in zip(plaintext[i:i+64], keystream)])
        ciphertext += chunk
        counter += 1
    return ciphertext

def main():
    key = bytes.fromhex("8d ec 91 12 eb 76 0e da 7c 7d 87 a4 43 27 1c 35 d9 e0 cb 87 89 93 b4 d9 04 ae f9 34 fa 21 66 d7") 
    nonce = bytes.fromhex("11 11 11 11 11 11 11 11 11 11 11 11") 
    counter = 0

    custom_sigma = b"expand 32-byte K"

    enc = bytes.fromhex("A9 F6 34 08 42 2A 9E 1C 0C 03 A8 08 94 70 BB 8D AA DC 6D 7B 24 FF 7F 24 7C DA 83 9E 92 F7 07 1D 02 63 90 2E C1 58")
    hex_val = chacha20_encrypt(key, nonce, counter, enc, sigma=custom_sigma).hex()

    flag = bytes.fromhex(hex_val)
    print(f"Message: {flag}")

if __name__ == "__main__":
    main()

```
