문제 설명
STEP 1과 2를 거쳐서 FLAG 페이지에 도달하면 플래그를 출력할 수 있다.
일단 먼저 / 페이지를 보면 index.html 을 렌더링 하고있다.
index.html 페이지를 보면 STEP1, STEP2, FLAG 페이지가 존재한다.
step1 페이지의 코드를 보자
step1 소스코드
@app.route("/step1", methods=["GET", "POST"])
def step1():
#### 풀이와 관계없는 치팅 방지 코드
global step1_num
step1_num = int.from_bytes(os.urandom(16), sys.byteorder)
####
if request.method == "GET":
prm1 = request.args.get("param", "")
prm2 = request.args.get("param2", "")
step1_text = "param : " + prm1 + "\nparam2 : " + prm2 + "\n"
if prm1 == "getget" and prm2 == "rerequest":
return redirect(url_for("step2", prev_step_num = step1_num))
return render_template("step1.html", text = step1_text)
else:
return render_template("step1.html", text = "Not POST")
global step1_num 으로 전역변수로 step1_num을 쓴다는 얘기이다.
step1_num변수에 int.from_bytes()함수를 사용하여 어떠한 값으로 넣었다.
os.urandom(16)함수는 랜덤으로 16 byte단위의 unsigned 수치값을 만들어 준다.
sys.byteorder은 현재의 바이트표기법이 little인지 big인지를 표시한다.
int.from_bytes함수의 형식
classmethod int.from_bytes(bytes, byteorder, *, signed=False)
이 함수는 bytes를 첫번째 인자로 받고 두번째로는 little엔디안 방식인지 big엔디안 방식인지 파악한다 그리고 signed가 True이면 signed로 받고 False이면 unsigned로 받는다. 그것을 int(정수)의 형태로 만들어준다.
따라서 해당 코드를 해석하면 os.urandom(16)으로 16바이트의 랜덤값을 만든 다음 sys.byteorder으로 현재 바이트 표기법을 알아내서 int.from_bytes함수에 넣는다. signed는 디폴트로 False라서 양수 값만 받는 unsinged형식으로 된다.
**그냥 랜덤한 정수값 만드는 소리다.**
GET방식으로 param과 param2를 받는다.
param이 getget이고 param2가 rerequest이면 step2로 렌더링하고 아까 랜덤값을 넘겨준다.
param을 getget, param2를 rerequest로 요청을 하였다.
랜덤값을 prev_step_num을 넘겨받아서 step2로 이동한다.
step2 페이지로 왔다.
step2 페이지 코드를 보자.
step2 소스코드
@app.route("/step2", methods=["GET", "POST"])
def step2():
if request.method == "GET":
#### 풀이와 관계없는 치팅 방지 코드
if request.args.get("prev_step_num"):
try:
prev_step_num = request.args.get("prev_step_num")
if prev_step_num == str(step1_num):
global step2_num
step2_num = int.from_bytes(os.urandom(16), sys.byteorder)
return render_template("step2.html", prev_step_num = step1_num, hidden_num = step2_num)
except:
return render_template("step2.html", text="Not yet")
return render_template("step2.html", text="Not yet")
####
else:
return render_template("step2.html", text="Not POST")
step2로 GET방식으로 입력 받는다.
인자로 prev_step_num을 받는다.
만약 prev_step_num이 아까 랜덤값인 step1_num 참이면 step2_num 전역 변수를 불러오고 step2_num변수에 또 랜덤값을 만들었다.
그리고 step1_num과 step2_num을 포함하여 step2.html을 렌더링한다.
hidden_num이 뭐냐 살펴보자.
쥐새끼처럼 type을 hidden으로 안보이게 숨어있다. 그리고 value값으로 hidden_num을 받는다.
이름이 check인걸 보니 몰래 숨겨서 check할 용도인것 같다.
개발자 도구에서 쉽게 쥐새끼를 찾을 수 있다.
값은 랜덤한 값이다.
근데 여기서 자세히 보면 from으로 action을 하는데 /flag로 post 형식으로 action을 한다.
그림 6에서 제출 버튼을 누르면 flag페이지로 post 방식으로 보낸다는 뜻이다.
flag 소스코드를 확인해 보자.
flag 소스코드
@app.route("/flag", methods=["GET", "POST"])
def flag():
if request.method == "GET":
return render_template("flag.html", flag_txt="Not yet")
else:
#### 풀이와 관계없는 치팅 방지 코드
prev_step_num = request.form.get("check", "")
try:
if prev_step_num == str(step2_num):
####
prm1 = request.form.get("param", "")
prm2 = request.form.get("param2", "")
if prm1 == "pooost" and prm2 == "requeeest":
return render_template("flag.html", flag_txt=FLAG)
else:
return redirect(url_for("step2", prev_step_num = str(step1_num)))
return render_template("flag.html", flag_txt="Not yet")
except:
return render_template("flag.html", flag_txt="Not yet")
자 일단 GET으로 보내면 가차없이 flag_txt에 Not yet을 보내서 플래그를 안보여준다.
POST로 보내야한다.
POST로 보냈을때 prev_step_num로 인자를 받는데 check라는 이름의 인자를 받는다.
prev_step_num값이 step2_num인 아까 쥐새끼 값이랑 같으면 param, param2을 get으로 받는다.
그것이 pooost이고 requeeest이면 flag_txt에 FLAG을 담아서 flag.html을 렌더링한다.
param에 pooost와 param2에 requeeest값을 입력한다.
그림 10은 그림 9에서 제출을 누른 후의 경우이다.
마지막 인자로 check를 보내는 것을 확인할 수 있다.
응답으로 flag를 받았다.
'WEB hacking > 드림핵(dreamhack)' 카테고리의 다른 글
드림핵 command-injection-1 Write Up (0) | 2023.07.15 |
---|---|
드림핵 simple_sqli Write Up (0) | 2023.06.20 |
드림핵 csrf-2 (Level1) Write up (0) | 2023.04.22 |
드림핵 csrf-1 (Level1) Write up (0) | 2023.04.20 |
드림핵 xss-2 (Level1) Write up (0) | 2023.03.29 |