Skip to content
Back to research

Code Spaces: the AWS attack that deleted its backups

A plain-English account of the June 2014 Code Spaces shutdown, checked against the company’s statement as reported at the time, followed by the MFA, least-privilege, cross-account backup, Vault Lock, Recycle Bin and alerting controls that break the same chain in AWS today, with commands to check your own account.

YouTubeCode Spaces: the AWS attack that deleted its backupsOpen the related video

What happened

The part most people miss

By the company’s own account, the attacker never got inside a single Code Spaces server. They didn’t need to. From the AWS control panel they deleted the servers, the disks, the saved server images and the backups. The attack took about 12 hours, and then the company announced it was closing [1][3].

Code Spaces was a code-hosting company. Software teams kept their source code and project files on its service [4]. On 17 June 2014 it was hit by a distributed denial-of-service (DDoS) attack, a flood of traffic from many machines at once that makes a service too busy to answer real users [2][5].

While it dealt with the flood, the company found something worse. Someone had got into the Amazon EC2 control panel it used to run its cloud servers [1]. The intruder left messages asking Code Spaces to get in touch through a Hotmail address, and demanded a large fee to stop the DDoS [1][3].

Code Spaces changed its EC2 passwords. It didn’t help. The company said the attacker had already created backup logins to the panel [1]. When the attacker saw the company trying to take the account back, they started deleting things at random from the panel [2].

WhatWhat it isWhat happened to it
EBS snapshotsPoint-in-time backups of the servers’ disksAll removed
S3 bucketsStorage folders for files, exports and backupsRemoved
AMIsSaved server images, used to rebuild a server quicklyAll removed
EBS volumesThe disks attached to the running serversSome removed
EC2 instancesThe running virtual serversSeveral removed
What Code Spaces said was deleted [1]

The company said most of its data, backups, machine configurations and offsite backups were partially or completely deleted [1]. Reports at the time put the whole thing at about 12 hours [3]. Code Spaces then said it would not be able to operate beyond that point. The cost of fixing the damage and refunding customers left it with, in its words, “no alternative but to cease trading” [1][2].

The painful detail is what the company had promised. Code Spaces had advertised full redundancy, real-time backups to several off-site locations, and a recovery plan it said was proven and practised [4]. The backups existed. They were just in reach of the same control panel the attacker now held.

Control panel access is not server access

Code Spaces said no machine had been accessed, because the intruder didn’t have its private keys [1]. A private key is the secret file that lets someone log in to a server. It sounds reassuring. Bradley’s video explains why it wasn’t.

AWS, Amazon Web Services, is Amazon’s cloud: companies rent computing power, storage and networking instead of buying the hardware. Amazon EC2 is the AWS service for renting virtual servers, and the AWS console is the web page for creating and managing them [7]. Logging in to a server means getting inside the machine. Using the console means working on the machine from outside: starting it, stopping it, or deleting it outright.

Think of the private keys as the keys to each flat in a building, and the console as the building’s management office. Without the keys you can’t get into the flats. From the office you can still have the whole building knocked down.

Five AWS terms in this story

An EC2 instance is one running virtual server [7]. An EBS volume is a disk attached to it, used like a hard drive [8]. An EBS snapshot is a point-in-time backup of that disk, which you can turn back into a new disk later [8]. An AMI, or Amazon Machine Image, is a saved image of a whole server, used to launch a new one without building it by hand [9]. An S3 bucket is a storage container for files, which S3 calls objects [10].

In the console, every one of those is something you can delete. So is every login. The account that runs the servers can also delete the backups of those servers, unless someone has deliberately set things up so it can’t. Code Spaces hadn’t.

How the attack worked, step by step

1. Get into the control panel

How the attacker got in has never been made public. Reports at the time said it was not clear, and the breach catalogue still lists the initial access as unknown [4][5].

2. Make noise and demand money

A DDoS flooded the service, and messages in the panel asked for contact through Hotmail and a large fee to make it stop [1][3].

3. Make spare ways in

Before the company reacted, the attacker had created backup logins to the panel. Changing the passwords locked out one door and left the others open [1].

4. Delete when challenged

