wargame

write-up 05

burrri 2023. 5. 11. 15:37

dreamhack | webhacking

01. csrf-1 

더보기

CSRF (cross-site-request forgery) 이란?

웹 어플리케이션에서 발생 가능한 보안 취약점 중 하나로 인증된 사용자의 권한을 이용하여, 사용자의 의지와 상관없이 공격자가 의도한 요청을 서버로 보내는 것.

xss를 이용한 공격이 사용자가 특정 웹사이트를 신용하는 점을 노리는 공격이라면, csrf는 특정 웹사이트가 사용자의 웹 브라우저를 신용하는 상태를 노리는 공격. 

 

+ 막기 위해서 사용되는 대중적인 보안 메커니즘은 CSRF토큰을 사용하는 것. 이는 서버가 웹페이지를 로드할 때, 사용자에게 임의의 토큰을 부여하고, 이 토큰을 요청과 함께 제출하도록 하는 방식. 토큰의 검증을 통해, 해당 요청이 실제로 사용자의 의지에 따라 이루어졌는지 확인 가능

 

 

https://dreamhack.io/wargame/challenges/26/

문제의 코드를 보쟈 ㅎ 하나하나 코드를 보기 귀찮아 주석으로 달아놨다

#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
import urllib
import os

app = Flask(__name__)
app.secret_key = os.urandom(32)

try:
    FLAG = open("./flag.txt", "r").read()
except:
    FLAG = "[**FLAG**]"


def read_url(url, cookie={"name": "name", "value": "value"}):  # url과 쿠키를 인자로 받는 함수
    cookie.update({"domain": "127.0.0.1"})  # cookie의 도메인을 127.0.0.1로 업데이트
    try:
        options = webdriver.ChromeOptions()  # 크롬 브라우저 옵션 설정
        for _ in [
            "headless", 
            "window-size=1920x1080", 
            "disable-gpu",  
            "no-sandbox",  
            "disable-dev-shm-usage",
        ]:
            options.add_argument(_)  # 위의 브라우저 옵션을 추가
       
        driver = webdriver.Chrome("/chromedriver", options=options)
        driver.implicitly_wait(3)
        driver.set_page_load_timeout(3) 
        driver.get("http://127.0.0.1:8000/")  # 접속url : 127.0.0.1의 8000포트
        driver.add_cookie(cookie)  # 인자로 받은 쿠키값으로 브라우저 쿠키 업로드
        driver.get(url)  # 브라우저가 접속할 url을 url로 설정하고 접속
    except Exception as e:
        driver.quit()
        print(str(e))
        # return str(e)
        return False
    driver.quit()
    return True


def check_csrf(param, cookie={"name": "name", "value": "value"}):
    # 인자로 받은 param을 urlib.parse.quote함수를 통해 인코딩
    url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}"
    return read_url(url, cookie)  # url값을 매개인자로 전달하며 read_url호출


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/vuln")
def vuln():
    # request.args.get함수를 통해 GET요청으로 전달된 param값을 가져오고, lower함수로 parma값을 모두 소문자로 변경
    param = request.args.get("param", "").lower()
    xss_filter = ["frame", "script", "on"]  # xss공격을 방지하기 위해 걸러낼 문자열
    for _ in xss_filter:
        param = param.replace(_, "*")
    return param


@app.route("/flag", methods=["GET", "POST"])
def flag():
    if request.method == "GET":
        return render_template("flag.html")
    elif request.method == "POST":
        param = request.form.get("param", "")
        if not check_csrf(param):  # check_csrf가 false를 반환시에
            # 공격자한테 wrong반환
            return '<script>alert("wrong??");history.go(-1);</script>'

        return '<script>alert("good");history.go(-1);</script>'  # good


memo_text = ""


@app.route("/memo")
def memo():
    global memo_text
    text = request.args.get("memo", None)
    if text:
        memo_text += text  # 상당히 길어지겟구만
    return render_template("memo.html", memo=memo_text)


@app.route("/admin/notice_flag")
def admin_notice_flag():
    global memo_text
    if request.remote_addr != "127.0.0.1":
        return "Access Denied"
    if request.args.get("userid", "") != "admin":
        return "Access Denied 2"
    # addr가 127.0.0.1이면서 userid == "admin"인 경우
    memo_text += f"[Notice] flag is {FLAG}\n"  # 전역변수에 flag추가되서 memo에서 확인가능
    return "Ok"


app.run(host="0.0.0.0", port=8000)

 

각 페이지를 정리하면

/vul : param을 받아 xss_filtering을 해서 param을 반환

script가 필터링 된 모습

/flag : post로 데이터를 보내면 check_scrf () -> read_url()을 통해 domain:127.0.0.1이 추가된다. 

/memo : 전역변수에 차곡차곡 덧붙여줘버리기 

/admin/notice_flag: addr가 127.0.0.1이면서 userid == "admin"인 경우에 전역변수를 통해 flag 추가

 

 

 

 즉, flag를 찾기 위해 위의 두 조건을 만족해야한다. 이때, /flag를 통해 /admin뭐시기 페이지로 연결한 후, 파라미터로 userid == "admin"조건을 추가해서 exploit해야한다.  이때 vul에서 필터링되지않는 img태그를 통해 공격을 실행한다. 

<img scr="/admin/notice_flag?userid==admin />
쟈란

 

 

 

02. file-download-1

https://dreamhack.io/wargame/challenges/37/

사이트를 열어보면 다음과 같다.

/
안녕?응안녕못해

메모내용을 입력하고 업로드하면 바로 업로드된 메모를 볼 수 있다.

 

 

이제 코드를 보자 

#!/usr/bin/env python3
import os
import shutil

from flask import Flask, request, render_template, redirect

from flag import FLAG

APP = Flask(__name__)

UPLOAD_DIR = 'uploads'


@APP.route('/')
def index():
    files = os.listdir(UPLOAD_DIR)
    return render_template('index.html', files=files)


@APP.route('/upload', methods=['GET', 'POST'])
def upload_memo():
    if request.method == 'POST':  # post요청시
        filename = request.form.get('filename')  # 파일이름이랑
        content = request.form.get('content').encode('utf-8')  # 파일내용 겟

        if filename.find('..') != -1:  # filename에 '..'이 있을 경우
            return render_template('upload_result.html', data='bad characters,,')

        with open(f'{UPLOAD_DIR}/{filename}', 'wb') as f:  # 업로드된 파일을 바이너리모드wb로 open
            f.write(content)  # 내용저장

        return redirect('/')

    return render_template('upload.html')


@APP.route('/read')
def read_memo():
    error = False
    data = b''

    filename = request.args.get('name', '')  # name매개변수를 가져와 filename에 저장

    try:
        with open(f'{UPLOAD_DIR}/{filename}', 'rb') as f:
            data = f.read()  # 파일을 read
    except (IsADirectoryError, FileNotFoundError):
        error = True  # excepion발생시 error설정

    return render_template('read.html',
                           filename=filename,
                           content=data.decode('utf-8'),
                           error=error)  # read.html 템플릿을 렌더링하고 내용 전달


if __name__ == '__main__':
    if os.path.exists(UPLOAD_DIR):  # upload dir경로에 dir가 있는지 확인
        shutil.rmtree(UPLOAD_DIR)  # remove

    os.mkdir(UPLOAD_DIR)  # upload_dir경로에 새로운 dir생성

    APP.run(host='0.0.0.0', port=8000)

생성한 파일의 주소를 보면 filename을 읽어서 반환하는 것을 알 수 있다. 

문제에서 flag.py를 열어서 flag를 찾으라고 하는 거보니 filename을 통해 exploit하면 될 것 같다. 

 

상대적 디렉토리를 이용해서 공격하면 /upload에서 bad response가 나와서 /read에서 exploit을 해주어야한다 !

위의 주소를 넣어주면 

 

 

dreamhack |  forensic

03. snowing!

https://dreamhack.io/wargame/challenges/241/

파일을 다운로드받아보면 snow.jpeg파일과 flag.txt가 있다. txt파일을 hxd에 넣어보니

분명 txt파일에서 공백으로 보였던 부분이 요상한 거로 가득 차 있다. 

공백

whitespace steganography를 통해 공백에 은닉된 데이터를 찾아낼 수 있다. 여러 툴 중에 snow를 unix기반의 os에서 이용해보았다. C옵션을 사용하여 은닉된 데이터를 추출가능했다 ! 

 

'wargame' 카테고리의 다른 글

write-up 07  (0) 2023.05.25
write-up 06  (0) 2023.05.18
write-up 04  (0) 2023.05.04
write-up 03  (0) 2023.04.05
write-up 02  (0) 2023.03.30