Skip to content

Instantly share code, notes, and snippets.

@mnixry
Created April 22, 2023 15:02
DASCTF 2023, dump leaked directory
import asyncio
import re
from pathlib import Path
from urllib.parse import urljoin, urlparse
from anyio import open_file
from httpx import AsyncClient
from loguru import logger
MATCH_QUOTES = re.compile(r'["\'](.*?)["\']')
PARALLEL = 4
HOST = "http://114.117.174.147:18886"
ENTRY_FILE = "app.js"
SAVE_DIRECTORY = Path("./website")
touched_paths = set[str]()
client = AsyncClient(base_url=HOST, http2=True)
sem = asyncio.Semaphore(PARALLEL)
def enum_paths(basic_path: str):
return [
basic_path,
f"{basic_path}.js",
f"{basic_path}/index.js",
]
async def get_file_content(path):
logger.debug(f"Getting file: {path!r}")
async with sem:
resp = await client.get(path)
if not resp.is_success:
return
logger.info(f"Found file: {path!r}, size {len(resp.content)}")
await save_file(path, resp.content)
strings = [matched[1] for matched in MATCH_QUOTES.finditer(resp.text)]
possible_paths = []
for string in strings:
parsed_path = urlparse(urljoin(str(resp.url), string))
if parsed_path.hostname != urlparse(HOST).hostname:
continue
basic_path = parsed_path.path
if basic_path in touched_paths:
continue
touched_paths.add(basic_path)
paths = [
*enum_paths(basic_path),
*enum_paths(basic_path.rstrip("/")),
*enum_paths("/api/" + basic_path.lstrip("/")),
]
basic_path = basic_path.lstrip("/")
possible_paths.extend(paths)
await asyncio.gather(
*[get_file_content(path) for path in possible_paths], return_exceptions=True
)
@logger.catch
async def save_file(path: str, content: bytes):
save_path = SAVE_DIRECTORY / path.replace("//", "/").lstrip("/")
save_path.parent.mkdir(parents=True, exist_ok=True)
async with await open_file(save_path, "wb") as file:
await file.write(content)
if __name__ == "__main__":
asyncio.run(get_file_content(ENTRY_FILE))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment