diff --git a/docs/attack-techniques/AWS/aws.credential-access.ec2-steal-instance-credentials.md b/docs/attack-techniques/AWS/aws.credential-access.ec2-steal-instance-credentials.md
index b39812a7..4949d7e7 100755
--- a/docs/attack-techniques/AWS/aws.credential-access.ec2-steal-instance-credentials.md
+++ b/docs/attack-techniques/AWS/aws.credential-access.ec2-steal-instance-credentials.md
@@ -54,15 +54,15 @@ See also: [Known detection bypasses](https://hackingthe.cloud/aws/avoiding-detec
The following CloudTrail events are generated when this technique is detonated[^1]:
-- `ssm:SendCommand`
+- `ec2:DescribeInstances`
- `ssm:DescribeInstanceInformation`
-- `sts:GetCallerIdentity`
+- `ssm:GetCommandInvocation`
-- `ec2:DescribeInstances`
+- `ssm:SendCommand`
-- `ssm:GetCommandInvocation`
+- `sts:GetCallerIdentity`
??? "View raw detonation logs"
diff --git a/docs/attack-techniques/AWS/aws.defense-evasion.organizations-leave.md b/docs/attack-techniques/AWS/aws.defense-evasion.organizations-leave.md
index 70f9a3e2..52c937d5 100755
--- a/docs/attack-techniques/AWS/aws.defense-evasion.organizations-leave.md
+++ b/docs/attack-techniques/AWS/aws.defense-evasion.organizations-leave.md
@@ -49,10 +49,10 @@ Use the CloudTrail event LeaveOrganization
.
The following CloudTrail events are generated when this technique is detonated[^1]:
-- `sts:AssumeRole`
-
- `organizations:LeaveOrganization`
+- `sts:AssumeRole`
+
??? "View raw detonation logs"
diff --git a/docs/attack-techniques/AWS/aws.execution.ec2-user-data.md b/docs/attack-techniques/AWS/aws.execution.ec2-user-data.md
index 65a5c112..3154b1d7 100755
--- a/docs/attack-techniques/AWS/aws.execution.ec2-user-data.md
+++ b/docs/attack-techniques/AWS/aws.execution.ec2-user-data.md
@@ -61,14 +61,14 @@ provisioned before instantiation.
The following CloudTrail events are generated when this technique is detonated[^1]:
-- `ec2:ModifyInstanceAttribute`
-
-- `ec2:StopInstances`
-
- `ec2:DescribeInstances`
+- `ec2:ModifyInstanceAttribute`
+
- `ec2:StartInstances`
+- `ec2:StopInstances`
+
??? "View raw detonation logs"
diff --git a/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md b/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md
index 7e319695..95dd589d 100755
--- a/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md
+++ b/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md
@@ -66,11 +66,11 @@ Identify, through CloudTrail's StartSession
event, when a user is s
The following CloudTrail events are generated when this technique is detonated[^1]:
-- `ssm:TerminateSession`
+- `ssm:DescribeInstanceInformation`
- `ssm:StartSession`
-- `ssm:DescribeInstanceInformation`
+- `ssm:TerminateSession`
??? "View raw detonation logs"
diff --git a/docs/attack-techniques/AWS/aws.persistence.iam-create-backdoor-role.md b/docs/attack-techniques/AWS/aws.persistence.iam-create-backdoor-role.md
index 4f88f78e..50772f16 100755
--- a/docs/attack-techniques/AWS/aws.persistence.iam-create-backdoor-role.md
+++ b/docs/attack-techniques/AWS/aws.persistence.iam-create-backdoor-role.md
@@ -84,10 +84,10 @@ which generates a finding when a role can be assumed from a new AWS account or p
The following CloudTrail events are generated when this technique is detonated[^1]:
-- `iam:CreateRole`
-
- `iam:AttachRolePolicy`
+- `iam:CreateRole`
+
??? "View raw detonation logs"
diff --git a/docs/attack-techniques/AWS/aws.persistence.iam-create-user-login-profile.md b/docs/attack-techniques/AWS/aws.persistence.iam-create-user-login-profile.md
index 3ad9df11..b2a96201 100755
--- a/docs/attack-techniques/AWS/aws.persistence.iam-create-user-login-profile.md
+++ b/docs/attack-techniques/AWS/aws.persistence.iam-create-user-login-profile.md
@@ -58,11 +58,11 @@ In particular, it's suspicious when these events occur on IAM users intended to
The following CloudTrail events are generated when this technique is detonated[^1]:
-- `sts:GetCallerIdentity`
+- `iam:CreateLoginProfile`
- `iam:DeleteLoginProfile`
-- `iam:CreateLoginProfile`
+- `sts:GetCallerIdentity`
??? "View raw detonation logs"
diff --git a/docs/attack-techniques/AWS/aws.persistence.rolesanywhere-create-trust-anchor.md b/docs/attack-techniques/AWS/aws.persistence.rolesanywhere-create-trust-anchor.md
index 82f86315..fef2e1e8 100755
--- a/docs/attack-techniques/AWS/aws.persistence.rolesanywhere-create-trust-anchor.md
+++ b/docs/attack-techniques/AWS/aws.persistence.rolesanywhere-create-trust-anchor.md
@@ -58,10 +58,10 @@ Identify when a trust anchor is created, through CloudTrail's CreateTrustA
The following CloudTrail events are generated when this technique is detonated[^1]:
-- `rolesanywhere:CreateTrustAnchor`
-
- `rolesanywhere:CreateProfile`
+- `rolesanywhere:CreateTrustAnchor`
+
??? "View raw detonation logs"
diff --git a/docs/attack-techniques/AWS/aws.privilege-escalation.iam-update-user-login-profile.md b/docs/attack-techniques/AWS/aws.privilege-escalation.iam-update-user-login-profile.md
new file mode 100755
index 00000000..9b2f089a
--- /dev/null
+++ b/docs/attack-techniques/AWS/aws.privilege-escalation.iam-update-user-login-profile.md
@@ -0,0 +1,99 @@
+---
+title: Change IAM user password
+---
+
+# Change IAM user password
+
+
+ idempotent
+
+Platform: AWS
+
+## MITRE ATT&CK Tactics
+
+
+- Privilege Escalation
+
+## Description
+
+
+Establishes persistence by updating a Login Profile on an existing IAM user to change its password. This allows an attacker to hijack
+an IAM user with an existing login profile.
+
+Warm-up:
+
+- Create an IAM user with a login profile
+
+Detonation:
+
+- Update the user's login profile to change its password
+
+References:
+
+- https://www.invictus-ir.com/news/the-curious-case-of-dangerdev-protonmail-me
+- https://expel.com/blog/incident-report-from-cli-to-console-chasing-an-attacker-in-aws/
+- https://permiso.io/blog/lucr-3-scattered-spider-getting-saas-y-in-the-cloud
+
+
+## Instructions
+
+```bash title="Detonate with Stratus Red Team"
+stratus detonate aws.privilege-escalation.iam-update-user-login-profile
+```
+## Detection
+
+
+Through CloudTrail's UpdateLoginProfile
events.
+
+
+
+## Detonation logs new!
+
+The following CloudTrail events are generated when this technique is detonated[^1]:
+
+
+- `iam:UpdateLoginProfile`
+
+
+??? "View raw detonation logs"
+
+ ```json hl_lines="6"
+
+ [
+ {
+ "awsRegion": "megov-southcentral-3r",
+ "eventCategory": "Management",
+ "eventID": "a46a1a42-9ef1-48d4-9c61-507eb6d4019f",
+ "eventName": "UpdateLoginProfile",
+ "eventSource": "iam.amazonaws.com",
+ "eventTime": "2024-08-28T09:54:40Z",
+ "eventType": "AwsApiCall",
+ "eventVersion": "1.09",
+ "managementEvent": true,
+ "readOnly": false,
+ "recipientAccountId": "763751499319",
+ "requestID": "bd8967e5-b80d-48cd-b8b5-45c9905a4a7f",
+ "requestParameters": {
+ "userName": "stratus-red-team-update-login-profile-user"
+ },
+ "responseElements": null,
+ "sourceIPAddress": "212.3.253.233",
+ "tlsDetails": {
+ "cipherSuite": "TLS_AES_128_GCM_SHA256",
+ "clientProvidedHostHeader": "iam.amazonaws.com",
+ "tlsVersion": "TLSv1.3"
+ },
+ "userAgent": "stratus-red-team_33d1bcd6-0716-4e7f-a145-8a75625cf180",
+ "userIdentity": {
+ "accessKeyId": "AKIAV1MIS7NGMDMR83FC",
+ "accountId": "763751499319",
+ "arn": "arn:aws:iam::763751499319:user/christophe",
+ "principalId": "AIDAXYBG3LDVX65FGD9O",
+ "type": "IAMUser",
+ "userName": "christophe"
+ }
+ }
+ ]
+ ```
+
+[^1]: These logs have been gathered from a real detonation of this technique in a test environment using [Grimoire](https://github.com/DataDog/grimoire), and anonymized using [LogLicker](https://github.com/Permiso-io-tools/LogLicker).
diff --git a/docs/attack-techniques/AWS/index.md b/docs/attack-techniques/AWS/index.md
index 9d964d3c..f21092f5 100755
--- a/docs/attack-techniques/AWS/index.md
+++ b/docs/attack-techniques/AWS/index.md
@@ -121,3 +121,5 @@ Note that some Stratus attack techniques may correspond to more than a single AT
- [Create an IAM Roles Anywhere trust anchor](./aws.persistence.rolesanywhere-create-trust-anchor.md)
+- [Change IAM user password](./aws.privilege-escalation.iam-update-user-login-profile.md)
+
diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md
index aeb9f500..376463fe 100755
--- a/docs/attack-techniques/list.md
+++ b/docs/attack-techniques/list.md
@@ -47,6 +47,7 @@ This page contains the list of all Stratus Attack Techniques.
| [Add a Malicious Lambda Extension](./AWS/aws.persistence.lambda-layer-extension.md) | [AWS](./AWS/index.md) | Persistence, Privilege Escalation |
| [Overwrite Lambda Function Code](./AWS/aws.persistence.lambda-overwrite-code.md) | [AWS](./AWS/index.md) | Persistence |
| [Create an IAM Roles Anywhere trust anchor](./AWS/aws.persistence.rolesanywhere-create-trust-anchor.md) | [AWS](./AWS/index.md) | Persistence, Privilege Escalation |
+| [Change IAM user password](./AWS/aws.privilege-escalation.iam-update-user-login-profile.md) | [AWS](./AWS/index.md) | Privilege Escalation |
| [Execute Command on Virtual Machine using Custom Script Extension](./azure/azure.execution.vm-custom-script-extension.md) | [Azure](./azure/index.md) | Execution |
| [Execute Commands on Virtual Machine using Run Command](./azure/azure.execution.vm-run-command.md) | [Azure](./azure/index.md) | Execution |
| [Export Disk Through SAS URL](./azure/azure.exfiltration.disk-export.md) | [Azure](./azure/index.md) | Exfiltration |
diff --git a/docs/contributing.md b/docs/contributing.md
index cce618e8..271429ea 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -6,6 +6,40 @@ We welcome pull requests, contributions and feedback! For any bug report or feed
Stratus Red Team is opinionated in the attack techniques it packages - see [Philosophy](./attack-techniques/philosophy.md). Feel free to open an issue to discuss ideas about new attack techniques. You can see the current backlog using the GitHub issue label [`new-technique`](https://github.com/DataDog/stratus-red-team/issues?q=is%3Aissue+is%3Aopen+label%3Anew-technique).
+To create a new attack technique:
+1. Create a new folder under `v2/internal/attacktechniques/your-cloud/your-mitre-attack-tactic/your-attack-name`
+2. Create a `main.go` file that contains the detonation (and optionally, the revert) behavior. See for example [cloudtrail-stop/main.go](https://github.com/DataDog/stratus-red-team/blob/main/v2/internal/attacktechniques/aws/defense-evasion/cloudtrail-stop/main.go)
+3. If your attack technique contains pre-requisites, create a `main.tf` file
+4. Add your attack technique to the imports of `v2/internal/attacktechniques/main.go`
+
+To generate the logs dataset using [Grimoire](https://github.com/DataDog/grimoire):
+1. Install Grimoire
+2. Run the following to detonate the attack and retrieve CloudTrail logs:
+
+```bash
+# Build your local Stratus Red Team version
+make
+
+# Generate cloud audit logs
+./bin/stratus warmup your-attack
+grimoire shell --command 'export STRATUS_RED_TEAM_DETONATION_ID=$GRIMOIRE_DETONATION_ID; ./bin/stratus detonate your-attack' -o /tmp/your-attack.json
+# Press Ctrl+C once you see the expected events
+./bin/stratus cleanup your-attack
+```
+
+3. Anonymize the logs using [LogLicker](https://github.com/Permiso-io-tools/LogLicker):
+
+```bash
+# Note: see https://github.com/Permiso-io-tools/LogLicker/issues/5 for a currently necessary patch
+../LogLicker/venv/bin/python ../LogLicker/RunLogLicker.py rawtext -ifp /tmp/your-attack.json -ofp ./docs/detonation-logs/your-attack.json
+```
+
+4. Generate the docs:
+
+```bash
+make docs
+```
+
## Contributing to the core of Stratus Red Team
When contributing to the core of Stratus Red Team (i.e. anything that is not a new attack technique), include unit tests if applicable.
\ No newline at end of file
diff --git a/docs/detonation-logs/aws.privilege-escalation.iam-update-user-login-profile.json b/docs/detonation-logs/aws.privilege-escalation.iam-update-user-login-profile.json
new file mode 100644
index 00000000..dd9d3e58
--- /dev/null
+++ b/docs/detonation-logs/aws.privilege-escalation.iam-update-user-login-profile.json
@@ -0,0 +1,35 @@
+[
+ {
+ "awsRegion": "megov-southcentral-3r",
+ "eventCategory": "Management",
+ "eventID": "a46a1a42-9ef1-48d4-9c61-507eb6d4019f",
+ "eventName": "UpdateLoginProfile",
+ "eventSource": "iam.amazonaws.com",
+ "eventTime": "2024-08-28T09:54:40Z",
+ "eventType": "AwsApiCall",
+ "eventVersion": "1.09",
+ "managementEvent": true,
+ "readOnly": false,
+ "recipientAccountId": "763751499319",
+ "requestID": "bd8967e5-b80d-48cd-b8b5-45c9905a4a7f",
+ "requestParameters": {
+ "userName": "stratus-red-team-update-login-profile-user"
+ },
+ "responseElements": null,
+ "sourceIPAddress": "212.3.253.233",
+ "tlsDetails": {
+ "cipherSuite": "TLS_AES_128_GCM_SHA256",
+ "clientProvidedHostHeader": "iam.amazonaws.com",
+ "tlsVersion": "TLSv1.3"
+ },
+ "userAgent": "stratus-red-team_33d1bcd6-0716-4e7f-a145-8a75625cf180",
+ "userIdentity": {
+ "accessKeyId": "AKIAV1MIS7NGMDMR83FC",
+ "accountId": "763751499319",
+ "arn": "arn:aws:iam::763751499319:user/christophe",
+ "principalId": "AIDAXYBG3LDVX65FGD9O",
+ "type": "IAMUser",
+ "userName": "christophe"
+ }
+ }
+]
\ No newline at end of file
diff --git a/docs/index.yaml b/docs/index.yaml
index c4c21c32..58b93948 100644
--- a/docs/index.yaml
+++ b/docs/index.yaml
@@ -329,6 +329,13 @@ AWS:
- Privilege Escalation
platform: AWS
isIdempotent: false
+ - id: aws.privilege-escalation.iam-update-user-login-profile
+ name: Change IAM user password
+ isSlow: false
+ mitreAttackTactics:
+ - Privilege Escalation
+ platform: AWS
+ isIdempotent: true
EKS:
Lateral Movement:
- id: eks.lateral-movement.create-access-entry
diff --git a/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password/main.go b/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password/main.go
new file mode 100644
index 00000000..c7ffdf5d
--- /dev/null
+++ b/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password/main.go
@@ -0,0 +1,72 @@
+package aws
+
+import (
+ "context"
+ _ "embed"
+ "errors"
+ "github.com/aws/aws-sdk-go-v2/service/iam"
+ "github.com/datadog/stratus-red-team/v2/internal/utils"
+ "github.com/datadog/stratus-red-team/v2/pkg/stratus"
+ "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack"
+ "log"
+)
+
+//go:embed main.tf
+var tf []byte
+
+func init() {
+ stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{
+ ID: "aws.privilege-escalation.iam-update-user-login-profile",
+ FriendlyName: "Change IAM user password",
+ Description: `
+Establishes persistence by updating a Login Profile on an existing IAM user to change its password. This allows an attacker to hijack
+an IAM user with an existing login profile.
+
+Warm-up:
+
+- Create an IAM user with a login profile
+
+Detonation:
+
+- Update the user's login profile to change its password
+
+References:
+
+- https://www.invictus-ir.com/news/the-curious-case-of-dangerdev-protonmail-me
+- https://expel.com/blog/incident-report-from-cli-to-console-chasing-an-attacker-in-aws/
+- https://permiso.io/blog/lucr-3-scattered-spider-getting-saas-y-in-the-cloud
+`,
+ Detection: `
+Through CloudTrail's UpdateLoginProfile
events.
+`,
+ Platform: stratus.AWS,
+ IsIdempotent: true,
+ MitreAttackTactics: []mitreattack.Tactic{mitreattack.PrivilegeEscalation},
+ PrerequisitesTerraformCode: tf,
+ Detonate: detonate,
+ })
+}
+
+func detonate(params map[string]string, providers stratus.CloudProviders) error {
+ iamClient := iam.NewFromConfig(providers.AWS().GetConnection())
+ userName := params["user_name"]
+ newPassword := utils.RandomString(16) + ".#1Aa" // extra characters to ensure we meet password requirements, no matter the password policy
+
+ log.Println("Changing console password for IAM user " + userName)
+ _, err := iamClient.UpdateLoginProfile(context.Background(), &iam.UpdateLoginProfileInput{
+ UserName: &userName,
+ Password: &newPassword,
+ })
+ if err != nil {
+ return errors.New("unable to update IAM login profile: " + err.Error())
+ }
+
+ accountId, _ := utils.GetCurrentAccountId(providers.AWS().GetConnection())
+ log.Println("Updated console password for user")
+ loginUrl := "https://" + accountId + ".signin.aws.amazon.com/console"
+ log.Println("You can log in at: " + loginUrl)
+ log.Println("User name: " + userName)
+ log.Println("Password: " + newPassword)
+
+ return nil
+}
diff --git a/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password/main.tf b/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password/main.tf
new file mode 100644
index 00000000..8ac5a5ee
--- /dev/null
+++ b/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password/main.tf
@@ -0,0 +1,43 @@
+terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = "~> 3.0"
+ }
+ }
+}
+provider "aws" {
+ skip_region_validation = true
+ skip_credentials_validation = true
+ skip_get_ec2_platforms = true
+ default_tags {
+ tags = {
+ StratusRedTeam = true
+ }
+ }
+}
+
+locals {
+ resource_prefix = "stratus-red-team-update-login-profile"
+}
+
+resource "aws_iam_user" "legit-user" {
+ name = "${local.resource_prefix}-user"
+ force_destroy = true
+}
+
+resource "aws_iam_user_login_profile" "example" {
+ user = aws_iam_user.legit-user.name
+ password_length = 20
+ password_reset_required = false
+ pgp_key = "keybase:christophetd" // NOTE: this field is required, but we don't even output/use the password
+}
+
+
+output "user_name" {
+ value = aws_iam_user.legit-user.name
+}
+
+output "display" {
+ value = format("IAM user %s ready", aws_iam_user.legit-user.name)
+}
\ No newline at end of file
diff --git a/v2/internal/attacktechniques/main.go b/v2/internal/attacktechniques/main.go
index 147af3f4..10faafc0 100644
--- a/v2/internal/attacktechniques/main.go
+++ b/v2/internal/attacktechniques/main.go
@@ -39,6 +39,7 @@ import (
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/persistence/lambda-layer-extension"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/persistence/lambda-overwrite-code"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/persistence/rolesanywhere-create-trust-anchor"
+ _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/privilege-escalation/change-iam-user-password"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/vm-custom-script-extension"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/vm-run-command"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/exfiltration/disk-export"
diff --git a/v2/pkg/stratus/runner/runner.go b/v2/pkg/stratus/runner/runner.go
index af232561..7ab68257 100644
--- a/v2/pkg/stratus/runner/runner.go
+++ b/v2/pkg/stratus/runner/runner.go
@@ -8,6 +8,7 @@ import (
"github.com/datadog/stratus-red-team/v2/pkg/stratus/useragent"
"github.com/google/uuid"
"log"
+ "os"
"path/filepath"
"strings"
)
@@ -15,6 +16,8 @@ import (
const StratusRunnerForce = true
const StratusRunnerNoForce = false
+const EnvVarStratusRedTeamDetonationId = "STRATUS_RED_TEAM_DETONATION_ID"
+
type runnerImpl struct {
Technique *stratus.AttackTechnique
TechniqueState stratus.AttackTechniqueState
@@ -44,14 +47,25 @@ func NewRunner(technique *stratus.AttackTechnique, force bool) Runner {
func NewRunnerWithContext(ctx context.Context, technique *stratus.AttackTechnique, force bool) Runner {
stateManager := state.NewFileSystemStateManager(technique)
- uuid := uuid.New()
+
+ var correlationId = uuid.New()
+ var err error
+ if grimoireDetonationId := os.Getenv("STRATUS_RED_TEAM_DETONATION_ID"); grimoireDetonationId != "" {
+ log.Println("STRATUS_RED_TEAM_DETONATION_ID is set, using it as the correlation ID")
+ correlationId, err = uuid.Parse(grimoireDetonationId)
+ if err != nil {
+ log.Println("STRATUS_RED_TEAM_DETONATION_ID is not a valid UUID, falling back to a randomly-generated one: " + err.Error())
+ correlationId = uuid.New()
+ }
+ }
+
runner := &runnerImpl{
Technique: technique,
ShouldForce: force,
StateManager: stateManager,
- UniqueCorrelationID: uuid,
+ UniqueCorrelationID: correlationId,
TerraformManager: NewTerraformManagerWithContext(
- ctx, filepath.Join(stateManager.GetRootDirectory(), "terraform"), useragent.GetStratusUserAgentForUUID(uuid),
+ ctx, filepath.Join(stateManager.GetRootDirectory(), "terraform"), useragent.GetStratusUserAgentForUUID(correlationId),
),
Context: ctx,
}
diff --git a/v2/tools/generate-techniques-documentation.go b/v2/tools/generate-techniques-documentation.go
index b41744d2..fc6c5bd7 100644
--- a/v2/tools/generate-techniques-documentation.go
+++ b/v2/tools/generate-techniques-documentation.go
@@ -8,6 +8,7 @@ import (
"log"
"os"
"path/filepath"
+ "sort"
"strings"
"text/template"
@@ -143,6 +144,7 @@ func findDetonationLogs(technique *stratus.AttackTechnique) *DetonationLogs {
for k := range eventNamesSet {
eventNames = append(eventNames, k)
}
+ sort.Strings(eventNames)
rawLogs := strings.ReplaceAll(string(data), "\n", "\n\t") // indent for markdown
var eventNameLines []int