facebookincubator / TTPForge

The TTPForge is a Cybersecurity Framework for developing, automating, and executing attacker Tactics, Techniques, and Procedures (TTPs).
MIT License
335 stars 31 forks source link

💡 [REQUEST] - Handling state between steps for complex TTPs #478

Open godlovepenn opened 9 months ago

godlovepenn commented 9 months ago

Implementation PR

No response

Reference Issues

No response

Summary

Basic Example

Sample TTP

---
name: create-and-delete-aws-resources
description: Creates an Ubuntu machine with a Public IP and sets up SSH access
requirements:
  platforms:
    - os: darwin
    - os: linux

args:
  - name: group
    type: string
    default: "TestSimulation"

steps:
  - name: create-vpc
    description: "Create a new VPC named {{.Args.group}}VPC"
    inline: |
      VPC_ID=$(aws ec2 create-vpc --cidr-block 10.0.0.0/16 --query 'Vpc.VpcId' --output text)
      echo "VPC_ID=$VPC_ID" > aws_resource_ids.txt
      aws ec2 create-tags --resources "$VPC_ID" --tags Key=Name,Value={{.Args.group}}VPC
    cleanup:
      description: "Delete {{.Args.group}}VPC"
      inline: |
        source aws_resource_ids.txt
        aws ec2 delete-vpc --vpc-id "$VPC_ID"

  - name: create-internet-gateway
    description: "Create an Internet Gateway and attach it to {{.Args.group}}VPC"
    inline: |
      source aws_resource_ids.txt
      IGW_ID=$(aws ec2 create-internet-gateway --query 'InternetGateway.InternetGatewayId' --output text)
      echo "IGW_ID=$IGW_ID" >> aws_resource_ids.txt
      aws ec2 attach-internet-gateway --internet-gateway-id "$IGW_ID" --vpc-id "$VPC_ID"
      aws ec2 create-tags --resources "$IGW_ID" --tags Key=Name,Value={{.Args.group}}IGW

  - name: create-subnet
    description: "Create a Subnet named {{.Args.group}}Subnet in {{.Args.group}}VPC"
    inline: |
      source aws_resource_ids.txt
      SUBNET_ID=$(aws ec2 create-subnet --vpc-id "$VPC_ID" --cidr-block 10.0.1.0/24 --query 'Subnet.SubnetId' --output text)
      echo "SUBNET_ID=$SUBNET_ID" >> aws_resource_ids.txt
      aws ec2 create-tags --resources "$SUBNET_ID" --tags Key=Name,Value={{.Args.group}}Subnet
      aws ec2 modify-subnet-attribute --subnet-id "$SUBNET_ID" --map-public-ip-on-launch

  - name: create-route-table
    description: "Create a Route Table named {{.Args.group}}RouteTable in {{.Args.group}}VPC"
    inline: |
      source aws_resource_ids.txt
      ROUTE_TABLE_ID=$(aws ec2 create-route-table --vpc-id "$VPC_ID" --query 'RouteTable.RouteTableId' --output text)
      echo "ROUTE_TABLE_ID=$ROUTE_TABLE_ID" >> aws_resource_ids.txt
      aws ec2 create-tags --resources "$ROUTE_TABLE_ID" --tags Key=Name,Value={{.Args.group}}RouteTable
      aws ec2 create-route --route-table-id "$ROUTE_TABLE_ID" --destination-cidr-block 0.0.0.0/0 --gateway-id "$IGW_ID"
      aws ec2 associate-route-table --route-table-id "$ROUTE_TABLE_ID" --subnet-id "$SUBNET_ID"

    cleanup:
      description: "Delete {{.Args.group}}Subnet, {{.Args.group}}IGW, and {{.Args.group}}RouteTable"
      inline: |
        source aws_resource_ids.txt
        # deleting subnet
        aws ec2 delete-subnet --subnet-id "$SUBNET_ID"
        # detach and delete internet gateway
        aws ec2 detach-internet-gateway --internet-gateway-id "$IGW_ID" --vpc-id "$VPC_ID"
        aws ec2 delete-internet-gateway --internet-gateway-id "$IGW_ID"
        # delete route table
        aws ec2 delete-route-table --route-table-id "$ROUTE_TABLE_ID"

  - name: create-security-group
    description: "Create Security Group in the specified VPC named {{.Args.group}}SecurityGroup"
    inline: |
      source aws_resource_ids.txt
      SECURITY_GROUP_ID=$(aws ec2 create-security-group --group-name "{{.Args.group}}SecurityGroup" --description "Security group for {{.Args.group}} instances" --vpc-id "$VPC_ID" --query 'GroupId' --output text)
      echo "SECURITY_GROUP_ID=$SECURITY_GROUP_ID" >> aws_resource_ids.txt
      aws ec2 authorize-security-group-ingress --group-id "$SECURITY_GROUP_ID" --protocol tcp --port 22 --cidr 0.0.0.0/0

    cleanup:
      description: "Delete {{.Args.group}}SecurityGroup"
      inline: |
        source aws_resource_ids.txt
        aws ec2 delete-security-group --group-id "$SECURITY_GROUP_ID"

  - name: create-key-pair
    description: "Create Key Pair named {{.Args.group}}_key"
    inline: |
      source aws_resource_ids.txt
      aws ec2 create-key-pair --key-name "{{.Args.group}}"_key --query 'KeyMaterial' --output text > "{{.Args.group}}_key.pem"
      chmod 600 "{{.Args.group}}_key.pem"
    cleanup:
      description: "Delete {{.Args.group}} Key Pair"
      inline: |
        aws ec2 delete-key-pair --key-name "{{.Args.group}}_key"
        rm -f "{{.Args.group}}_key.pem"

  - name: get-latest-ubuntu-ami-id
    description: "Retrieve ami id for ubuntu 22.04 amd64 "
    inline: |

      # https://raw.githubusercontent.com/l50/dotfiles/main/aws
      amiNamePattern="ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"
      AMI_ID=$(aws ec2 describe-images --filters "Name=name,Values=$amiNamePattern" --owners amazon --query 'sort_by(Images, &CreationDate)[-1].ImageId' --output text)
      echo "AMI_ID=$AMI_ID" >> aws_resource_ids.txt

  - name: create-ec2-instance
    description: "Launch EC2 Instance named {{.Args.group}}Ubuntu"
    inline: |
      source aws_resource_ids.txt
      INSTANCE_ID=$(aws ec2 run-instances --image-id "$AMI_ID" --count 1 --instance-type "t2.micro" --key-name "{{.Args.group}}_key" --security-group-ids "$SECURITY_GROUP_ID" --subnet-id "$SUBNET_ID" --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value={{.Args.group}}Ubuntu}]' --query 'Instances[0].InstanceId' --output text)
      echo "INSTANCE_ID=$INSTANCE_ID" >> aws_resource_ids.txt
      aws ec2 wait instance-running --instance-ids "$INSTANCE_ID"
      echo "Instance is now running"
      PUBLIC_IP=$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" --query 'Reservations[0].Instances[0].PublicIpAddress' --output text )
      echo "PUBLIC_IP=$PUBLIC_IP" >> aws_resource_ids.txt
      echo "Public IP of the instance: $PUBLIC_IP"
      echo "SSH to machine with ssh -i {{.Args.group}}_key.pem ubuntu@$PUBLIC_IP"
    cleanup:
      description: "Terminate {{.Args.group}}Ubuntu Instance"
      inline: |
        source aws_resource_ids.txt
        aws ec2 terminate-instances --instance-ids "$INSTANCE_ID"
        aws ec2 wait instance-terminated --instance-ids "$INSTANCE_ID"

Execution Command ttpforge run ttp.yaml --arg group=TestSimulation --cleanup-delay-seconds=60

Drawbacks

Unresolved questions

No response

d3sch41n commented 9 months ago

Great issue!

This should be a medium-scope change that can go onto the H1 development roadmap I'll be drafting when I return.

We can at the very least get rid of all the source calls by emulating Github actions' behavior.

I implemented a version of this a long time ago that one TTP from the internal catalog uses, but it should be revamped.

Claiming this one for when I return