소스코드나 보러가자
#!/usr/bin/env python3
from flask import Flask, request, render_template
app = Flask(__name__)
try:
FLAG = open("./flag.txt", "r").read() # flag is here!
except:
FLAG = "[**FLAG**]"
@app.route('/', methods=['GET', 'POST'])
def index():
menu_str = ''
org = FLAG[10:29]
org = int(org)
st = ['' for i in range(16)]
for i in range (0, 16):
res = (org >> (4 * i)) & 0xf
if 0 < res < 12:
if ~res & 0xf == 0x4:
st[16-i-1] = '_'
else:
st[16-i-1] = str(res)
else:
st[16-i-1] = format(res, 'x')
menu_str = menu_str.join(st)
# POST
if request.method == "POST":
input_str = request.form.get("menu_input", "")
if input_str == str(org):
return render_template('index.html', menu=menu_str, flag=FLAG)
return render_template('index.html', menu=menu_str, flag='try again...')
# GET
return render_template('index.html', menu=menu_str)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
이 Flask 애플리케이션은 사용자가 특정 조건을 충족하는 입력값(menu_input)을 제공하면 플래그(FLAG)를 반환하는 구조입니다. 주어진 코드를 분석하고, 메뉴 번호를 알아내고 FLAG를 얻는 방법을 설명하겠습니다.
1. 코드 분석
FLAG 변수
try:
FLAG = open("./flag.txt", "r").read() # flag is here!
except:
FLAG = "[**FLAG**]"
- FLAG는 ./flag.txt 파일에서 읽어오며, 파일이 없거나 읽기 실패 시 [**FLAG**]를 기본값으로 설정합니다.
메뉴 문자열 생성
org = FLAG[10:29]
org = int(org)
st = ['' for i in range(16)]
for i in range(0, 16):
res = (org >> (4 * i)) & 0xf
if 0 < res < 12:
if ~res & 0xf == 0x4:
st[16-i-1] = '_'
else:
st[16-i-1] = str(res)
else:
st[16-i-1] = format(res, 'x')
menu_str = menu_str.join(st)
- FLAG[10:29]:
- FLAG 문자열에서 10번째부터 29번째 문자까지 추출.
- 이를 정수로 변환(int)하여 org에 저장.
- 16진수 처리:
- org의 값을 4비트씩 분리하여 처리.
- res = (org >> (4 * i)) & 0xf는 오른쪽으로 4비트씩 쉬프트한 후, 하위 4비트만 추출.
- 조건 처리:
- 0 < res < 12:
- res가 1~11 사이일 때:
- ~res & 0xf == 0x4: 결과가 4이면 _로 변환.
- 그렇지 않으면, res를 문자열로 저장.
- res가 1~11 사이일 때:
- 그렇지 않을 경우, res를 16진수로 변환(format(res, 'x')).
- 0 < res < 12:
- 결과:
- menu_str는 org의 각 4비트를 특정 규칙으로 변환하여 구성된 문자열.
플래그 검증
if input_str == str(org):
return render_template('index.html', menu=menu_str, flag=FLAG)
return render_template('index.html', menu=menu_str, flag='try again...')
- 사용자가 입력한 menu_input이 org와 같은지 비교.
- 조건 충족 시:
- FLAG를 반환.
- 조건 불충족 시:
- 'try again...'을 반환.
2. 메뉴 번호(menu_input)를 알아내는 방법
핵심 조건:
- menu_input == str(org)
- 따라서, 입력값(menu_input)은 org와 같아야 합니다.
org 값 유도 방법:
- FLAG 값 추출:
- FLAG는 FLAG[10:29]을 사용하여 org를 계산.
- 예를 들어, FLAG가 "ABCDEFGHIJKLMNOPQRST"라면:
- FLAG[10:29] = "KLMNOPQRST".
- 이를 int로 변환하면 org가 계산됨.
- FLAG 위치 접근:
- 코드를 실행할 수 있는 권한이 있다면 FLAG 값을 직접 확인.
- 디버깅을 통해 FLAG 값을 추출.
- 파일 경로(./flag.txt)에 접근 가능한 경우 파일 내용 읽기.
- Brute Force:
- FLAG의 가능한 값들을 대입하여 menu_str의 결과를 맞춰보는 방식.
3. FLAG를 얻는 방법
- FLAG의 10번째부터 29번째 문자 추출:
- 예를 들어, FLAG가 "ABCDEFGHIJKLMNOPQRST"라면:
- FLAG[10:29] = "KLMNOPQRST".
- 이를 정수로 변환:
org = int("KLMNOPQRST") # 가정된 값
- 예를 들어, FLAG가 "ABCDEFGHIJKLMNOPQRST"라면:
- 웹 요청 보내기:
- menu_input에 계산된 org 값을 넣어 POST 요청.
- 예제:
curl -X POST http://localhost:8000 -d "menu_input=123456789" # org 값
- 출력 확인:
- 조건 충족 시 FLAG 반환.
정리
- FLAG를 얻기 위해서는 FLAG[10:29] 값을 정확히 알아내거나 추측해야 합니다.
- 애플리케이션 소스 코드가 있다면, 디버깅 또는 코드 실행을 통해 FLAG를 유도할 수 있습니다.
- 만약 FLAG를 알 수 없다면, 가능한 값을 대입하여 brute force로 계산 가능합니다.
만약 org의 값이 54라면 54를 이진수로 변환 시 0011 0110이 되는데
첫 번째 for문에서 i가 0일 때 0만큼 시프트 연산이 적용되면 0011 0110이 된다
( >> x : x의 수만큼 비트를 화살표 방향 따라 이동, 이동 후 비는 공간은 0으로 대체)
그 후 & 0xf를 통해 16진수 f(==이진수 1111)와 and연산을 하게 되면 st리스트에는 0110이 저장이 된다.
(and연산은 두 값이 모두 1일 경우 1이 되는 연산이다)
두 번째 for문이 시작되면 i가 1이 되고 시프트 연산을 4만큼 해야 한다.
0011 0110에서 4만큼 옆으로 이동하게 되면 0000 0011의 모습이 되고 and 연산을 통해 마지막 4비트만 확인하게 되므로
st리스트에는 0011이 저장이 된다.
해당 문제는 이러한 연산을 거쳐서 나온 값이 1_c_3_c_0__ff_3e 임을 알려준다.
그럼 이 16진수 문자열을 다시 비트로 변경 후 10진수로 바꿔준다면 올바른 input이 될 것이다.
출처 ㅣ https://st-together.tistory.com/62
https://st-together.tistory.com/62
캬 보통 코드를 짜면 되긴한데, 알고리즘 완벽히 이해 후 그냥 코드 없이 품;;; 레전드
풀이가 너무 아름다워서 줏어왔다..
그러려면 알고리즘을 확실히 이해해야한다.
res = (org >> (4 * i)) & 0xf
위 코드가 어떻게 동작하는지 보자.
우리가 입력한 정수(org)를 오른쪽으로 (4 * i)만큼 시프트 연산을 수행한 뒤 0xf를 AND연산한다.
예를 들어 우리가 입력한 정수가 '1000000000000000000' 이라고 가정해보자
(위 코드에 주석에서 설명했듯이 입력값은 19자리 정수이다)
오른쪽으로 시프트 연산을 수행하려면 입력한 정수를 아래와 같이 2진수로 바꿔야한다.
'110111100000101101101011001110100111011001000000000000000000'
여기서 i = 1이면, 4만큼 오른쪽으로 시프트 연산을 수행하면 오른쪽 끝 4자리가 사라지고 왼쪽 끝 4자리에 0이 들어온다.
'000011011110000010110110101100111010011101100100000000000000' 0000
그리고 16진수인 0xf를 2진수로 바꾸고 AND연산을 하면 아래와 같이 '0000'이 나온다.
000011011110000010110110101100111010011101100100000000000000
x 1111
000011011110000010110110101100111010011101100100000000000000
간단히 연산결과만 보자면 i=0일 때, res에는 입력한 값의 맨 끝 4자리 '0000'이 들어가고
'110111100000101101101011001110100111011001000000000000000000'
i=1일 때, res에는 그 다음 4자리
'110111100000101101101011001110100111011001000000000000000000'
i=2일 때는, res에는 그 다음 4자리가 들어가는 식이다.
'110111100000101101101011001110100111011001000000000000000000'
이렇게 16번 반복되는동안 오른쪽 끝부터 차례대로 4자리씩 res에 들어가고 res의 숫자에 따라 문자를 출력한다.
1. 0 < res < 12 (res가 1이상 11이하일 경우)
1-1) res = 11일때(~res & 0xf == 0x4, res를 NOT 시키고 0xf와 AND연산했을때 0x4인 경우, 즉 res가 11인 경우)
문자열 '_'를 출력한다.
1-2) res가 1이상 10이하일 경우
정수를 문자형으로 출력한다.
2. res가 12이상일 경우
16진수 포맷으로 변환하여 출력한다.
이렇게 출력된 문자열이 '1_c_3_c_0__ff_3e' 이어야 한다.
알고리즘을 역설계할 필요없이 '1'은 '0001', '_'은 '1011', 'c'는 '1100' 이런식으로 쭉 써보면
'0001101111001011001110111100101100001011101111111111101100111110'가 나오고 이를 10진수로 변환하면
'1_c_3_c_0__ff_3e' 문자열을 얻을 수 있는 입력값 ' 2002760202557848382'를 알 수 있다.
출처 ㅣ https://ylong.tistory.com/33
약간 마지막이 이해가 안가는데, 나중에 쌤한테 물어봐야겠다
'Dreamhack > Dreamhack Wargame (Challenge)' 카테고리의 다른 글
[95] IT 비전공자 [dreamhack]Broken Buffalo Wings문제 풀기 (1) | 2024.12.13 |
---|---|
[94] IT 비전공자 [dreamhack]wargame.kr type confusion문제 풀기 (1) | 2024.12.12 |
[92] IT 비전공자 [dreamhack]Format String Bug문제 풀기 (1) | 2024.12.10 |
[91] IT 비전공자 [dreamhack]Windows Search문제 풀기 (1) | 2024.12.09 |
[90] IT 비전공자 [dreamhack][wargame.kr] fly me to the moon문제 풀기 (0) | 2024.12.08 |