고객사가 웹 취약점 점검을 매년 받아왔고 조치도 잘 하는 사이트라면 취약점 하나 잡는게 여간 쉬운 일이 아니다.
그럴땐 js 나 html 파일에서 화면 상에 노출되지 않는 Endpoint URL 들을 뒤져보면 다른 사람이 아직 발견하지 못한 취약점들이 나오는 경우가 꽤 있기도 하다.
이를 위해 버프스위트 익스텐션으로 JS Link Finder 라는게 존재하지만
특정 URL 을 대상으로만 수동으로 스캔하고 싶어서 Endpoint URL을 파싱해주는 도구를 만들었다.
위 실행 파일을 다운받아 C:\Windows\System32 등 환경변수로 지정된 경로에 저장하면 된다.
명령어의 옵션은 아래와 같다.
기본적인 사용방법은 ep 명령어 뒤에 endpoints 를 수집할 URL 경로 또는 파일(js, html 등) 경로를 입력하면 된다.
필요에 따라 저장 경로(-o) 나 쿠키 헤더 설정(-c) 이 가능하도록 하였으며, 사용해보면 감이 온다.
아래는 python 원본 코드를 첨부한다.
import requests
import re
from urllib.parse import urljoin
import argparse
import warnings
import os
def extract_endpoints_from_html(html_content, base_url=''):
potential_endpoints = re.findall(r'(["\'])(https?://[^\s"\']+?)\1|(["\'])(/[^\s"\']+?)\3', html_content)
endpoints = set()
for match in potential_endpoints:
if match[1]:
endpoints.add(match[1])
if match[3]:
full_url = urljoin(base_url, match[3]) # 절대경로
#full_url = match[3] # 상대경로
endpoints.add(full_url)
return list(endpoints)
def extract_endpoints_from_url(base_url, cookies=None):
try:
warnings.filterwarnings('ignore', category=requests.packages.urllib3.exceptions.InsecureRequestWarning)
response = requests.get(base_url, verify=False, cookies=cookies)
response.raise_for_status()
except requests.RequestException as e:
print(f"Error fetching the URL: {e}")
return []
return extract_endpoints_from_html(response.text, base_url)
def parse_cookies(cookie_string):
if not cookie_string:
return {}
cookies = {}
try:
# 쿠키 문자열을 딕셔너리로 변환
pairs = cookie_string.split(';')
for pair in pairs:
key, value = pair.strip().split('=', 1)
cookies[key.strip()] = value.strip()
except Exception as e:
print(f"Error parsing cookies: {e}")
return {}
return cookies
def extract_endpoints_from_file(file_path, base_url=''):
if not os.path.isfile(file_path):
print("File not found.")
return []
try:
with open(file_path, 'r', encoding='utf-8') as file:
html_content = file.read()
except IOError as e:
print(f"Error reading the file: {e}")
return []
return extract_endpoints_from_html(html_content, base_url)
def save_endpoints_to_file(endpoints, filename='endpoints.txt'):
try:
with open(filename, 'w', encoding='utf-8') as file:
for endpoint in endpoints:
file.write(endpoint + '\n')
except IOError as e:
print(f"Error writing to file: {e}")
def main():
parser = argparse.ArgumentParser(description="Extract endpoints from a URL or a file.")
parser.add_argument('source', help="URL or file path")
parser.add_argument('-o', '--output', default='endpoints.txt', help="Output file name (default: 'endpoints.txt')")
parser.add_argument('-b', '--base-url', default='', help="Base URL for resolving relative paths")
parser.add_argument('-c', '--cookies', help="Cookies in format 'name1=value1; name2=value2'")
args = parser.parse_args()
source = args.source
output_file = args.output
base_url = args.base_url
cookies = parse_cookies(args.cookies)
if source.startswith('http://') or source.startswith('https://'):
endpoints = extract_endpoints_from_url(source, cookies)
else:
endpoints = extract_endpoints_from_file(source, base_url)
if endpoints:
save_endpoints_to_file(endpoints, output_file)
print(f"Extracted {len(endpoints)} endpoints and saved to '{output_file}'.")
print(f"Made by SECUWORM")
else:
print("No endpoints were found.")
if __name__ == "__main__":
main()