Once the attacker saw the company trying to recover the account, they deleted snapshots, buckets, server images, disks and servers [1][2].

5. Leave nothing to restore from

The offsite backups went too [1]. With no clean copy left, the company announced it would close [1][2].

Bradley’s view in the video is that the DDoS was smoke and mirrors. The deletion didn’t happen because the traffic was heavy. It happened because the attacker had the control panel. One attack made Code Spaces hard to reach. The other erased the way back.

Step 3 is the one incident responders dread. Removing one way in does nothing if the attacker has already made several more. Code Spaces was still trying to lock the account while the attacker carried on working.

What is known, and what isn’t

No one has ever been identified as the attacker [5]. So unlike many breaches, there is no indictment or court record to check the story against. Almost everything comes from the company’s own statement, as quoted at the time [1][6].

PointWhat is said, and whereHow firm
Start dateThe DDoS began on 17 June 2014. eSecurity Planet [2] and the breach catalogue [5]Reported from the company’s statement
Control panelThe attacker had the EC2 control panel and asked for money through a Hotmail address. The company’s statement, quoted by Threatpost [1]. The fee is from GovInfoSecurity [3]The company’s own account
The serversNo server was logged in to, because the attacker lacked the private keys. The statement [1]The company’s account at the time
Backup loginsExtra logins had been created, so changing the passwords failed. The statement [1]The company’s account. It doesn’t say what kind of logins they were
The deletionsSnapshots, buckets, AMIs, some volumes and several servers were deleted. The statement [1]The company’s account. No independent check was published
TimelineIt all took about 12 hours. GovInfoSecurity [3]Reported
The way inHow the attacker got in. Computerworld [4] and the breach catalogue [5]Never made public
The attackerWho did it. The breach catalogue [5]Unknown. No one has been identified
Each claim, its source and how firm it is
Why the unknowns don’t change the fix

We don’t know whether a password was stolen, guessed or reused, and we won’t guess. The defences below assume any single login can be lost, and make sure losing it doesn’t mean losing the company.

What defenders could have seen

Every move the attacker made in the panel was an AWS API call, so today each one would leave a record in CloudTrail, AWS’s log of actions taken in an account [11]. We don’t know what logging Code Spaces had in 2014, so read this as what you can have now, not as what they missed.

SignalWhere you see itWhat it looked like here
New logins or keysCloudTrail events such as CreateUser, CreateLoginProfile and CreateAccessKey, sent through an EventBridge rule [20]. GuardDuty Persistence:IAMUser/AnomalousBehavior [22]The backup logins [1]
Bursts of deletesCloudTrail events such as DeleteSnapshot, DeregisterImage, DeleteVolume and TerminateInstances, counted by a CloudWatch alarm [21]. GuardDuty Impact:IAMUser/AnomalousBehavior [22]Snapshots, images, disks and servers deleted within hours [1][3]
Root user in useGuardDuty Policy:IAMUser/RootCredentialUsage [22]Not known. The reports don’t say which login was used
Logging switched offGuardDuty Stealth:IAMUser/CloudTrailLoggingDisabled [22]Not reported here. An attacker who switches logging off hides everything after it
Signals from this attack, and where AWS shows them now

GuardDuty is AWS’s managed threat detection service. It reads CloudTrail and raises findings like the ones above [22]. EventBridge passes events, such as a new login being created, to wherever you want them: an email, a chat channel or a ticket [20].

Seeing it wasn’t the problem

Code Spaces knew it was under attack while it was happening. It still lost, because the attacker could delete faster than the company could lock them out. Alerts buy time. Only a backup the attacker can’t reach turns that time into a recovery.

How to stop it

Bradley’s lesson is not to avoid AWS. It is defence in depth to control the blast radius: how much damage one stolen login can do. His fixes are MFA, least privilege, backups in a separate account and a vault lock. Here is each one as real settings, plus two safety nets. Swap the example account numbers, Region, names and IDs for your own.

