반응형
문제설명
간단한 논리를 깨다.
코드분석
hidden_dir = '/dir_' + str(randint(1, 99999999999999999))
- hidden_dir 은 /dir_???값으로 이뤄져있음. ???은 1~99999999999999999 사이값
@app.route('/', methods=['GET','POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
elif request.method == 'POST':
if not login_check():
return render_template('login.html', error='Something wrong with the login details. Try again.')
else:
return redirect(hidden_dir)
else:
return make_response(405)
- login_check() 가 false면 login.html을 login_check()이 true면 redirct(hiine_dir)을 출력
@app.route(hidden_dir)
def hidden_endpoint():
return render_template('hidden.html', FLAG=open('flag.txt').read())
- hidden_dir 경로 요청 시 hidden.html 출력하고 flag 획득
def login_check():
uname = request.form.get('uname', '')
password = request.form.get('password', '')
if not uname and not password:
return False
connection = connect("logical.db")
cursor = connection.cursor()
query = ("SELECT uname, password FROM users WHERE password = '{}'").format(md5(password.encode()).hexdigest())
usrname = cursor.execute(query).fetchall()
name = usrname[0][0] if usrname and usrname[0] and usrname[0][0] else ''
return name == uname
- 로그인 체크 검사
- uname과 password 입력받음 / 없으면 공백입력
- uname과 password 둘 다 비어있으면 False 반환
- uname과 password 입력받으면 logical.db 연결하고 실행
- query는 password를 인코딩하고 md5로 해시한 다음 16진수 문자열로 만든 값
- usrname은 query문 실행한 값
- name은 usrname이 있고, usrname[0]이 존재하고, usrname[0][0]이 존재하면 usrname[0][0]을 넣고, 아니라면 공백 넣음
- 최종 uname과 name 비교
취약점 분석
- uname을 쿼리 조건에 사용하지 않음
- 쿼리 결과가 없을 때(공백일 때) usrname값도 ' '(공백)으로 name도 ' '(공백) uname이 ' '(공백)으로 로그인 우회 가능
익스플로잇
먼저 POST 요청을 가로챔
uname은 공백으로 해주고 password만 전송해주면 hidden_dir 획득
→ DB에는 아직 아무런 데이터가 없기에 password로 아무거나 보내도 쿼리 결과가 없음

즉,
name == uname이 '' == '' // True
login_check() 함수가 우회됨
hidden_dir획득

반응형
'Dreamhack 워게임 > Lv.1' 카테고리의 다른 글
| Pearfect Markdown (0) | 2026.02.15 |
|---|---|
| Simple Note Manager (0) | 2026.02.15 |
| baby-ai (0) | 2026.02.14 |
| what-is-my-ip (0) | 2026.02.14 |
| Dream Badge (0) | 2026.02.09 |