소스코드를 보러가자 main.py 파일
from flask import Flask, render_template, request
import ipaddress
import urllib.parse
import urllib.request
import urllib.error
app = Flask(__name__)
try:
with open('flag') as f:
flag = f.read()
except FileNotFoundError:
flag = 'flag{this_is_a_fake_flag}'
@app.route('/')
def form():
return render_template('index.html')
@app.route('/vuln', methods=['POST'])
def vuln():
name = request.form.get('vulntest')
try:
address = ipaddress.ip_address(name)
if address.version == 4:
return "no..."
url = urllib.parse.urlparse(f"http://[{address.exploded}]:5000/localonly")
if url.netloc != f'[{address.exploded}]:5000':
print(url.netloc, f'[{address.exploded}]')
return "no..."
req = urllib.request.Request(url.geturl())
return urllib.request.urlopen(req).read().decode('utf-8')
except ValueError:
return "no..."
except urllib.error.URLError:
return "connection refused"
@app.route('/localonly', methods=['GET'])
def localonly():
addr = ipaddress.ip_address(request.remote_addr)
if addr.is_loopback and addr.version == 4:
return flag
else:
return 'not loopback'
if __name__ == '__main__':
app.run('0.0.0.0', 5000)
/vuln경로에서
IPv4는 no..반환
입력받은 ipv6 주소를 기반으로 url을 만들고 (urllib.parse.urlparse), address.exploded로 ipv6 주소를 문자열 형식으로 변환한 후 url.netloc과 비교하여 다르면 no, 맞으면 request를 보내는 듯 하다.
만약 성공적으로 url 요청이 보내지면 -> 응답 반환 ( return urllib.request.urlopen(req).read().decode('utf-8')
※ url.netloc
url.netloc는 url의 네트워크 위치값(호스트, 포트) 반환
출처ㅣhttps://melonbbang-ruffy.tistory.com/26
@app.route('/localonly', methods=['GET'])
def localonly():
addr = ipaddress.ip_address(request.remote_addr)
if addr.is_loopback and addr.version == 4:
return flag
else:
return 'not loopback'
/localonly경로에
flag가 있다
IPv4 루프백 주소로 127.0.0.1에서 요청이 오면 플래그가 나오는데
위에서는 ipv6만 입력 받는데(?)
IPv4 127.0.0.1 == IPv6 0:0:0:0:0:0:0:0:1
IPv4 루프백 주소를 IPv6 루프백 주소로 인식되게해야한다
하지만 외부 ip나 ipv6는 not loopback인걸?
IPv6 루프백 주소는 0:0:0:0:0:0:0:0:1이며 ::1/128로 표기할 수 있습니다.
이 Flask 애플리케이션에서 localhost 접근을 제한하는 보안 메커니즘이 있지만, 이를 우회하기 위해 IPv6 주소와 관련된 취약점을 악용할 수 있습니다. 애플리케이션의 동작을 분석하고 이를 우회하는 방법을 설명하겠습니다.
분석
- 취약점이 존재하는 /vuln 라우트:
- 사용자 입력 name을 받아 IP 주소로 변환하고 검증합니다.
- 검증 후 해당 IP 주소를 기반으로 http://[IPv6_address]:5000/localonly에 요청을 보냅니다.
- 요청하는 URL은 url.netloc과 f'[{address.exploded}]:5000'을 비교하여 악의적인 조작을 방지하려고 합니다.
- 하지만 URL 파싱 및 IP 주소 처리 과정에서 일부 우회가 가능합니다.
- /localonly 라우트:
- 요청의 원격 주소 (request.remote_addr)가 loopback(127.0.0.1)이며 IPv4인지 확인합니다.
- 조건을 만족하면 flag를 반환합니다.
- 외부 IP나 IPv6 주소에서 요청 시 not loopback이 반환됩니다.
- 우회 가능성:
- ipaddress.ip_address와 urllib.parse를 사용하는 과정에서 IPv6 특수 주소나 변형된 loopback 주소(::1)를 활용하면 접근 가능성이 있습니다.
- 예를 들어, IPv6의 loopback 주소 ::1은 localhost를 의미하지만, 변형된 표현(::0:1 등)도 동일하게 해석됩니다.
- URL 파싱 시 제대로 비교하지 못하는 경우 이를 악용할 수 있습니다.
공격 방법
- IPv6 Loopback 주소 활용:
- IPv6 loopback 주소는 ::1이며, http://[::1]:5000/localonly는 localhost에 요청하는 것과 동일합니다.
- 변형된 IPv6 표현을 사용하여 우회를 시도합니다. 예:
- ::1
- ::0:1
- 0:0:0:0:0:0:0:1
- 특수 URL 인코딩 사용:
- urllib.parse.urlparse가 URL 검증에 사용되므로 특수 인코딩을 통해 URL을 조작할 수 있습니다. 예:
- %5b::1%5d는 [::1]과 동일하게 해석됩니다.
- urllib.parse.urlparse가 URL 검증에 사용되므로 특수 인코딩을 통해 URL을 조작할 수 있습니다. 예:
공격 시나리오
- 요청 흐름:
- /vuln에 POST 요청을 보내며 vulntest 필드에 변형된 IPv6 loopback 주소를 입력합니다.
- 서버는 이를 검증하고, /localonly로 요청을 전송합니다.
- 요청의 원격 주소가 ::1로 확인되면 flag를 반환합니다.
- 예제 요청:
- curl -X POST http://<서버 IP>:5000/vuln -d "vulntest=::1"
- 결과:
- 서버가 /localonly에 요청을 보내 request.remote_addr를 확인하고, 127.0.0.1 또는 ::1인 경우 flag를 반환합니다.
코드 수정이 필요할 경우 보안 강화
- IPv6 변형 표현 처리:
- 모든 loopback 변형 표현(::1, ::0:1, 0:0:0:0:0:0:0:1 등)을 검사해야 합니다.
- 이를 방지하려면 ipaddress.ip_address를 사용해 직접 확인해야 합니다.
- URL 검증 강화:
- urllib.parse.urlparse를 사용하는 대신, URL 문자열을 엄격히 검증합니다.
- 헤더 추가 검증:
- 요청 헤더(Host 또는 X-Forwarded-For)를 추가로 확인하여 신뢰할 수 있는 요청인지 검증합니다.
위의 방법을 통해 플래그를 획득하거나 보안을 강화할 수 있습니다. 😊
https://melonbbang-ruffy.tistory.com/26
[Dreamhack] Where-is-localhost
https://dreamhack.io/wargame/challenges/1560 Where-is-localhostlocalhost... 과연 어떻게 접근할까? Edit (10/22/2024 7:49): fix error in codedreamhack.io오랜만에 웹 해킹 문제문제명만 보면 localhost를 접근해서 플래그를 알아
melonbbang-ruffy.tistory.com
https://notes.networklessons.com/ipv6-ipv4-mapped-ipv6-address
IPv6 - IPv4-mapped IPv6 address
IPv4-mapped IPv6 addresses are a special type of IPv6 addresses used to represent an IPv4 address in an IPv6 format. This can be particularly useful in a
notes.networklessons.com
The syntax for an IPv4-mapped IPv6 address is ::FFFF:w.x.y.z, where w.x.y.z is the decimal representation of the IPv4 address.
For example, the IPv4-mapped IPv6 address of
192.0.2.128 is
::FFFF:192.0.2.128
출처 : https://notes.networklessons.com/ipv6-ipv4-mapped-ipv6-address
ㅇㅎ192.168.10.13을 IPv6에서는 ::FFFF:192.168.10.13으로 나타내기 가능하다한다 그러면
127.0.0.1은 ::FFFF:172.0.0.1로 나타내기 가능하다는 소리
::ffff:127.0.0.1
시도해보자
DH{L0C4L_En0ugh_IPv4_mapped_v6-a44dc189}
'Dreamhack > Dreamhack Wargame (Challenge)' 카테고리의 다른 글
[142] IT 비전공자 [dreamhack]VBR문제 풀기 (0) | 2025.01.30 |
---|---|
[141] IT 비전공자 [dreamhack]datestring문제 풀기 (0) | 2025.01.29 |
[139] IT 비전공자 [dreamhack]legacyopt문제 풀기 (0) | 2025.01.27 |
[138] IT 비전공자 [dreamhack]safeprime문제 풀기 (0) | 2025.01.27 |
[137] IT 비전공자 [dreamhack]baby-ai문제 풀기 (0) | 2025.01.25 |