1. 엔드포인트 분석
/vuln
사용자가 입력 받은 파라미터를 소문자로 변경 후에, 필터링을 통해 xss 공격을 차단합니다.
@app.route("/vuln") # vuln 페이지 라우팅 (이용자가 /vuln 페이지에 접근시 아래 코드 실행)
def vuln():
param = request.args.get("param", "").lower() # 이용자가 입력한 param 파라미터를 소문자로 변경
xss_filter = ["frame", "script", "on"] # 세 가지 필터링 키워드
for _ in xss_filter:
param = param.replace(_, "*") # 이용자가 입력한 값 중에 필터링 키워드가 있는 경우, '*'로 치환
return param # 이용자의 입력 값을 화면 상에 표시
/memo
전역변수로 입력된 메모의 값을 가져와 해당 내용을 화면에 출력합니다.
@app.route('/memo') # memo 페이지 라우팅
def memo(): # memo 함수 선언
global memo_text # 메모를 전역변수로 참조
text = request.args.get('memo', '') # 이용자가 전송한 memo 입력값을 가져옴
memo_text += text + '\n' # 메모의 마지막에 새 줄 삽입 후 메모에 기록
return render_template('memo.html', memo=memo_text) # 사이트에 기록된 메모를 화면에 출력
/admin/notice_flag
전역변수로 선언된 메모에 플래그 값을 저장하여 기록합니다.
하지만 조건이 걸려있기 때문에 접속하는 아이피가 서버여야 하며, admin 계정으로 실행되어야 합니다.
@app.route('/admin/notice_flag') # notice_flag 페이지 라우팅
def admin_notice_flag():
global memo_text # 메모를 전역변수로 참조
if request.remote_addr != '127.0.0.1': # 이용자의 IP가 로컬호스트가 아닌 경우
return 'Access Denied' # 접근 제한
if request.args.get('userid', '') != 'admin': # userid 파라미터가 admin이 아닌 경우
return 'Access Denied 2' # 접근 제한
memo_text += f'[Notice] flag is {FLAG}\n' # 위의 조건을 만족한 경우 메모에 FLAG 기록
return 'Ok' # Ok 반환
2. 풀이
분석이 끝났으니 스크립트를 작성할 수 있는 flag 엔드포인트로 이동합시다.
아래의 html 태그를 사용한다면
/flag -> /vuln -> /admin/notice_flag -> /memo 로 순차적으로 이동하면서
결국엔 memo라는 전역변수에 플래그 값이 입력되어 출력이 됩니다.
<img src="/admin/notice_flag?userid=admin" />


1. 엔드포인트 분석
/vuln
사용자가 입력 받은 파라미터를 소문자로 변경 후에, 필터링을 통해 xss 공격을 차단합니다.
@app.route("/vuln") # vuln 페이지 라우팅 (이용자가 /vuln 페이지에 접근시 아래 코드 실행) def vuln(): param = request.args.get("param", "").lower() # 이용자가 입력한 param 파라미터를 소문자로 변경 xss_filter = ["frame", "script", "on"] # 세 가지 필터링 키워드 for _ in xss_filter: param = param.replace(_, "*") # 이용자가 입력한 값 중에 필터링 키워드가 있는 경우, '*'로 치환 return param # 이용자의 입력 값을 화면 상에 표시
/memo
전역변수로 입력된 메모의 값을 가져와 해당 내용을 화면에 출력합니다.
@app.route('/memo') # memo 페이지 라우팅 def memo(): # memo 함수 선언 global memo_text # 메모를 전역변수로 참조 text = request.args.get('memo', '') # 이용자가 전송한 memo 입력값을 가져옴 memo_text += text + '\n' # 메모의 마지막에 새 줄 삽입 후 메모에 기록 return render_template('memo.html', memo=memo_text) # 사이트에 기록된 메모를 화면에 출력
/admin/notice_flag
전역변수로 선언된 메모에 플래그 값을 저장하여 기록합니다.
하지만 조건이 걸려있기 때문에 접속하는 아이피가 서버여야 하며, admin 계정으로 실행되어야 합니다.
@app.route('/admin/notice_flag') # notice_flag 페이지 라우팅 def admin_notice_flag(): global memo_text # 메모를 전역변수로 참조 if request.remote_addr != '127.0.0.1': # 이용자의 IP가 로컬호스트가 아닌 경우 return 'Access Denied' # 접근 제한 if request.args.get('userid', '') != 'admin': # userid 파라미터가 admin이 아닌 경우 return 'Access Denied 2' # 접근 제한 memo_text += f'[Notice] flag is {FLAG}\n' # 위의 조건을 만족한 경우 메모에 FLAG 기록 return 'Ok' # Ok 반환
2. 풀이
분석이 끝났으니 스크립트를 작성할 수 있는 flag 엔드포인트로 이동합시다.
아래의 html 태그를 사용한다면
/flag -> /vuln -> /admin/notice_flag -> /memo 로 순차적으로 이동하면서
결국엔 memo라는 전역변수에 플래그 값이 입력되어 출력이 됩니다.
<img src="/admin/notice_flag?userid=admin" />