Fix 1: MFA on every human login. Multi-factor authentication (MFA) means a password alone isn’t enough to sign in. You also need a code or a security key. AWS recommends it for everyone with console access, and says to keep the root user, the all-powerful owner login of an account, for the few jobs that need it [12]. Since 17 June 2025 AWS has enforced MFA for root users in every type of account [13]. Check the root user has MFA and no access keys, then find any person who signs in with a password but no MFA [15].

Bash
# Root user: want [1, 0] (MFA on, no root access keys)
aws iam get-account-summary \
  --query "SummaryMap.[AccountMFAEnabled,AccountAccessKeysPresent]"

# IAM users who have a console password but no MFA
aws iam generate-credential-report
aws iam get-credential-report --query Content --output text | base64 --decode \
  | awk -F, 'tolower($4)=="true" && tolower($8)=="false" {print $1}'

Fix 2: an everyday login can’t delete a recovery asset. Bradley says the most powerful credentials shouldn’t be treated like normal logins. This policy refuses the destructive actions from this attack unless the person signed in with MFA [14]. Attach it to the groups your people belong to, not to automation roles, which never carry MFA and would be blocked.

JSON
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyDestructiveActionsWithoutMfa",
      "Effect": "Deny",
      "Action": [
        "ec2:TerminateInstances",
        "ec2:DeleteSnapshot",
        "ec2:DeleteVolume",
        "ec2:DeregisterImage",
        "s3:DeleteBucket",
        "backup:DeleteRecoveryPoint",
        "backup:DeleteBackupVault"
      ],
      "Resource": "*",
      "Condition": { "BoolIfExists": { "aws:MultiFactorAuthPresent": "false" } }
    }
  ]
}

This slows an attacker who has a password or an access key. It won’t stop one who has a full MFA session, which is why the next fix matters most.

Fix 3: keep a copy in another account, and lock it. Bradley calls backups within the attacker’s reach the major architectural flaw in this story. AWS Backup can copy each backup into a vault, a container for backups, in a separate AWS account [16]. The breach catalogue gives the same lesson: keep a copy of critical data in an account that doesn’t run your workloads [5]. Both accounts must be in the same AWS Organizations organisation, and the copy’s vault can’t be the default vault [16].

Bash
# 1. In the organisation's management account: allow cross-account copies
aws backup update-global-settings \
  --global-settings isCrossAccountBackupEnabled=true

# 2. In the separate backup account (444455556666): a vault for the copies,
#    encrypted with a customer managed KMS key
aws backup create-backup-vault --backup-vault-name central-locked \
  --encryption-key-arn "<KMS key ARN in the backup account>"

# 3. Still in the backup account: let the workload account copy into it
aws backup put-backup-vault-access-policy --backup-vault-name central-locked \
  --policy file://vault-policy.json
JSON
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCopyFromWorkloadAccount",
      "Effect": "Allow",
      "Principal": { "AWS": "111122223333" },
      "Action": "backup:CopyIntoBackupVault",
      "Resource": "*"
    }
  ]
}

Then lock the vault. Vault Lock in compliance mode sets a grace time of at least 3 days. After that, the lock can’t be changed or removed by the customer, the account owner or AWS while the vault holds recovery points [17]. Any attempt to delete a backup in a locked vault is refused, even from the root user [17]. Test it in governance mode first if you’re unsure: leaving out the grace time gives governance mode, which people with the right permission can still remove [17].

Bash
# In the backup account. Compliance mode: after 3 days nobody can undo this
aws backup put-backup-vault-lock-configuration \
  --backup-vault-name central-locked \
  --changeable-for-days 3 --min-retention-days 7 --max-retention-days 35

In the workload account, a backup plan takes a nightly backup into a local vault and copies it to the locked vault. If your resources use a customer managed KMS key, share that key with the backup account [16].

JSON
{
  "BackupPlanName": "daily-with-offsite-copy",
  "Rules": [
    {
      "RuleName": "daily",
      "TargetBackupVaultName": "local-vault",
      "ScheduleExpression": "cron(0 3 * * ? *)",
      "Lifecycle": { "DeleteAfterDays": 35 },
      "CopyActions": [
        {
          "DestinationBackupVaultArn": "<central-locked vault ARN in the backup account>",
          "Lifecycle": { "DeleteAfterDays": 35 }
        }
      ]
    }
  ]
}
Bash
# Create the plan, then choose what it backs up (here: anything tagged backup=daily)
aws backup create-backup-plan --backup-plan file://plan.json

