SSTI (Server-Side Template Injection)
Practica de explotación SSTI
import os
from flask import Flask, request, render_template_string, session, redirect, url_for
app = Flask(__name__)
app.secret_key = 'supersecretkey' # Clave secreta para sesiones
# Base de datos simple en memoria (solo para prueba)
users = {
'admin': {'password': 'admin123', 'avatar': 'default.jpg', 'name': 'Admin'},
'user': {'password': 'user123', 'avatar': 'default.jpg', 'name': 'Usuario'}
}
@app.route('/', methods=['GET', 'POST'])
def login():
""" Página de inicio de sesión """
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if username in users and users[username]['password'] == password:
session['username'] = username
return redirect(url_for('profile'))
return '''
<form method="post">
Usuario: <input name="username" style="padding: 10px; margin: 10px; font-size: 16px;" required><br>
Contraseña: <input name="password" type="password" style="padding: 10px; margin: 10px; font-size: 16px;" required><br>
<input type="submit" value="Iniciar sesión" style="background-color: #4CAF50; color: white; padding: 12px 20px; font-size: 16px; border-radius: 5px; cursor: pointer; border: none;">
</form>
'''
@app.route('/profile', methods=['GET', 'POST'])
def profile():
""" Página de perfil con vulnerabilidad SSTI """
if 'username' not in session:
return redirect(url_for('login'))
username = session['username']
avatar = users[username]['avatar']
if request.method == 'POST':
new_name = request.form['new_name']
# ⚠️ **Vulnerabilidad SSTI intencionada** ⚠️
users[username]['name'] = render_template_string(new_name) # Se almacena directamente el input del usuario
return render_template_string('''
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Perfil de {{ name }}</title>
<style>
body {
font-family: 'Arial', sans-serif;
background-color: #f4f7fc;
color: #333;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
}
h1 {
color: #4CAF50;
margin-bottom: 20px;
}
img {
border-radius: 50%;
margin-bottom: 20px;
}
form {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
width: 300px;
display: flex;
flex-direction: column;
align-items: center;
}
input[type="text"], input[type="file"] {
padding: 10px;
font-size: 1em;
width: 100%;
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 10px;
}
input[type="submit"] {
background-color: #4CAF50;
color: white;
padding: 12px 20px;
font-size: 1em;
border-radius: 5px;
border: none;
cursor: pointer;
transition: background-color 0.3s;
}
input[type="submit"]:hover {
background-color: #45a049;
}
a {
text-decoration: none;
color: #4CAF50;
font-weight: bold;
margin-top: 20px;
}
a:hover {
color: #45a049;
}
</style>
</head>
<body>
<h1>Perfil de {{ name }}</h1>
<img src="{{ url_for('static', filename=avatar) }}" alt="Avatar de {{ name }}" width="150">
<h3>Actualizar nombre</h3>
<form method="post">
Nuevo nombre: <input type="text" name="new_name" required><br>
<input type="submit" value="Actualizar">
</form>
<h3>Subir avatar</h3>
<form action="{{ url_for('upload') }}" method="post" enctype="multipart/form-data">
<input type="file" name="avatar" required><br>
<input type="submit" value="Subir imagen">
</form>
<br>
<a href="{{ url_for('logout') }}">Cerrar sesión</a>
</body>
</html>
''', name=users[username]['name'], avatar=avatar)
@app.route('/upload', methods=['POST'])
def upload():
""" Cargar un avatar directamente en static """
if 'username' not in session:
return redirect(url_for('login'))
username = session['username']
file = request.files['avatar']
if file:
# Asegúrate de usar una ruta absoluta
file_path = os.path.join(app.root_path, 'static', file.filename)
file.save(file_path)
users[username]['avatar'] = file.filename # Actualizamos el avatar del usuario con el nuevo archivo
return redirect(url_for('profile'))
@app.route('/logout')
def logout():
""" Cierra la sesión del usuario """
session.pop('username', None)
return redirect(url_for('login'))
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000) # Usa otro puerto si Apache ya usa el 80



Explicación detallada SSTI
Vulnerabilidad SSTI (Server-Side Template Injection)
Impactos de SSTI:
Cómo prevenir la vulnerabilidad SSTI:
Tabla de Payloads y Bypasses para SSTI
Protección contra SSTI
Conclusión
PreviousXEE (XML External Entity Injection)NextPrototype Pollution (Contaminación de prototipos) JavaScript
Last updated