-
<WEB Hacking> ServerSide: SSRFWEB Hacking 2022. 1. 5. 11:46
■SSRF(Server-Side Request Forgery)
웹 서비스는 외부에서 접근할 수 없는 내부망의 기능을 사용할 때가 있다. 이 서비스는 관리자만 이용해야 하므로 외부에서 접근할 수 없는 내부망에 위치한다. 외부에서 직접 접근할 수 없지만, 웹 서비스는 내부망 서비스와 통신할 수 있다.
만약 공격자가 SSRF 취약점을 통해 웹 서비스의 권한으로 요청을 보낼 수 있다면, 외부에서 간접적으로 내부망 서비스를 이용할 수 있게된다.
웹 서비스가 보내는 요청을 변조하기 위해서는 요청 내에 공격자의 입력값이 포함되어야 한다.
ex) 웹 서비스가 공격자가 입력한 URL에 요청을 보내는 경우
URL에 공격자의 번호가 사용되는 경우
공격자가 입력한 값이 HTTP Body에 포함되는 경우
■웹 서비스가 공격자가 입력한 URL에 요청을 보내는 경우
from flask import Flask, request import requests app = Flask(__name__) @app.route("/image_downloader") def image_downloader(): image_url = request.args.get("image_url", "") response = requests.get(image_url) return (response.content, 200, {"Content-Type": response.headers.get("Content-Type", "")}) @app.route("/request_info") def request_info(): return request.user_agent.string app.run(host="127.0.0.1", port=8000) # 출처: https://dreamhack.io/learn/190#7
image_downloader 페이지는 이용자가 입력한 image_url을 requests.get() 함수로 사용해 GET 메소드로 HTTP 요청을 보내고 응답을 반환한다.
request_info 페이지는 웹 페이지에 접속한 User-Agent를 반환한다.
image_downloader 페이지에서 image_url에 request_info 페이지 경로를 입력하면
이용자가 웹 서비스에서 사용한 마이크로서비스의 API 주소를 반환한다.
즉, image_url에 주소를 전달함으로서 외부에서 직접 접근할 수 없는 마이크로서비스의 기능을 임의로 사용할 수 있다.
■URL에 공격자의 입력값이 포함되는 경우
INTERNAL_API = "http://api.internal/" @app.route("v1/api/user/information") def user_info(): user_idx = request.args.get("user_idx", "") response = requests.get(f"{INTERNAL_API}/user/{user_idx}") @app.route("/v1/api/user/search") def user_search(): user_name = reqeust.args.get("user_name", "") user_type = "public" response = requests.get(f"{INTERNAL_API}/user/search?username={user_name}&user_type={user_type}") # 출처: https://dreamhack.io/learn/190#9
user_info() 함수는 user_idx 값을 내부 API의 URL 경로로 사용한다.
user_search() 함수는 이용자가 입력한 user_name 값을 내부 API의 쿼리로 사용한다.
만약, 이용자의 입력값에 URL의 구성 요소 문자를 사용하면 API 경로를 조작할 수 있다.
ex) user_info()에서 user_idx의 값으로 "../search"를 입력하면, 웹 서비스는 "http://api.internal/user/search"에 요청을 보낸다.
ex) user_search()에서 user_name의 값으로 "woong&user_type=private#"를 입력하면, 웹 서비스는 "http://api.internal/search?user_name=woong&user_type=private#&user_type=public"을 요청한다.
■공격자가 입력한 값이 HTTP Body에 포함되는 경우
from flask import Flask, request, session import requests from os import urandom app = Flask(__name__) app.secret_key = urandom(32) INTERNAL_API = "http://127.0.0.1:8000/" header = {"Content_Type":"application/x-www-form-urlencoded"} @app.route("v1/api/board/write", methods=["POST"]) def board_write(): session["idx"] = "guest" title = request.form.get("title", "") body = request.form.get("body", "") data = f"title={title}&body={body}&user={session['idx']}" response = requests.post(f"{INTERNAL_API}/board/write", headers=header, data=data) retrun response.content @app.route("/board/write", methods=["POST"]) def internal_board_write(): title = request.form.get("title", "") body = request.form.get("body", "") user = request.form.get("user", "") info = {"title" : title, "body" : body, "user" : user } return info @app.route("/") def index(): return """ <form action="/v1/api/board/write" method="POST"> <input type="text" placeholder="title" name="title"/><br/> <input type="text" placeholder="body" name="body"/><br/> <input type="submit"/> </form> """ app.run(host="127.0.0.1", port=8000, debug=True) # 출처: https://dreamhack.io/learn/190#12
board_write() 함수는 이용자의 입력값을 HTTP Body에 포함하고 내부 API로 요청을 보낸다. 전송할 데이터를 구성할 때 세션 정보를 "guest" 계정으로 설정한다.
internal_board_write() 함수는 board_write() 함수에서 요청하는 내부 API를 구현한 기능으로, 전달된 title, body, user를 JSON 형식으로 변환하고 반환한다.
index() 함수는 board_write 기능을 호출하기 위한 인덱스 페이지다.
"http://127.0.0.1:8000"에 접속하면 title과 body를 입력하는 페이지가 표시된다.
입력창에 값을 입력하고 제출하면 다음과 같은 응답을 반환한다.
{"body": "body", "title": "title", "user": "guest"}
요청을 전송할 때 세션 정보를 "guest"로 설정했기 때문에 "user"가 "guest"인 것을 확인할 수 있다.
내부 API로 요청을 보내기 전에 다음과 같이 데이터를 구성하는 것을 확인할 수 있다.
data = f"title={title}&body={body}&user={session['idx']}
데이터를 구성할 때 이용자의 입력값인 title, body, user의 값을 파라미터 형식으로 설정한다.
이로 인해 이용자가 URL 파라미터를 구분하는 '&'를 사용하면 data의 값을 변조할 수 있다.
title에 title&user=admin을 입력하면 data는 다음과 같이 구성된다.
title=title&user=admin&body=body&user=guest
따라서, 실행 결과를 확인하면
{"body": "body", "title": "title", "user": "admin"}
'WEB Hacking' 카테고리의 다른 글
<WEB Hacking> LFI (0) 2022.01.12 <WEB Hacking> File Vulnerability (0) 2021.12.23 <WEB Hacking> Command Injection (0) 2021.12.22 <WEB Hacking> NoSQL Injection (0) 2021.12.20 <WEB Hacking> SQL Injection (0) 2021.12.20