문제
여러 기능과 입력받은 URL을 확인하는 봇이 구현된 서비스입니다.
CSRF 취약점을 이용해 플래그를 획득하세요.
문제 수정 내역2023.07.18 css, html 제공 및 read_url() 코드 일부가 변경되었습니다.
풀이
위는 접속하면 나오는 문제 페이지이다.
csrf로 접속한다.
csrf 페이지는 param=<script>alert(1)</script> 형태로 이루어져있다.
화면단에 표기에서는 script가 *로 치환되어 있는 것을 확인할 수 있다.
memo 페이지에서는 memo=hello 로 memo의 변수설정을 추가함에 따라 출력되는 값이 다르다.
필자가 memo=hello 를 memo=mymemo로 변경한다면, 출력은 mymemo가 뒤에 덧붙여서 출력된다.
notice flag 페이지에서는 Access Denied 가 출력된다.
flag 페이지에서는 입력 값을 넣을 수 있는 input 박스가 설정되어 있다.
단순한 스크립트를 한번 찍어보도록 한다.
confirm(1) 이 아닌 good로 처리된다.
파이썬 코드를 확인해보도록 하자
#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
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"}):
cookie.update({"domain": "127.0.0.1"})
try:
service = Service(executable_path="/chromedriver")
options = webdriver.ChromeOptions()
for _ in [
"headless",
"window-size=1920x1080",
"disable-gpu",
"no-sandbox",
"disable-dev-shm-usage",
]:
options.add_argument(_)
driver = webdriver.Chrome(service=service, options=options)
driver.implicitly_wait(3)
driver.set_page_load_timeout(3)
driver.get("http://127.0.0.1:8000/")
driver.add_cookie(cookie)
driver.get(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"}):
url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}"
return read_url(url, cookie)
@app.route("/")
def index():
return render_template("index.html")
@app.route("/vuln")
def vuln():
param = request.args.get("param", "").lower()
xss_filter = ["frame", "script", "on"]
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):
return '<script>alert("wrong??");history.go(-1);</script>'
return '<script>alert("good");history.go(-1);</script>'
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"
memo_text += f"[Notice] flag is {FLAG}\n"
return "Ok"
app.run(host="0.0.0.0", port=8000)
- /vuln 페이지는 받은 파라미터를 소문자처리하고, [ frame, script, on ] 이 존재한다면 *로 치환하도록 설계되어있다.
- /flag 페이지는 get으로 이동했을 때, input 창이 나오고, input 창에서 클릭을 해 post 로 넘기게 된다.
정상 작동 시 good, 문제 발생 시 wrong?? 이 나오며, 전페이지로 돌아오게 된다. - /memo 페이지는 memo_text에 누적되서 쌓이는 것을 확인할 수 있다.
- /admin/notice_flag 페이지는 127.0.0.1 일 경우, userid가 admin이 아닐경우 Denied 가 표시된다.
또한, 조건이 만족될 시 memo_text 에 더해진다.
이제 input 창에 직접 대입을 해본다
img 태그를 사용해서 notice_flag에 매개변수를 사용하여 시도를 한다.
<img src=/admin/notice_flag?userid=admin>
memo_text에 포함되어 memo 페이지에 출력되는 것을 볼 수 있다.
'Dreamhack > Dreamhack(1단계)' 카테고리의 다른 글
xss-2 (웹 해킹) (0) | 2024.10.11 |
---|---|
image-storage (웹 해킹) (1) | 2024.10.08 |
xss-1 (웹 해킹) (0) | 2024.10.08 |
simple_sqli (웹 해킹) (0) | 2024.10.08 |
php-1 (웹 해킹) (1) | 2024.08.28 |