문제 분류
난이도 : 중
작성일자 : 2022-04-04
기본 숙지 개념 : flask, Web, CSRF 취약점, 웹 인증방식(쿠키,세션)
작업환경 : VIsual Studio Code, Web(Chrome)
사용언어 : Python_flask, HTML, JavaScript
작업도구 : -
추천 자료 : https://dreamhack.io/lecture/roadmaps/1
1. 사전탐색&정찰 (reconnaissance)
해당 문제 자체가 CSRF 취약점에 관련된 문제이기 때문에
우리는 이 문제를 CSRF 취약점을 통해 풀이하면 된다.
먼저 사이트에 접속해보면
"Please login" 로그인을 하라는 문구와 세개의 페이지가 보인다
▶ /vuln(csrf)
vuln 페이지는 말 그대로 취약점을 알려준다.
url에 삽입된 스크립트 코드 중 <,>,*,alert(1) 이 코드들이 출력되는걸 알 수 있는데
이는 <script> 코드는 사용 불가능 하지만
<,> 꺽쇠를 이용한 코드 사용이 가능하다는 뜻이다.
-> 즉 html 태그를 이용한 csrf 공격을 해야한다.
▶ /flag
/flag 페이지는 플래그를 얻기위한 코드를 삽입하는 곳으로
csrf 취약점을 발생시킬수 있는 코드를 찾아 넣으면 된다.(플래그 코드가 나올 수 있는 코드)
▶ /login
csrf-1 문제와 다르게 이번엔 계정을 통한 로그인이 필요한듯하다.
아마 admin 계정을 탈취해야하지 않을까 싶다.
2. 스캐닝 및 취약점 분석(Scanning and Enumeration)
먼저 제공된 코드를 조금씩 살펴보자
#전체코드
#!/usr/bin/python3
from flask import Flask, request, render_template, make_response, redirect, url_for
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**]"
users = {
'guest': 'guest',
'admin': FLAG
}
session_storage = {}
def read_url(url, cookie={"name": "name", "value": "value"}):
cookie.update({"domain": "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/")
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():
session_id = request.cookies.get('sessionid', None)
try:
username = session_storage[session_id]
except KeyError:
return render_template('index.html', text='please login')
return render_template('index.html', text=f'Hello {username}, {"flag is " + FLAG if username == "admin" else "you are not an admin"}')
@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", "")
session_id = os.urandom(16).hex()
session_storage[session_id] = 'admin'
if not check_csrf(param, {"name":"sessionid", "value": session_id}):
return '<script>alert("wrong??");history.go(-1);</script>'
return '<script>alert("good");history.go(-1);</script>'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
elif request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
try:
pw = users[username]
except:
return '<script>alert("not found user");history.go(-1);</script>'
if pw == password:
resp = make_response(redirect(url_for('index')) )
session_id = os.urandom(8).hex()
session_storage[session_id] = username
resp.set_cookie('sessionid', session_id)
return resp
return '<script>alert("wrong password");history.go(-1);</script>'
@app.route("/change_password")
def change_password():
pw = request.args.get("pw", "")
session_id = request.cookies.get('sessionid', None)
try:
username = session_storage[session_id]
except KeyError:
return render_template('index.html', text='please login')
users[username] = pw
return 'Done'
app.run(host="0.0.0.0", port=8000)
전체 코드들 중에서
내가 중요하다고 생각해서 살펴본 코드들은
#15번째 줄
#계정
users = {
'guest': 'guest',
'admin': FLAG
}
guest와 admin 계정 두개가 존재한다는 코드
#108번째 줄
#패스워드 교체 url
@app.route("/change_password")
def change_password():
pw = request.args.get("pw", "")
session_id = request.cookies.get('sessionid', None)
try:
username = session_storage[session_id]
except KeyError:
return render_template('index.html', text='please login')
users[username] = pw
패스워드를 수정하는 change_password 페이지가 있음을 알 수 있는데
해당 페이지에 pw를 변경하는 기능이 있기 때문에
flag를 통해 change_password 로 암호를 변경하는 파라미터를 보내면 된다.
해당 문제역시 csrf-1 문제와 동일하게 127.0.0.1(localhost)에서만 동작하기 때문에
flag를 통해 값을 전달해야한다.
3. 침투 (Gaining Access)
change_password는 세션 id가 존재해야만 접속할수 있기에 로그인을 먼저 해야한다.
먼저 login 에서 guest(pw:guest) 계정으로 로그인한다.
guest계정임을 알려줌과 동시에 admin이 아니라는 메시지가 출력된다.
이제 flag에 삽입할 코드를 짜보자
csrf가 가능한 코드는 아래와 같다.
<img src='url' width=0px height=0px>
<img src="link">
<img src=1 onerror="fetch('/link');">
<link rel="stylesheet" href="link">
아까 말했듯 자바 스크립트 태그는 전부 필터링 되므로
html 태그만 사용해야 한다.
해당 html 태그들중 <img> 태그를 활용해 공격 코드를 짜보았다.
// 변조된 공격코드
<img src="/change_password?pw=admin">
// "/change_password?pw=admin"
//change_password 에 pw를 admin으로 변경한다는 파라미터 전달
해당 공격코드를 /flag 에 삽입한다.
4. 권한상승 및 탈취 (Privilege Escalation)
change_password로 password 변경 파라미터를 img 태그로 보냈다.
그 후 ID : admin & PW : admin
으로 로그인 하면 플래그가 출력된다.
계정명은 pw와 동일하다.
아무튼 이렇게 csrf 취약점으로 admin 계정 탈취가 되었다.
'정보보안 > Web Hacking' 카테고리의 다른 글
드림핵 문제풀이 - Carve party // 웹 해킹 기초, JavaScript 문법기초, JavaScript 함수 (0) | 2022.08.02 |
---|---|
드림핵 문제풀이 - Simple_sqli // SQL Injection, query, 로그인우회, SQL (0) | 2022.05.09 |
드림핵 문제풀이 - XSS-2 // XSS, Cross Site Scripting, 우회, 인증우회 (0) | 2022.03.28 |
드림핵 문제풀이 - Session Basic // Cookie, Session, 인증, Web hacking (0) | 2022.03.21 |
파일다운로드 취약점 예제-1 // Dreamhack, Filedownload취약점 (0) | 2022.03.03 |
댓글