From 0814da8576826e1acfead66253aabf445e75053d Mon Sep 17 00:00:00 2001 From: Translator Date: Fri, 9 May 2025 11:19:10 +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 | 326 ++++---- .../az-services/az-app-services.md | 65 +- .../az-services/az-function-apps.md | 72 +- theme/elasticlunr.min.js | 10 + theme/ht_searcher.js | 720 ++++-------------- 7 files changed, 414 insertions(+), 780 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**: - +- **Criar túnel e depois conectar ao 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 ``` +- **Depurar a aplicação**: +1. Instale a extensão Azure no VScode. +2. Faça login na extensão com a conta Azure. +3. Liste todos os serviços de App dentro da assinatura. +4. Selecione o serviço de App que você deseja depurar, clique com o botão direito e selecione "Iniciar Depuração". +5. Se o aplicativo não tiver a depuração habilitada, a extensão tentará habilitá-la, mas sua conta precisa da permissão `Microsoft.Web/sites/config/write` para isso. -- **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. +### Obtendo Credenciais SCM e Habilitando Autenticação Básica -### 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: +Para obter as credenciais SCM, você pode usar os seguintes **comandos e permissões**: +- A permissão **`Microsoft.Web/sites/publishxml/action`** permite chamar: ```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" +} ] ``` +Observe como o **nome de usuário é sempre o mesmo** (exceto no FTP, que adiciona o nome do aplicativo no início), mas a **senha é a mesma** para todos eles. -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: +Além disso, a **URL do SCM é `.scm.azurewebsites.net`**. +- A permissão **`Microsoft.Web/sites/config/list/action`** permite chamar: ```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" } ``` +Observe como as **credenciais são as mesmas** que no comando anterior. -Note how the **credentials are the same** as in the previous command. - -- Another option would be to **set you own creds** and use them: - +- Outra opção seria **definir suas próprias credenciais** e usá-las: ```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 ``` +Então, você pode usar essas credenciais para **acessar as plataformas SCM e FTP**. Esta também é uma ótima maneira de manter a persistência. -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`**. +Lembre-se de que para acessar a plataforma SCM pela **web, você precisa acessar `/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`): +> Note que todo usuário pode configurar suas próprias credenciais chamando o comando anterior, mas se o usuário não tiver permissões suficientes para acessar o SCM ou FTP, as credenciais não funcionarão. +- Se você ver que essas credenciais estão **REDACTED**, é porque você **precisa habilitar a opção de autenticação básica do SCM** e para isso você precisa da segunda permissão (`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 +} +}' ``` +### Publicar código usando credenciais SCM -### Publish code using SCM credentials +Apenas ter credenciais SCM válidas já é possível **publicar código** no serviço de aplicativo. Isso pode ser feito usando o seguinte comando. -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: +Para este exemplo em python, você pode baixar o repositório de https://github.com/Azure-Samples/msdocs-python-flask-webapp-quickstart, fazer quaisquer **alterações** que desejar e então **compactá-lo executando: `zip -r app.zip .`**. +Em seguida, você pode **publicar o código** em um aplicativo da web com o seguinte comando: ```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 | Credenciais do SCM -### 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: +A permissão do Azure mencionada permite realizar várias ações interessantes que também podem ser realizadas com as credenciais do SCM: +- Ler logs de **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: - +- Leia o código-fonte do **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**: - +- Criar **Webjob contínuo**: ```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. - +Essas permissões permitem **atribuir uma identidade gerenciada** ao serviço de aplicativo, então, se um serviço de aplicativo foi comprometido anteriormente, isso permitirá que o atacante atribua novas identidades gerenciadas ao serviço de aplicativo e **escalone privilégios** para elas. ```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. - +Esta permissão permite listar as **connection strings** e os **appsettings** do serviço de aplicativo, que podem conter informações sensíveis, como credenciais de banco de dados. ```bash az webapp config connection-string list --name --resource-group az webapp config appsettings list --name --resource-group ``` +### Ler Credenciais de Terceiros Configuradas -### 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. - +Executando o seguinte comando, é possível **ler as credenciais de terceiros** configuradas na conta atual. Note que, se por exemplo, algumas credenciais do Github estiverem configuradas em um usuário diferente, você não poderá acessar o token de um diferente. ```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" ``` +Este comando retorna tokens para Github, Bitbucket, Dropbox e OneDrive. -This command returns tokens for Github, Bitbucket, Dropbox and OneDrive. - -Here you have some command examples to check the tokens: - +Aqui estão alguns exemplos de comandos para verificar os tokens: ```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 ``` +### Atualizar o Código do App a partir da fonte -### 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 ` +- Se a fonte configurada for um provedor de terceiros como Github, BitBucket ou um Repositório Azure, você pode **atualizar o código** do serviço App comprometendo o código fonte no repositório. +- Se o app estiver configurado usando um **repositório git remoto** (com nome de usuário e senha), é possível obter a **URL e as credenciais de autenticação básica** para clonar e enviar alterações com: +- Usando a permissão **`Microsoft.Web/sites/sourcecontrols/read`**: `az webapp deployment source show --name --resource-group ` +- Usando a permissão **`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"` +- Se o app estiver configurado para usar um **repositório git local**, é possível **clonar o repositório** e **enviar alterações** para ele: +- Usando a permissão **`Microsoft.Web/sites/sourcecontrols/read`**: Você pode obter a URL do repositório git com `az webapp deployment source show --name --resource-group `, mas será a mesma que a URL SCM do app com o caminho `/.git` (por exemplo, `https://pythonwebapp-audeh9f5fzeyhhed.scm.canadacentral-01.azurewebsites.net:443/pythonwebapp.git`). +- Para obter as credenciais SCM, você precisa da permissão: +- **`Microsoft.Web/sites/publishxml/action`**: Então execute `az webapp deployment list-publishing-profiles --resource-group -n `. +- **`Microsoft.Web/sites/config/list/action`**: Então execute `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. +> Observe que ter a permissão `Microsoft.Web/sites/config/list/action` e as credenciais SCM torna sempre possível implantar em um webapp (mesmo que tenha sido configurado para usar um provedor de terceiros), como mencionado em uma seção anterior. > [!WARNING] -> Note that having the permissions below it's also **possible to execute an arbitrary container** even if the webapp was configured differently. +> Observe que ter as permissões abaixo também torna **possível executar um contêiner arbitrário** mesmo que o webapp tenha sido configurado de forma diferente. ### `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. - +Este é o conjunto de permissões que permite **modificar o contêiner usado** por um webapp. Um atacante poderia abusar disso para fazer um webapp executar um contêiner malicioso. ```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 de8ec174e..dda1d0bcf 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 @@ -4,27 +4,28 @@ ## Informações Básicas do App Service -O Azure App Services permite que os desenvolvedores **construam, implantem e escalem aplicativos web, backends de aplicativos móveis e APIs de forma contínua**. Ele suporta várias linguagens de programação e se integra a várias ferramentas e serviços do Azure para funcionalidade e gerenciamento aprimorados. +Azure App Services permite que os desenvolvedores **construam, implantem e escalem aplicações web, backends de aplicativos móveis e APIs de forma contínua**. Suporta várias linguagens de programação e se integra a diversas ferramentas e serviços do Azure para funcionalidade e gerenciamento aprimorados. -Cada aplicativo é executado dentro de um sandbox, mas a isolação depende dos planos do App Service: +Cada aplicativo roda dentro de um sandbox, mas a isolação depende dos planos do App Service: -- Aplicativos nos níveis Free e Shared são executados em **VMs compartilhadas** -- Aplicativos nos níveis Standard e Premium são executados em **VMs dedicadas compartilhadas apenas por aplicativos** no mesmo plano do App Service. -- Os níveis Isolated são executados em **VMs dedicadas em redes virtuais dedicadas**, melhorando a isolação dos aplicativos. +- Aplicativos nos níveis Free e Shared rodam em **VMs compartilhadas** +- Aplicativos nos níveis Standard e Premium rodam em **VMs dedicadas compartilhadas apenas por aplicativos** no mesmo plano do App Service. +- Os níveis Isolated rodam em **VMs dedicadas em redes virtuais dedicadas**, melhorando a isolação dos aplicativos. > [!WARNING] -> Observe que **nenhuma** dessas isolamentos **previne** outras **vulnerabilidades web** comuns (como upload de arquivos ou injeções). E se uma **identidade de gerenciamento** for usada, ela pode ser capaz de **escalar privilégios para elas**. +> Note que **nenhuma** dessas isolamentos **previne** outras **vulnerabilidades web** comuns (como upload de arquivos ou injeções). E se uma **identidade de gerenciamento** for usada, ela pode ser capaz de **escalar privilégios para elas**. Os aplicativos têm algumas configurações interessantes: -- **Always On**: Garante que o aplicativo esteja sempre em execução. Se não estiver habilitado, o aplicativo parará de funcionar após 20 minutos de inatividade e será reiniciado quando uma solicitação for recebida. -- Isso é essencial se você tiver um webjob que precisa ser executado continuamente, pois o webjob parará se o aplicativo parar. +- **Always On**: Garante que o aplicativo esteja sempre em execução. Se não estiver habilitado, o aplicativo parará de rodar após 20 minutos de inatividade e reiniciará quando uma solicitação for recebida. +- Isso é essencial se você tiver um webjob que precisa rodar continuamente, pois o webjob parará se o aplicativo parar. - **SSH**: Se habilitado, um usuário com permissões suficientes pode se conectar ao aplicativo usando SSH. -- **Depuração**: Se habilitado, um usuário com permissões suficientes pode depurar o aplicativo. No entanto, isso é desativado automaticamente a cada 48h. -- **Web App + Banco de Dados**: O console web permite criar um aplicativo com um banco de dados. Nesse caso, é possível selecionar o banco de dados a ser usado (SQLAzure, PostgreSQL, MySQL, MongoDB) e também permite criar um Azure Cache para Redis. +- **Debugging**: Se habilitado, um usuário com permissões suficientes pode depurar o aplicativo. No entanto, isso é desabilitado automaticamente a cada 48h. +- **Web App + Database**: O console web permite criar um App com um banco de dados. Nesse caso, é possível selecionar o banco de dados a ser usado (SQLAzure, PostgreSQL, MySQL, MongoDB) e também permite criar um Azure Cache para Redis. - A URL contendo as credenciais para o banco de dados e Redis será armazenada nas **appsettings**. - **Container**: É possível implantar um container no App Service indicando a URL do container e as credenciais para acessá-lo. - **Mounts**: É possível criar 5 mounts a partir de contas de armazenamento, sendo estas Azure Blob (Somente Leitura) ou Azure Files. A configuração armazenará a chave de acesso sobre a Conta de Armazenamento. +- **Networking**: Pode ser publicamente disponível ou acessível apenas por endpoints privados de uma VNet. ## Autenticação Básica @@ -32,31 +33,31 @@ Ao criar um aplicativo web (e uma função do Azure geralmente), é possível in Para acessar os servidores SCM e FTP, é necessário um **nome de usuário e senha**. Portanto, o Azure fornece algumas **APIs para obter as URLs** para essas plataformas e as credenciais. -O **servidor FTP não tem nenhuma mágica especial**, apenas com a URL válida, nome de usuário e senha é possível conectar e obter permissões de leitura e gravação sobre o ambiente do App. +O **servidor FTP não tem nenhuma mágica especial**, apenas com a URL válida, nome de usuário e senha é possível conectar e obter permissões de leitura e escrita sobre o ambiente do App. O SCM -É possível conectar ao SCM usando um navegador da web em `https:///BasicAuth` e verificar todos os arquivos e implantações lá. +É possível conectar ao SCM usando um navegador web em `https:///BasicAuth` e verificar todos os arquivos e implantações lá. ### Kudu Kudu é a plataforma que **gerencia tanto o SCM quanto uma interface web e API** para gerenciar um App Service, e fornece implantações baseadas em Git, depuração remota e capacidades de gerenciamento de arquivos. É acessível através da URL do SCM definida no aplicativo web. -Observe que as versões do Kudu usadas pelos App Services e pelos Function Apps são diferentes, sendo a versão dos Function Apps muito mais limitada. +Note que as versões do Kudu usadas pelos App Services e pelos Function Apps são diferentes, sendo a versão dos Function Apps muito mais limitada. Alguns endpoints interessantes que você pode encontrar no Kudu são: - `/BasicAuth`: Você precisa acessar este caminho para **fazer login no Kudu**. -- `/DebugConsole`: Um console que permite executar comandos no ambiente onde o Kudu está sendo executado. -- Observe que este ambiente **não tem acesso** ao serviço de metadados para obter tokens. -- `/webssh/host`: Um cliente SSH baseado na web que permite conectar-se dentro do container onde o aplicativo está sendo executado. +- `/DebugConsole`: Um console que permite executar comandos no ambiente onde o Kudu está rodando. +- Note que este ambiente **não tem acesso** ao serviço de metadados para obter tokens. +- `/webssh/host`: Um cliente SSH baseado na web que permite conectar dentro do container onde o aplicativo está rodando. - Este ambiente **tem acesso ao serviço de metadados** para obter tokens das identidades gerenciadas atribuídas. -- `/Env`: Obtenha informações sobre o sistema, configurações do aplicativo, variáveis de ambiente, strings de conexão e cabeçalhos HTTP. +- `/Env`: Obter informações sobre o sistema, configurações do aplicativo, variáveis de ambiente, strings de conexão e cabeçalhos HTTP. - `/wwwroot/`: O diretório raiz do aplicativo web. Você pode baixar todos os arquivos daqui. -Além disso, o Kudu costumava ser open source em [https://github.com/projectkudu/kudu](https://github.com/projectkudu/kudu), mas o projeto foi descontinuado e, comparando o comportamento do Kudu atual no Azure com o antigo, é possível ver que **várias coisas já mudaram**. +Além disso, o Kudu costumava ser open source em [https://github.com/projectkudu/kudu](https://github.com/projectkudu/kudu), mas o projeto foi descontinuado e comparando o comportamento do Kudu atual no Azure com o antigo, é possível ver que **várias coisas já mudaram**. ## Fontes -Os App Services permitem fazer upload do código como um arquivo zip por padrão, mas também permitem conectar-se a um serviço de terceiros e obter o código de lá. +Os App Services permitem fazer upload do código como um arquivo zip por padrão, mas também permitem conectar a um serviço de terceiros e obter o código de lá. - As fontes de terceiros atualmente suportadas são **Github** e **Bitbucket**. - Você pode obter os tokens de autenticação executando `az rest --url "https://management.azure.com/providers/Microsoft.Web/sourcecontrols?api-version=2024-04-01"` @@ -70,26 +71,26 @@ Os App Services permitem fazer upload do código como um arquivo zip por padrão ## Webjobs -Os Azure WebJobs são **tarefas em segundo plano que são executadas no ambiente do Azure App Service**. Eles permitem que os desenvolvedores executem scripts ou programas ao lado de seus aplicativos web, facilitando o manuseio de operações assíncronas ou intensivas em tempo, como processamento de arquivos, manipulação de dados ou tarefas agendadas. +Azure WebJobs são **tarefas em segundo plano que rodam no ambiente do Azure App Service**. Eles permitem que os desenvolvedores executem scripts ou programas ao lado de suas aplicações web, facilitando o manuseio de operações assíncronas ou intensivas em tempo, como processamento de arquivos, manipulação de dados ou tarefas agendadas. Existem 2 tipos de web jobs: -- **Contínuo**: Executa indefinidamente em um loop e é acionado assim que é criado. É ideal para tarefas que requerem processamento constante. No entanto, se o aplicativo parar de funcionar porque o Always On está desabilitado e não recebeu uma solicitação nos últimos 20 minutos, o web job também parará. -- **Acionado**: Executa sob demanda ou com base em um cronograma. É mais adequado para tarefas periódicas, como atualizações de dados em lote ou rotinas de manutenção. +- **Contínuo**: Roda indefinidamente em um loop e é acionado assim que é criado. É ideal para tarefas que requerem processamento constante. No entanto, se o aplicativo parar de rodar porque o Always On está desabilitado e não recebeu uma solicitação nos últimos 20 minutos, o web job também parará. +- **Acionado**: Roda sob demanda ou com base em um cronograma. É mais adequado para tarefas periódicas, como atualizações de dados em lote ou rotinas de manutenção. -Os webjobs são muito interessantes do ponto de vista de um atacante, pois podem ser usados para **executar código** no ambiente e **escalar privilégios** para as identidades gerenciadas anexadas. +Webjobs são muito interessantes do ponto de vista de um atacante, pois podem ser usados para **executar código** no ambiente e **escalar privilégios** para as identidades gerenciadas anexadas. Além disso, é sempre interessante verificar os **logs** gerados pelos Webjobs, pois podem conter **informações sensíveis**. ## Slots -Os Slots do Azure App Service são usados para **implantar diferentes versões do aplicativo** no mesmo App Service. Isso permite que os desenvolvedores testem novos recursos ou alterações em um ambiente separado antes de implantá-los no ambiente de produção. +Os Slots do Azure App Service são usados para **implantar diferentes versões da aplicação** no mesmo App Service. Isso permite que os desenvolvedores testem novos recursos ou alterações em um ambiente separado antes de implantá-los no ambiente de produção. Além disso, é possível direcionar uma **porcentagem do tráfego** para um slot específico, o que é útil para testes A/B e para **fins de backdoor**. ## Azure Function Apps -Basicamente, **os Azure Function apps são um subconjunto do Azure App Service** no console web e, se você acessar o console web e listar todos os serviços de aplicativo ou executar `az webapp list` no az cli, você poderá **ver os Function apps também listados lá**. +Basicamente, **Azure Function apps são um subconjunto do Azure App Service** no console web e se você acessar o console web e listar todos os serviços de aplicativo ou executar `az webapp list` no az cli, você poderá **ver os Function apps também listados lá**. -Portanto, ambos os serviços têm, na verdade, principalmente as **mesmas configurações, recursos e opções no az cli**, embora possam configurá-los de maneira um pouco diferente (como valores padrão de appsettings ou o uso de uma Conta de Armazenamento nos Function apps). +Portanto, ambos os serviços têm na verdade principalmente as **mesmas configurações, recursos e opções no az cli**, embora possam configurá-los de maneira um pouco diferente (como valores padrão de appsettings ou o uso de uma Conta de Armazenamento nos Function apps). ## Enumeração @@ -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 }} @@ -272,9 +277,9 @@ done ## Exemplos para gerar Aplicativos Web -### Python a partir do local +### Python do local -Este tutorial é baseado no disponível em [https://learn.microsoft.com/en-us/azure/app-service/quickstart-python](https://learn.microsoft.com/en-us/azure/app-service/quickstart-python?tabs=flask%2Cwindows%2Cazure-cli%2Cazure-cli-deploy%2Cdeploy-instructions-azportal%2Cterminal-bash%2Cdeploy-instructions-zip-azcli). +Este tutorial é baseado no de [https://learn.microsoft.com/en-us/azure/app-service/quickstart-python](https://learn.microsoft.com/en-us/azure/app-service/quickstart-python?tabs=flask%2Cwindows%2Cazure-cli%2Cazure-cli-deploy%2Cdeploy-instructions-azportal%2Cterminal-bash%2Cdeploy-instructions-zip-azcli). ```bash # Clone repository git clone https://github.com/Azure-Samples/msdocs-python-flask-webapp-quickstart @@ -295,7 +300,7 @@ Fazendo login no portal SCM ou acessando via FTP, é possível ver em `/wwwroot` Este tutorial é baseado no anterior, mas usando um repositório do Github. 1. Faça um fork do repositório msdocs-python-flask-webapp-quickstart na sua conta do Github. -2. Crie um novo Web App em Python no Azure. +2. Crie uma nova Web App em Python no Azure. 3. No `Deployment Center`, altere a fonte, faça login com o Github, selecione o repositório forkado e clique em `Save`. Como no caso anterior, fazendo login no portal SCM ou acessando via FTP, é possível ver em `/wwwroot` o arquivo compactado `output.tar.gz` que contém o código da webapp. @@ -303,7 +308,7 @@ Como no caso anterior, fazendo login no portal SCM ou acessando via FTP, é poss > [!TIP] > Apenas conectar via FTP e modificar o arquivo `output.tar.gz` e reativar uma implantação não é suficiente para alterar o código executado pela webapp. -## Escalação de Privilégios +## Escalada de Privilégios {{#ref}} ../az-privilege-escalation/az-app-services-privesc.md 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 3adec0693..de92223e8 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 @@ -15,11 +15,11 @@ - **Traditional Consumption Plan**: A opção sem servidor padrão, onde você **paga apenas pelos recursos de computação quando as funções são executadas**. Ele escala automaticamente com base nos eventos recebidos e inclui **otimizações de início a frio**, mas não suporta implantações de contêiner. Ideal para **cargas de trabalho intermitentes** que requerem escalonamento automático. - **Premium Plan**: Projetado para **desempenho consistente**, com **trabalhadores pré-aquecidos** para eliminar inícios a frio. Oferece **tempos de execução estendidos, rede virtual** e suporta **imagens personalizadas do Linux**, tornando-o perfeito para **aplicações críticas** que necessitam de alto desempenho e recursos avançados. - **Dedicated Plan**: Executa em máquinas virtuais dedicadas com **faturamento previsível** e suporta escalonamento manual ou automático. Permite executar várias apps no mesmo plano, fornece **isolamento de computação** e garante **acesso seguro à rede** por meio de App Service Environments, tornando-o ideal para **aplicações de longa duração** que necessitam de alocação consistente de recursos. -- **Container Apps**: Permite implantar **function apps containerizadas** em um ambiente gerenciado, juntamente com microsserviços e APIs. Suporta bibliotecas personalizadas, migração de aplicativos legados e **processamento GPU**, eliminando a necessidade de gerenciamento de clusters Kubernetes. Ideal para **aplicações escaláveis e orientadas a eventos**. +- **Container Apps**: Permite implantar **function apps containerizadas** em um ambiente gerenciado, ao lado de microsserviços e APIs. Suporta bibliotecas personalizadas, migração de aplicativos legados e **processamento GPU**, eliminando a necessidade de gerenciamento de clusters Kubernetes. Ideal para **aplicações containerizadas escaláveis e orientadas a eventos**. ### **Buckets de Armazenamento** -Ao criar um novo Function App não containerizado (mas fornecendo o código para execução), os **códigos e outros dados relacionados à função serão armazenados em uma conta de Armazenamento**. Por padrão, o console da web criará um novo por função para armazenar o código. +Ao criar um novo Function App não containerizado (mas fornecendo o código para executar), os **códigos e outros dados relacionados à função serão armazenados em uma conta de Armazenamento**. Por padrão, o console da web criará um novo por função para armazenar o código. Além disso, ao modificar o código dentro do bucket (nos diferentes formatos em que pode ser armazenado), o **código do aplicativo será modificado para o novo e executado** na próxima vez que a Função for chamada. @@ -58,16 +58,18 @@ Em uma função **Windows** usando NodeJS, o código estava localizado em **`C:\ Assim como [**VMs**](vms/index.html), Functions podem ter **Identidades Gerenciadas** de 2 tipos: Atribuídas pelo sistema e Atribuídas pelo usuário. -A **atribuição pelo sistema** será uma identidade gerenciada que **apenas a função** que a possui atribuída poderá usar, enquanto as identidades gerenciadas **atribuidas pelo usuário** são identidades gerenciadas que **qualquer outro serviço Azure poderá usar**. +A **atribuição pelo sistema** será uma identidade gerenciada que **apenas a função** que a tem atribuída poderá usar, enquanto as identidades gerenciadas **atribuidas pelo usuário** são identidades gerenciadas que **qualquer outro serviço Azure poderá usar**. > [!NOTE] -> Assim como em [**VMs**](vms/index.html), Functions podem ter **1 identidade gerenciada atribuída pelo sistema** e **várias atribuídas pelo usuário**, portanto, é sempre importante tentar encontrar todas elas se você comprometer a função, pois você pode ser capaz de elevar privilégios para várias identidades gerenciadas a partir de apenas uma Função. +> Assim como em [**VMs**](vms/index.html), Functions podem ter **1 identidade gerenciada atribuída pelo sistema** e **várias atribuídas pelo usuário**, então é sempre importante tentar encontrar todas elas se você comprometer a função, pois você pode ser capaz de elevar privilégios para várias identidades gerenciadas a partir de apenas uma Função. > > Se uma identidade gerenciada pelo sistema não for usada, mas uma ou mais identidades gerenciadas pelo usuário estiverem anexadas a uma função, por padrão você não poderá obter nenhum token. -É possível usar os [**scripts PEASS**](https://github.com/peass-ng/PEASS-ng) para obter tokens da identidade gerenciada padrão a partir do endpoint de metadados. Ou você pode obtê-los **manualmente** conforme explicado em: +É possível usar os [**scripts PEASS**](https://github.com/peass-ng/PEASS-ng) para obter tokens da identidade gerenciada padrão a partir do endpoint de metadados. Ou você pode obtê-los **manualmente** como explicado em: -{% 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}} Note que você precisa descobrir uma maneira de **verificar todas as Identidades Gerenciadas que uma função tem anexadas**, pois se você não indicar, o endpoint de metadados **usará apenas a padrão** (verifique o link anterior para mais informações). @@ -79,15 +81,15 @@ Note que você precisa descobrir uma maneira de **verificar todas as Identidades Ao criar um endpoint dentro de uma função usando um **gatilho HTTP**, é possível indicar o **nível de autorização da chave de acesso** necessário para acionar a função. Três opções estão disponíveis: - **ANONYMOUS**: **Todos** podem acessar a função pela URL. -- **FUNCTION**: O endpoint é acessível apenas a usuários que usam uma **chave de função, host ou mestre**. -- **ADMIN**: O endpoint é acessível apenas a usuários com uma **chave mestre**. +- **FUNCTION**: O endpoint é acessível apenas para usuários usando uma **chave de função, host ou mestre**. +- **ADMIN**: O endpoint é acessível apenas para usuários com uma **chave mestre**. **Tipo de chaves:** - **Chaves de Função:** As chaves de função podem ser padrão ou definidas pelo usuário e são projetadas para conceder acesso exclusivamente a **endpoints de função específicos** dentro de um Function App, permitindo um acesso mais granular sobre os endpoints. - **Chaves de Host:** As chaves de host, que também podem ser padrão ou definidas pelo usuário, fornecem acesso a **todos os endpoints de função dentro de um Function App com nível de acesso FUNCTION**. - **Chave Mestre:** A chave mestre (`_master`) serve como uma chave administrativa que oferece permissões elevadas, incluindo acesso a todos os endpoints de função (nível de acesso ADMIN incluído). Esta **chave não pode ser revogada.** -- **Chaves do Sistema:** As chaves do sistema são **gerenciadas por extensões específicas** e são necessárias para acessar endpoints de webhook usados por componentes internos. Exemplos incluem o gatilho do Event Grid e Durable Functions, que utilizam chaves do sistema para interagir de forma segura com suas respectivas APIs. +- **Chaves do Sistema:** As chaves do sistema são **gerenciadas por extensões específicas** e são necessárias para acessar endpoints de webhook usados por componentes internos. Exemplos incluem o gatilho do Event Grid e Funções Duráveis, que utilizam chaves do sistema para interagir de forma segura com suas respectivas APIs. > [!TIP] > Exemplo para acessar um endpoint de API de função usando uma chave: @@ -102,9 +104,9 @@ Assim como nos App Services, Functions também suportam autenticação básica p az-app-services.md {{#endref}} -### Implantações Baseadas em Github +### Implantações Baseadas no Github -Quando uma função é gerada a partir de um repositório Github, o console web do Azure permite **criar automaticamente um Workflow do Github em um repositório específico**, de modo que sempre que este repositório for atualizado, o código da função seja atualizado. Na verdade, o yaml da Ação do Github para uma função em python se parece com isso: +Quando uma função é gerada a partir de um repositório do Github, o console web do Azure permite **criar automaticamente um Workflow do Github em um repositório específico**, de modo que sempre que este repositório for atualizado, o código da função seja atualizado. Na verdade, o yaml da Ação do Github para uma função em python se parece com isso:

@@ -195,7 +197,7 @@ package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }} Além disso, uma **Identidade Gerenciada** também é criada para que a Ação do Github do repositório possa fazer login no Azure com ela. Isso é feito gerando uma credencial Federada sobre a **Identidade Gerenciada**, permitindo o **Emissor** `https://token.actions.githubusercontent.com` e o **Identificador do Sujeito** `repo:/:ref:refs/heads/`. > [!CAUTION] -> Portanto, qualquer pessoa que comprometer esse repositório poderá comprometer a função e as Identidades Gerenciadas associadas a ela. +> Portanto, qualquer pessoa que comprometer esse repositório poderá comprometer a função e as Identidades Gerenciadas anexadas a ela. ### Implantações Baseadas em Contêiner @@ -211,10 +213,10 @@ Além disso, **nenhum código-fonte será armazenado na conta de armazenamento** # 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