ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Dreamhack - web-ssrf
    wargame/Dreamhack 2024. 3. 26. 01:13

    [LEVEL 2 - web-ssrf - web]

     

     

    이 문제의 목표는 SSRF 취약점을 통해 플래그를 획득하는 것이다.

    flask로 작성된 image viewer 서비스이며, 플래그는 /app/flag.txt에 있다고 한다.

     

     

     

     

    우선 문제 파일을 다운로드 받고 app.py 코드를 살펴본다.

     

     

    /img_viewer은 GET과 POST 요청을 처리한다.

     

    • GET : img_viewer.html을 렌더링
    • POST : 이용자가 입력한 url에 HTTP 요청을 보내고, 응답을 img_viewer.html의 인자로 하여 렌더링

     

    img_viewer 함수를 보면 이용자가 POST로 전달한 url에 HTTP 요청을 보내고, 응답을 반환한다. 그런데 조건문에서 서버 주소에 "127.0.0.1", "localhost"이 포함된 URL로의 접근을 막는다. 즉, 이 url 필터링을 우회하면 SSRF를 통해 내부 HTTP 서버에 접근할 수 있다.

     

     

    cf) URL 필터링

    • URL 필터링 : URL에 포함된 문자열을 검사하여 부적절한 URL로의 접근을 막는 보호 기법. 제어 방식에 따라 크게 아래 두 가지로 나뉨.
    • 블랙리스트 필터링 : URL에 포함되면 안되는 문자열로 블랙리스트를 만들고, 이를 악용하여 이용자의 접근을 제어함. 블랙리스트 필터링에는 항상 예외가 있을 수 있어서 유의해야 함.
    • 화이트리스트 필터링 : 접근을 허용할 URL로 화이트리스트를 만들고, 이용자가 화이트리스트 외의 URL에 접근하려고 하면 차단함.

     

     

    그리고 run_local_server 함수를 살펴보면, 파이썬의 기본 모듈인 http를 이용하여 127.0.0.1의 임의 포트에 HTTP 서버를 실행하는 것을 알 수 있다.

    그리고 http.server.HTTPServer()의 두 번째 인자로 http.server.SimpleHTTPRequestHandler를 전달하면, 현재 디렉터리를 기준으로 URL이 가리키는 리소스를 반환하는 웹 서버가 생성된다.

     

    여기서 호스트가 127.0.0.1 이므로, 외부에서 이 서버에 직접 접근하는 것을 불가하다.

     

     

     

     

    위 내용을 참고하여, 아래와 같이 URL 필터링을 우회하면 된다.

     

    • 127.0.0.1과 매핑된 도메인 이름 사용 : 임의의 도메인 이름 구매 후 DNS 서버 등록하면 원하는 IP 주소(127.0.0.1)와 연결 가능. 이후 등록한 이름이 IP 주소로 Resolve됨. 즉, 그 이름을 url로 사용하면 필터링 우회 가능. 이미 127.0.0.1에 매핑된 "*.vcap.me" 이용하는 방법도 있음.
    • 127.0.0.1의 alias 이용 : 여러 방식으로 IP 표기 가능. ex) 127.0.0.1은 각 자릿수를 16진수로 변환한 0x7f.0x00.0x00.0x01 에서 .을 제거한 0x7f000001 또는 이를 10진수로 변환한 2130706433 또는 각 자리에서 0을 생략한 127.1 또는 127.0.1 등과 같은 호스트를 가리킴. 또한, 127.0.0.1부터 127.0.0.255까지의 IP는 루프백(loop-back) 주소라고 하여 모두 로컬 호스트를 가리킴.
    • localhost의 alias 이용 : URL에서 호스트와 스키마는 대소문자 구분X. 즉, "localhost"의 임의 문자를 대문자로 바꿔도 같은 호스트임.

     

     

    필터링을 우회할 수 있는 localhost URL

    • http://vcap.me:8000/
    • http://0x7f.0x00.0x00.0x01:8000/
    • http://0x7f000001:8000/
    • http://2130706433:8000/
    • http://Localhost:8000/
    • http://127.0.0.255:8000/

     

     

     

     

     

    문제 환경에 접속하면 나오는 처음 화면이다.

     

     

     

     

     

    로컬 호스트의 8000번 포트에는 문제 서버가 실행되고 있는데, 위와 같이 /img_viewer 페이지에서 http://Localhost:8000/ 을 쓰고 View를 누르면 문제 인덱스 페이지를 인코딩한 이미지가 반환된다.

    즉, 앞서 정리해 본 필터링을 우회할 수 있는 localhost URL들은 로컬 호스트를 가리키면서, 필터링을 우회할 수 있는 URL이라는 것이 증명된 것이다.

    개발자도구로 해당 코드 부분 더블클릭해서 자세히 보면 base64로 인코딩 된 인덱스 페이지의 소스 코드가 보인다.

     

     

    이제 마지막으로 앞서 알게 된 url을 활용하여 랜덤한 포트를 찾는 파이썬 코드만 작성하면 무차별 대입 공격으로 포트를 찾을 수 있다.

    아까 app.py에서 내부 HTTP 서버는 포트 번호가 1500 이상 1800 이하인 임의 포트에서 실행되고 있다는 걸 알 수 있었다.

     

    문제 설명에 플래그는 "/app/flag.txt"에 있다고 했는데, 내부 HTTP 서버가 "/app"에서 실행되고 있으니까, 해당 서버의 /flag.txt를 읽으면 플래그값을 얻을 수 있다.

     

     

     

     

     

    드림핵 강의 Exercise에 나와있는 것처럼 find_port.py 파일을 작성한다.

     

     

     

     

     

    python find_port.py 15070 실행 후,

    내부 포트 번호가 1537라는 것을 알아냈다.

     

     

     

     

     

    위와 같이 /img_viewer 페이지에서 http://Localhost:1537/flag.txt 를 입력하고 View를 누른다.

     

     

     

     

     

    그러면 깨진 이미지 파일이 하나 뜬다.

     

     

     

     

     

    개발자 도구를 다시 열어서 Elements 탭에서 이미지 파일 부분을 보면 base64로 인코딩된 문자열이 있다.

     

     

     

     

    <img src="data:image/png;base64, REh7NDNkZDIxODkwNTY0NzVhN2YzYmQxMTQ1NmExN2FkNzF9">

     

    자세히 보면 위와 같다.

    이제 뒷부분 REh7NDNkZDIxODkwNTY0NzVhN2YzYmQxMTQ1NmExN2FkNzF9 을 base64 디코딩하면 플래그값이 나올 것이다.

     

     

     

    드림핵 툴즈 Cyber Chef 기능을 이용하여 Base64 디코딩하면 위와 같다.

     

     

     

     

    플래그를 획득했다!

     

    'wargame > Dreamhack' 카테고리의 다른 글

    Dreamhack - BMP Recovery  (1) 2024.05.01
    Dreamhack - sleepingshark  (0) 2024.05.01
    Dreamhack - file-download-1  (1) 2024.03.25
    Dreamhack - image-storage  (0) 2024.03.18
    Dreamhack - broken-png  (0) 2024.03.18
Designed by Tistory.