EC2StepShell: A Tool for Getting Reverse Shells on Instances with Network Restrictions

A tool for getting reverse shells in EC2 instances where network communication to your host is restricted.

In my last article, AWS ssm:SendCommand or network agnostic built-in RCE as root, I talked about how ssm:SendCommand can be used to execute code under high privileges on EC2 instances along with the prerequisites needed to complete the attack chain.

One topic of interest was getting reverse shells, which was easy for public EC2 instances, but didn’t work with private and network restricted instances.

Well, I made a tool that fixes this and in this article I’ll show you how to use it.

About the tool

EC2StepShell is an open-source AWS post-exploitation tool for getting high privileges reverse shells in public or private EC2 instances. It works by sending commands to EC2 instances with ssm:SendCommand and then retrieves the output using ssm:ListCommandInvocations.

The tools shines for EC2 instances that can’t establish network communication with your host (private EC2 instances or with restrictive security groups).

You can find the tool on:


Let’s go straight into it so that you can get hacking. We’ll see in the next chapters more technical details.

# Installation
python3 -m pip install EC2StepShell

# Help menu
python3 -m ec2stepshell -h

# Most basic usage
python3 -m ec2stepshell $instance_id --region $region

Now, if you run the tool like this, the default profile configured in AWS CLI will be used to start the reverse shell. The tool also supports custom profiles, temporary access credentials and persistent access credentials.

# running using the default profile configured in AWS CLI
python -m ec2stepshell $instance_id --region $region

# running using a specific profile configured in AWS CLI
python -m ec2stepshell $instance_id --region $region --profile $profile

# running using persistent access credentials
python -m ec2stepshell $instance_id --region $region --access-key $access_key --secret-key $secret_key

# running using temporary access credentials
python -m ec2stepshell $instance_id --region $region --access-key $access_key --secret-key $secret_key --session-token $session_token

How it works

The tool doesn’t directly interact with the EC2 instance. Instead, it communicates with the service AWS Systems Manager (SSM).

To act like reverse shell, the tools does two things:

  1. Sends the user’s command by calling ssm:SendCommand
  2. Retrieves the command’s output

The 2 steps are inside a “while True” loop.

Before starting the shell, there is an initialization process. In this phase, the running OS on the instance, the host’s name and the current working directory are determined. With this information the command input line is constructed so that it would like like a SSH connection.

Even if the OS is mentioned using input arguments, the tool still verifies if the mentioned OS is right and tries to self determine it otherwise.

This is how the process looks like for both UNIX and Windows. In the first image we manually specified that the OS is Windows, but the instance is running on Linux.

In the next image we didn’t specify the OS, but the tool automatically determined that it is Windows.


The tool is meant to be used in a post-exploitation scenario, so you need to have access within the AWS environment. The type of access required by the tool is programmatic access, meaning access credentials. Both temporary and persistent access credentials are fine.

Only two permissions are needed for the tool to work:

  • ssm:SendCommand
  • ssm:ListCommandInvocations

The action ssm:SendCommand must be granted over the target EC2 instance and the documents:

  • AWS-RunShellScript
  • AWS-RunPowerShellScript

I tested the tool with the next policy and it worked just fine:

Usually organizations are not restricting the access to only certain documents, but this is what permissions you need in a more granular approach:

What if you don’t know if you meet the requirements? Well, if you have the instance’s ID and you know the region, just run the tool and see if you get a shell. Worst case scenario, you’ll get an Access Denied error, but best case scenario you get a high privileges reverse shell.

Advanced usage

When sending a command, there is an initial wait time of 0.7 seconds before attempting to retrieve the output. If the output is not retrieved, then a retry process starts.

The tool will retry 3 times to get the output and between each retry there is a wait time of 0.3 seconds.

This works fine for Linux instances, even if they are t2.micro. I wanted to make the shell as responsive as possible by default, without affecting the availability. However, this will not work as well for Windows instances. This is the reason why you can and should modify the initial delay, the number of retries and the retry delay.

For a t2.micro Windows Server instance I had to adjust the initial delay to 2.5 seconds and the retry delay to 0.7 in order to make it work alright. Depending on the instance’s performance, these values can be smaller.

What happens if the command’s output is not retrieved after all the retries? The command ID returned after sending the command is put in a local queue. You can see the queue by typing the command !showqueue. This is a custom command that will be processed by the tool. To view all the custom commands run !help.

Once you have the command ID, you can manually retry to get the command’s output by running !retry command_id.

The !showqueue command will also display the command that was sent. You can use !clearqueue to empty the queue. This will not cancel the commands sent, so if you still have the command IDs you will be able to retrieve the output manually later.


Before we end, let’s quickly talk about how stealthy the tool is. If you are in a red team engagement, you will not want to be detected. This depends on the security measures the AWS environments has, but let’s look into something typical.

I ran the tool on a public EC2 instance and a private one. The AWS account had GuardDuty enabled. Inside the shell I performed some manual enumeration to see what alerts I will trigger.

In turns out that GuardDuty doesn’t flag the activity, which is a good thing for us. I find this weird, especially for the private EC2 instance.

However, all commands sent are logged along with the output. This is why, if you want stealth, you should use EC2StepShell only for private EC2 instances since you can’t get a reverse shell otherwise from the internet.

Future work and conclusions

I plan to extend the tool by adding two new custom commands for downloading and uploading files. I would also like integrate the tool as module in AWS post-exploitation frameworks like pacu.

If you encounter bugs or you have development suggestions, please open an issue on GitHub.

Don’t hesitate to use EC2StepShell for upgrading that ssm:SendCommand to a reverse shell. It’s always easier and faster to use an interactive shell in the post-exploitation phase than to manually send and retrieve commands.

As a last point, imagine how great it will be to show a client that you got a reverse shell from the internet into one of their private EC2 instances

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s