AWS S3 Static Site JavaScript File Uploaded
This rule detects when a JavaScript file is uploaded in an S3 static site directory (static/js/) by an IAM
user or assumed role. This can indicate suspicious modification of web content hosted on S3, such as injecting malicious
scripts into a static website frontend.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2025/04/15"
3integration = ["aws"]
4maturity = "production"
5updated_date = "2026/09/18"
6
7[rule]
8author = ["Elastic"]
9description = """
10This rule detects when a JavaScript file is uploaded in an S3 static site directory (`static/js/`) by an IAM
11user or assumed role. This can indicate suspicious modification of web content hosted on S3, such as injecting malicious
12scripts into a static website frontend.
13"""
14false_positives = [
15 """
16 Development or deployment pipelines that update static frontends frequently (e.g., React/Vue apps) may trigger this.
17 Verify the user agent, source IP, and whether the modification was expected.
18 """,
19]
20from = "now-6m"
21language = "esql"
22license = "Elastic License v2"
23name = "AWS S3 Static Site JavaScript File Uploaded"
24note = """## Triage and Analysis
25
26### Investigating AWS S3 Static Site JavaScript File Uploaded
27
28An S3 `PutObject` action that targets a path like `static/js/` and uploads a `.js` file is a potential signal for web content modification. If done by an unexpected IAM user or outside of CI/CD workflows, it may indicate a compromise.
29
30#### Possible Investigation Steps
31
32- **Identify the Source User**: Check `aws.cloudtrail.user_identity.arn`, access key ID, and session type (`IAMUser`, `AssumedRole`, etc).
33- **Review File Content**: Use the S3 `GetObject` or CloudTrail `requestParameters` to inspect the uploaded file for signs of obfuscation or injection.
34- **Correlate to Other Events**: Review events from the same IAM user before and after the upload (e.g., `ListBuckets`, `GetCallerIdentity`, IAM activity).
35- **Look for Multiple Uploads**: Attackers may attempt to upload several files or modify multiple directories.
36
37### False Positive Analysis
38
39- This behavior may be expected during app deployments. Look at:
40 - The `user_agent.original` to detect legitimate CI tools (like Terraform or GitHub Actions).
41 - Timing patterns—does this match a regular release window?
42 - The origin IP and device identity.
43
44### Response and Remediation
45
46- **Revert Malicious Code**: Replace the uploaded JS file with a clean version and invalidate CloudFront cache if applicable.
47- **Revoke Access**: If compromise is confirmed, revoke the IAM credentials and disable the user.
48- **Audit IAM Policies**: Ensure that only deployment users can modify static site buckets.
49- **Enable Bucket Versioning**: This can allow for quick rollback and historical review.
50"""
51references = [
52 "https://www.sygnia.co/blog/sygnia-investigation-bybit-hack/",
53 "https://docs.aws.amazon.com/AmazonS3/latest/userguide/WebsiteHosting.html",
54 "https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html",
55]
56risk_score = 73
57rule_id = "16acac42-b2f9-4802-9290-d6c30914db6e"
58severity = "high"
59tags = [
60 "Domain: Cloud",
61 "Data Source: AWS",
62 "Data Source: Amazon Web Services",
63 "Data Source: AWS S3",
64 "Tactic: Impact",
65 "Use Case: Web Application Compromise",
66 "Use Case: Cloud Threat Detection",
67 "Resources: Investigation Guide",
68 "Noise: Low",
69 "Performance: Normal",
70 "Rule Type: ES|QL",
71 "Platform: AWS",
72 "Service: AWS S3",
73]
74timestamp_override = "event.ingested"
75type = "esql"
76
77query = '''
78from logs-aws.cloudtrail* metadata _id, _version, _index
79
80| where
81 // S3 object write activity
82 data_stream.dataset == "aws.cloudtrail"
83 and event.provider == "s3.amazonaws.com"
84 and event.action == "PutObject"
85 and event.outcome == "success"
86
87 // IAM users or assumed roles only
88 and aws.cloudtrail.user_identity.type in ("IAMUser", "AssumedRole")
89
90 // Requests for static site bundles
91 and aws.cloudtrail.request_parameters like "*static/js/*.js*"
92
93 // Exclude IaC and automation tools
94 and not (
95 user_agent.original like "*Terraform*"
96 or user_agent.original like "*Ansible*"
97 or user_agent.original like "*Pulumi*"
98 )
99
100// Extract fields from request parameters
101| dissect aws.cloudtrail.request_parameters
102 "%{{?bucket.name.key}=%{Esql.aws_cloudtrail_request_parameters_bucket_name}, %{?host.key}=%{Esql_priv.aws_cloudtrail_request_parameters_host}, %{?bucket.object.location.key}=%{Esql.aws_cloudtrail_request_parameters_bucket_object_location}}"
103
104// Extract file name portion from full object path
105| dissect Esql.aws_cloudtrail_request_parameters_bucket_object_location "%{}static/js/%{Esql.aws_cloudtrail_request_parameters_object_key}"
106
107// Match on JavaScript files
108| where ends_with(Esql.aws_cloudtrail_request_parameters_object_key, ".js")
109
110// Retain relevant ECS and dissected fields
111| keep
112 aws.cloudtrail.user_identity.arn,
113 aws.cloudtrail.user_identity.access_key_id,
114 aws.cloudtrail.user_identity.type,
115 aws.cloudtrail.request_parameters,
116 Esql.aws_cloudtrail_request_parameters_bucket_name,
117 Esql.aws_cloudtrail_request_parameters_object_key,
118 user_agent.original,
119 source.ip,
120 event.action,
121 @timestamp,
122 _id,
123 _version,
124 _index
125'''
126
127
128[[rule.threat]]
129framework = "MITRE ATT&CK"
130
131[[rule.threat.technique]]
132id = "T1491"
133name = "Defacement"
134reference = "https://attack.mitre.org/techniques/T1491/"
135
136[[rule.threat.technique.subtechnique]]
137id = "T1491.002"
138name = "External Defacement"
139reference = "https://attack.mitre.org/techniques/T1491/002/"
140
141[[rule.threat.technique]]
142id = "T1565"
143name = "Data Manipulation"
144reference = "https://attack.mitre.org/techniques/T1565/"
145
146[[rule.threat.technique.subtechnique]]
147id = "T1565.001"
148name = "Stored Data Manipulation"
149reference = "https://attack.mitre.org/techniques/T1565/001/"
150
151[rule.threat.tactic]
152id = "TA0040"
153name = "Impact"
154reference = "https://attack.mitre.org/tactics/TA0040/"
155[rule.investigation_fields]
156field_names = [
157 "@timestamp",
158 "user.name",
159 "user_agent.original",
160 "source.ip",
161 "aws.cloudtrail.user_identity.arn",
162 "aws.cloudtrail.user_identity.type",
163 "aws.cloudtrail.user_identity.access_key_id",
164 "aws.cloudtrail.resources.arn",
165 "aws.cloudtrail.resources.type",
166 "event.action",
167 "event.outcome",
168 "cloud.account.id",
169 "cloud.region",
170 "aws.cloudtrail.request_parameters",
171 "aws.cloudtrail.response_elements"
172]
Triage and Analysis
Investigating AWS S3 Static Site JavaScript File Uploaded
An S3 PutObject action that targets a path like static/js/ and uploads a .js file is a potential signal for web content modification. If done by an unexpected IAM user or outside of CI/CD workflows, it may indicate a compromise.
Possible Investigation Steps
- Identify the Source User: Check
aws.cloudtrail.user_identity.arn, access key ID, and session type (IAMUser,AssumedRole, etc). - Review File Content: Use the S3
GetObjector CloudTrailrequestParametersto inspect the uploaded file for signs of obfuscation or injection. - Correlate to Other Events: Review events from the same IAM user before and after the upload (e.g.,
ListBuckets,GetCallerIdentity, IAM activity). - Look for Multiple Uploads: Attackers may attempt to upload several files or modify multiple directories.
False Positive Analysis
- This behavior may be expected during app deployments. Look at:
- The
user_agent.originalto detect legitimate CI tools (like Terraform or GitHub Actions). - Timing patterns—does this match a regular release window?
- The origin IP and device identity.
- The
Response and Remediation
- Revert Malicious Code: Replace the uploaded JS file with a clean version and invalidate CloudFront cache if applicable.
- Revoke Access: If compromise is confirmed, revoke the IAM credentials and disable the user.
- Audit IAM Policies: Ensure that only deployment users can modify static site buckets.
- Enable Bucket Versioning: This can allow for quick rollback and historical review.
References
Related rules
- Excessive AWS S3 Object Encryption with SSE-C
- Unusual AWS S3 Object Encryption with SSE-C
- AWS S3 Bucket Enumeration or Brute Force
- AWS S3 Bucket Policy Added to Allow Public Access
- AWS S3 Rapid Bucket Posture API Calls from a Single Principal