aws backup create-backup-selection \
  --backup-plan-id <BackupPlanId from the last command> \
  --backup-selection '{
    "SelectionName": "tagged",
    "IamRoleArn": "<AWSBackupDefaultServiceRole ARN>",
    "ListOfTags": [
      {"ConditionType": "STRINGEQUALS", "ConditionKey": "backup", "ConditionValue": "daily"}
    ]
  }'

If the originals are deleted, copy the backup from the backup account back to the workload account, then restore it as normal [16]. An attacker who takes over the workload account, as happened to Code Spaces, can’t reach the copies at all.

Safety net: Recycle Bin. Recycle Bin keeps deleted EBS snapshots and AMIs for a set time, so they can be recovered [18]. A rule with no tags covers the whole Region. Locking the rule means it can’t be changed or deleted until an unlock delay of 7 to 30 days has passed [19]. Snapshots and AMIs can be kept for 1 to 365 days [18].

Bash
# Keep deleted snapshots for 14 days, with a locked rule
aws rbin create-rule --resource-type EBS_SNAPSHOT \
  --retention-period RetentionPeriodValue=14,RetentionPeriodUnit=DAYS \
  --description "Keep deleted snapshots 14 days" \
  --lock-configuration "UnlockDelay={UnlockDelayValue=7,UnlockDelayUnit=DAYS}"

# The same for server images (AMIs)
aws rbin create-rule --resource-type EC2_IMAGE \
  --retention-period RetentionPeriodValue=14,RetentionPeriodUnit=DAYS \
  --description "Keep deleted AMIs 14 days" \
  --lock-configuration "UnlockDelay={UnlockDelayValue=7,UnlockDelayUnit=DAYS}"

Safety net: alarms on the moves this attacker made. IAM changes, such as a new user or a new password, reach EventBridge through CloudTrail in US East (N. Virginia), whichever Region you work in, so the rule goes there [20]. Save this pattern as iam-changes.json.

JSON
{
  "source": ["aws.iam"],
  "detail-type": ["AWS API Call via CloudTrail"],
  "detail": {
    "eventSource": ["iam.amazonaws.com"],
    "eventName": [
      "CreateUser",
      "CreateLoginProfile",
      "UpdateLoginProfile",
      "CreateAccessKey",
      "AttachUserPolicy",
      "PutUserPolicy",
      "AddUserToGroup",
      "DeactivateMFADevice"
    ]
  }
}
Bash
# Send every new login, key or permission change to the security-alerts topic
aws events put-rule --region us-east-1 --name iam-new-logins \
  --event-pattern file://iam-changes.json
aws events put-targets --region us-east-1 --rule iam-new-logins \
  --targets "Id=alert,Arn=<security-alerts topic ARN in us-east-1>"

The SNS topic’s access policy must allow events.amazonaws.com to publish to it. For a burst of deletes, send CloudTrail to CloudWatch Logs and count the destructive calls [21]. Five in five minutes is a starting point. Set it above what your own automation does on a normal day.

Bash
# The calls to count, built up in one shell variable
PATTERN='{ ($.eventName = "DeleteSnapshot") || ($.eventName = "DeregisterImage") ||'
PATTERN+=' ($.eventName = "DeleteVolume") || ($.eventName = "TerminateInstances") ||'
PATTERN+=' ($.eventName = "DeleteBucket") || ($.eventName = "DeleteRecoveryPoint") }'

# Count them in the CloudTrail log group
aws logs put-metric-filter --log-group-name example-cloudtrail-logs \
  --filter-name destructive-calls --filter-pattern "$PATTERN" \
  --metric-transformations \
  metricName=DestructiveCalls,metricNamespace=Security,metricValue=1

# Alarm on 5 or more in 5 minutes
aws cloudwatch put-metric-alarm --alarm-name mass-deletes \
  --namespace Security --metric-name DestructiveCalls \
  --statistic Sum --period 300 --evaluation-periods 1 \
  --threshold 5 --comparison-operator GreaterThanOrEqualToThreshold \
  --treat-missing-data notBreaching \
  --alarm-actions "<security-alerts topic ARN>"

