Antes que nada, no soy especialista en el tema, estoy aprendiendo y divirtiendome haciendo uso de herramientas de IA.
Dicho eso... Estuve realizando un desarrollo para un amigo, tiene un PDF con 100 hojas, con registros de transacciones bancarias. Necesita tener estos registros en un excel para poder procesarlo despues.
Con el uso de Github copilot, realice un script para la extraccion de datos usando python y algunas librerias... Tres doritos despues... Y como desayuno, almuerzo y ceno sistemas, levante un servidor flask, para servir el script atravez de un endpoint en un front con html, css y js puro y duro.
Durante el proceso de interaccion con Copilot, me levanto una alerta que una parte del código tenia una vulnerabilidad del tipo "path injection". Bastante interesante esta alerta :D
Y como estoy en busqueda de aprender mas sobre seguridad informatica, acá estoy blogeando para dejar registro de este journey...
Acá empieza a hablar la IA.
Un atacante podría explotar una vulnerabilidad de path injection manipulando la entrada del usuario para acceder a archivos o directorios no autorizados en el sistema de archivos del servidor. Aquí tienes un ejemplo de cómo podría hacerlo:
Ejemplo de Path Injection
Supongamos que tienes una ruta en tu aplicación Flask que permite a los usuarios subir archivos y guardarlos en el servidor. Si no validas o sanitizas el nombre del archivo, un atacante podría enviar un nombre de archivo malicioso para intentar acceder a archivos sensibles en el sistema.
Código Vulnerable
@app.route('/upload', methods=['POST'])
def upload():
file = request.files['file']
filename = file.filename # Nombre del archivo proporcionado por el usuario
save_path = os.path.join('uploads', filename)
# Guardar el archivo sin validar el nombre del archivo
with open(save_path, 'wb') as f:
f.write(file.read())
return 'File uploaded successfully'
Ataque de Path Injection
Un atacante podría enviar una solicitud con un nombre de archivo malicioso como ../../etc/passwd. Esto podría resultar en la siguiente ruta:
uploads/../../etc/passwd
Si el servidor no valida ni sanitiza el nombre del archivo, el archivo podría ser guardado en una ubicación no deseada, permitiendo al atacante sobrescribir archivos sensibles en el sistema.
Solución: Validar y Sanitizar la Entrada
Para evitar este tipo de ataques, debes validar y sanitizar el nombre del archivo antes de guardarlo. Aquí tienes una versión segura del código:
Código Seguro
import os
import re
from flask import Flask, render_template, request, send_file
import requests
from datetime import datetime
app = Flask(__name__)
def sanitize_filename(filename):
# Eliminar caracteres no permitidos del nombre del archivo
return re.sub(r'[^a-zA-Z0-9_\-\.]', '', filename)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/upload', methods=['POST'])
def upload():
file = request.files['file']
response = requests.post('http://127.0.0.1:5001/pdf-to-excel', files={'file': file})
# Obtener la fecha actual
current_date = datetime.now().strftime("%Y%m%d")
# Obtener el nombre original del archivo sin la extensión
original_filename = os.path.splitext(file.filename)[0]
# Sanitizar el nombre del archivo
sanitized_filename = sanitize_filename(original_filename)
# Crear el nuevo nombre del archivo con la fecha al final
output_filename = f'{sanitized_filename}_{current_date}.xlsx'
# Crear la ruta completa del archivo
save_path = os.path.join('uploads', output_filename)
# Asegurarse de que la ruta sea absoluta
save_path = os.path.abspath(save_path)
# Guardar el archivo Excel resultante localmente con el nuevo nombre
with open(save_path, 'wb') as f:
f.write(response.content)
# Enviar el archivo Excel de vuelta al usuario con el nuevo nombre
return send_file(save_path, as_attachment=True, download_name=output_filename)
if __name__ == '__main__':
app.run(port=5000, debug=True)
Explicación del Código Seguro
Sanitizar el Nombre del Archivo: La función sanitize_filename elimina caracteres no permitidos del nombre del archivo utilizando una expresión regular.
Crear la Ruta Completa del Archivo: La función os.path.join se utiliza para crear la ruta completa del archivo, combinando el directorio de destino (uploads) con el nombre del archivo sanitizado (output_filename).
Asegurarse de que la Ruta sea Absoluta: La función os.path.abspath convierte la ruta en una ruta absoluta para evitar manipulaciones con rutas relativas.
Guardar el Archivo: Finalmente, el archivo se guarda en la ruta especificada.
Resumen
Path Injection: Un atacante puede manipular la entrada del usuario para acceder a archivos o directorios no autorizados.
Validación y Sanitización: Siempre valida y sanitiza las entradas del usuario para evitar ataques de path injection.
Rutas Absolutas: Utiliza rutas absolutas para evitar manipulaciones con rutas relativas.
Siguiendo estas prácticas, puedes proteger tu aplicación contra ataques de path injection y mejorar la seguridad general de tu sistema.
Acá le pregunté el how to para llegar a realizar el ataque. Además que me dejé registrados logs para poder depurar estos casos.
Para asegurar tu aplicación contra ataques de path injection, es útil entender cómo se pueden realizar estos ataques. Aquí te muestro cómo un atacante podría intentar explotar tu aplicación y cómo puedes detectar estas actividades en los logs.
1. Desde el Navegador
Manipulando Formularios HTML
Un atacante puede manipular un formulario HTML para enviar un nombre de archivo malicioso.
Paso a Paso:
- Abre la página web con el formulario de subida de archivos.
- Usa las herramientas de desarrollo del navegador (F12) para inspeccionar el formulario.
- Modifica el valor del campo de archivo para incluir una ruta maliciosa, por ejemplo, ../../etc/passwd.
- Envía el formulario.
- Detección en Logs: Registra el nombre del archivo recibido en los logs.
import logging
# Configurar el logging
logging.basicConfig(filename='app.log', level=logging.INFO)
@app.route('/upload', methods=['POST'])
def upload():
file = request.files['file']
logging.info(f"Archivo recibido: {file.filename}")
# Resto del código...
2. Usando Herramientas como Postman
Enviando Solicitudes HTTP Manipuladas
Un atacante puede usar Postman para enviar solicitudes HTTP manipuladas directamente al servidor.
Paso a Paso:
- Abre Postman y configura una solicitud POST a http://127.0.0.1:5000/upload.
- En el cuerpo de la solicitud, selecciona form-data y añade un campo file con un archivo cuyo nombre sea ../../etc/passwd.
- Envía la solicitud.
- Detección en Logs: Registra el nombre del archivo recibido en los logs.
3. Mediante Scripts Automatizados
Usando Scripts en Python
Un atacante puede usar scripts en Python para enviar solicitudes HTTP maliciosas.
Ejemplo de Script en Python:
import requests
url = 'http://127.0.0.1:5000/upload'
files = {'file': ('../../etc/passwd', open('local_file.txt', 'rb'))}
response = requests.post(url, files=files)
print(response.text)
Detección en Logs: Registra el nombre del archivo recibido en los logs.
Implementación de Logs para Detección
Aquí tienes una implementación completa que incluye logging para detectar nombres de archivos sospechosos:
from flask import Flask, render_template, request, send_file
import requests
from datetime import datetime
import os
import re
import logging
# Configurar el logging
logging.basicConfig(filename='app.log', level=logging.INFO)
app = Flask(__name__)
def sanitize_filename(filename):
# Eliminar caracteres no permitidos del nombre del archivo
return re.sub(r'[^a-zA-Z0-9_\-\.]', '', filename)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/upload', methods=['POST'])
def upload():
file = request.files['file']
logging.info(f"Archivo recibido: {file.filename}")
response = requests.post('http://127.0.0.1:5001/pdf-to-excel', files={'file': file})
# Obtener la fecha actual
current_date = datetime.now().strftime("%Y%m%d")
# Obtener el nombre original del archivo sin la extensión
original_filename = os.path.splitext(file.filename)[0]
# Sanitizar el nombre del archivo
sanitized_filename = sanitize_filename(original_filename)
# Crear el nuevo nombre del archivo con la fecha al final
output_filename = f'{sanitized_filename}_{current_date}.xlsx'
# Crear la ruta completa del archivo
save_path = os.path.join('uploads', output_filename)
# Asegurarse de que la ruta sea absoluta
save_path = os.path.abspath(save_path)
# Guardar el archivo Excel resultante localmente con el nuevo nombre
with open(save_path, 'wb') as f:
f.write(response.content)
logging.info(f"Archivo guardado en: {save_path}")
# Enviar el archivo Excel de vuelta al usuario con el nuevo nombre
return send_file(save_path, as_attachment=True, download_name=output_filename)
if __name__ == '__main__':
app.run(port=5000, debug=True)
Resumen
Desde el Navegador: Manipulando formularios HTML o usando herramientas de desarrollo del navegador.
Usando Herramientas como Postman: Enviando solicitudes HTTP manipuladas directamente al servidor.
Mediante Scripts Automatizados: Usando scripts en Python, Bash, o cualquier otro lenguaje para enviar solicitudes HTTP maliciosas.
Detección en Logs: Registra el nombre del archivo recibido y otras actividades sospechosas en los logs para detectar posibles ataques.
Implementando estas medidas, puedes mejorar la seguridad de tu aplicación y detectar actividades sospechosas que podrían indicar un intento de ataque.
Conclución: Bajo este contexto de aprendizaje y desarrollo haciendo pair programming con la IA, me parece sumamente útil este tipo de alertas en la plataforma para detectar vulnerabilidades. Hoy me levante pensando que iba a deployar a heroku el back y acá me encuentro descubriendo distintas vulnerabilidades :D
happy coding!
Top comments (0)