Check your own account

These checks only read settings. They change nothing, so they are safe to run in production.

Read-only check 1

Check the root user is locked down

Run this in each account. It shows whether the root user has MFA and whether it has access keys.

Root MFA and root keys
aws iam get-account-summary \
  --query "SummaryMap.[AccountMFAEnabled,AccountAccessKeysPresent]"

ResultSafe answer: [1, 0]

What to do about it

A 0 in the first place means root has no MFA. A 1 in the second means root has access keys, which almost nothing needs. Fix both before anything else.

Control that stops it
MFA on the root user, no root access keys, and root kept for the few tasks that need it [12][13].
Read-only check 2

Find people who sign in without MFA

The credential report lists every IAM user. This pulls out the ones with a console password and no MFA.

Password users without MFA
aws iam generate-credential-report
aws iam get-credential-report --query Content --output text | base64 --decode \
  | awk -F, 'tolower($4)=="true" && tolower($8)=="false" {print $1}'

ResultSafe answer: no output at all

What to do about it

Every name printed can sign in to the console with a password alone. One stolen password is then enough to reach the control panel.

Control that stops it
MFA for every person, and the deny-without-MFA policy from Fix 2 on their groups [14].
Read-only check 3

Check your backups leave the account

List your backup plans, then look at where each one copies to.

Copy destinations in a backup plan
aws backup list-backup-plans \
  --query "BackupPlansList[].[BackupPlanName,BackupPlanId]" --output table

aws backup get-backup-plan --backup-plan-id <BackupPlanId> \
  --query "BackupPlan.Rules[].CopyActions[].DestinationBackupVaultArn"

ResultSafe answer: a vault ARN with a different account number from this one

What to do about it

An empty list, or a vault in this same account, means whoever takes this account can delete the backups too. That is the Code Spaces position.

Control that stops it
A cross-account copy to a dedicated backup account, as in Fix 3 [16].
Read-only check 4

Check the backup vault is really locked

Run this in the backup account.

Vault Lock state
aws backup list-backup-vaults \
  --query "BackupVaultList[].[BackupVaultName,Locked,MinRetentionDays,LockDate]" \
  --output table

ResultSafe answer: Locked is True, with a LockDate in the past

What to do about it

Locked False means no lock. Locked True with no LockDate is governance mode, which someone with enough permission can remove. A LockDate in the future means compliance mode is still in its grace time. Only a LockDate in the past means the lock is set for good [17].

Control that stops it
Vault Lock in compliance mode on the vault that holds the copies [17].
Read-only check 5

Check a deleted snapshot would come back

Run this in each Region you use, and again with EC2_IMAGE for server images.

Recycle Bin rules for snapshots
aws rbin list-rules --resource-type EBS_SNAPSHOT \
  --query "Rules[].[Identifier,RetentionPeriod.RetentionPeriodValue,LockState]" \
  --output table

ResultSafe answer: at least one rule, with LockState locked

What to do about it

No rule means a deleted snapshot is gone at once. An unlocked rule can be deleted by the same attacker before they delete the snapshots.

Control that stops it
Locked Region-level Recycle Bin rules for snapshots and AMIs [18][19].

Bradley ends the video on this. In AWS, servers, disks, server images, buckets, users and backups all become buttons. That is powerful, and just as dangerous. The question isn’t whether one control will protect you. It is how you lock down each part, so that one lost login can’t press every button at once.

Sources and further reading

References

Numbered sources support the practical points in this article. Video timestamps will appear when the related media is published.

  1. ArticleThreatpost
    Hacker Puts Hosting Service Code Spaces Out of Businesshttps://threatpost.com/hacker-puts-hosting-service-code-spaces-out-of-business/106761/

    18 June 2014. Quotes the company’s statement: the control panel, the private keys, the backup logins, what was deleted and the decision to stop trading.

  2. ArticleeSecurity Planet
    Code Spaces Destroyed by Cyber Attackhttps://www.esecurityplanet.com/networks/code-spaces-destroyed-by-cyber-attack/

    23 June 2014. Dates the DDoS to 17 June 2014 and quotes the deletions starting once the attacker saw the recovery attempt.

  3. ArticleGovInfoSecurity
    DDoS + Breach = End of Businesshttps://www.govinfosecurity.com/ddos-breach-end-business-a-6978

    23 June 2014. The 12-hour timeline and the demand for a large fee to stop the DDoS.

  4. ArticleComputerworld
    Hacker puts ‘full redundancy’ code-hosting firm out of businesshttps://www.computerworld.com/article/2695663/hacker-puts--full-redundancy--code-hosting-firm-out-of-business.html

    19 June 2014. What Code Spaces had promised about redundancy and recovery, and that it was not clear how the attacker got in.

  5. ReportPublic Cloud Security Breaches
    Codespaces (2014)https://www.breaches.cloud/incidents/codespaces/

    The catalogue entry: 17 to 18 June 2014, initial access unknown, attacker never identified.

  6. AdvisoryInternet Archive
    Code Spaces statement on its home page, as archived on 18 June 2014https://web.archive.org/web/20140618165208/http://www.codespaces.com/

    The company’s own statement, linked by [5]. The quotes on this page are taken from the reports above, which reproduced it at the time.

  7. Vendor DocAWS
    What is Amazon EC2?https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html
  8. Vendor DocAWS
    What is Amazon Elastic Block Store?https://docs.aws.amazon.com/ebs/latest/userguide/what-is-ebs.html

    EBS volumes and the point-in-time snapshots taken from them.

  9. Vendor DocAWS
    Amazon Machine Images in Amazon EC2https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html
  10. Vendor DocAWS
    What is Amazon S3?https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html
  11. Vendor DocAWS
    What Is AWS CloudTrail?https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-user-guide.html
  12. Vendor DocAWS
    Security best practices in IAMhttps://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html

    MFA for people, least-privilege permissions, and keeping the root user for the few tasks that need it.

  13. Vendor DocAWS
    AWS IAM now enforces MFA for root users across all account typeshttps://aws.amazon.com/about-aws/whats-new/2025/06/aws-iam-mfa-root-users-across-all-account-types/

    The 17 June 2025 announcement.

  14. Vendor DocAWS
    Secure API access with MFAhttps://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_mfa_configure-api-require.html

    The aws:MultiFactorAuthPresent condition used in the deny policy below.

  15. Vendor DocAWS
    Generate credential reports for your AWS accounthttps://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html
  16. Vendor DocAWS
    Creating backup copies across AWS accountshttps://docs.aws.amazon.com/aws-backup/latest/devguide/create-cross-account-backup.html

    Requirements, the vault access policy, and restoring by copying back to the source account.

  17. Vendor DocAWS
    AWS Backup Vault Lockhttps://docs.aws.amazon.com/aws-backup/latest/devguide/vault-lock.html

    Governance and compliance modes, the grace time, and what can’t be changed afterwards.

  18. Vendor DocAWS
    Recover deleted EBS volumes, EBS snapshots, and EBS-backed AMIs with Recycle Binhttps://docs.aws.amazon.com/ebs/latest/userguide/recycle-bin.html
  19. Vendor DocAWS
    create-rule (Recycle Bin), AWS CLI Command Referencehttps://docs.aws.amazon.com/cli/latest/reference/rbin/create-rule.html

    Retention periods, the lock configuration and the 7 to 30 day unlock delay.

  20. Vendor DocAWS
    AWS Identity and Access Management eventshttps://docs.aws.amazon.com/eventbridge/latest/ref/events-ref-iam.html

    IAM changes reach EventBridge through CloudTrail, in US East (N. Virginia).

  21. Vendor DocAWS
    Creating CloudWatch alarms for CloudTrail events: exampleshttps://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudwatch-alarms-for-cloudtrail.html
  22. Vendor DocAWS
    GuardDuty IAM finding typeshttps://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-iam.html

    Defines the persistence, impact, root-use and logging-disabled findings named below.

Seen something like this in your own estate?

Find the same weaknesses before an attacker does

Talk to us