How To Use AWS CloudTrail For Intrusion Detection To Monitor Your AWS Account For Unwanted Activity
Summary
Use AWS CloudTrail as the basis for a simple Intrusion Detection System to monitor your AWS account for unwanted activity.
Background
While I follow best practices for security, it is always possible that a bad actor could obtain my credentials and gain access to my AWS account.
Once access is gained, such criminals could launch new, maximum-sized instances all over the world and proceed to harness them for nefarious purposes, often for DDoS attacks and crypto-mining.
These instances can accrue enormous charges in a short time given the cost per instance times the quantity launched.
Attacks like this can cripple an organization financially, especially because Amazon Web Services are NOT flexible about refunding such charges if they happen more than once.
AWS-based Solutions
AWS itself offers a number of paid services to assist with account security, including CloudWatch and Trusted Advisor.
While they are no doubt excellent offerings, I was reluctant to pay more than I had to, and the CloudWatch tool seemed like overkill in terms of complexity.
Chosen Solution
While CloudWatch did not work for me, the underlying CloudTrail service in fact was the answer, when used with the aws command-line tool to query the associated event data periodically via cron for non-Read-Only events.
This solution required me to roll my own tool (in Perl) to interpret the data and alert via email under the desired circumstances.
Solution Steps
- Make sure you have the latest AWS CLI tool installed, Instructions are here: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
- Create a new Trail in the AWS CloudTrail console.
- This Trail specifically watches all Management events/API activity (both Read and Write, no exclusions).
- When you create the Trail, choose “Log to S3” and the form will allow you to create a new bucket just for this purpose – you should do so.
- Detailed instructions are here: https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-create-a-trail-using-the-console-first-time.html
- Use the aws CLI tool to query non-Read-Only events as JSON
- Look through the provided events to see if any are dangerous.
- Create a wrapper script and call via cron to check cloudtrail once per minute
- Anyone that wants the complete script, please contact me at bloghelp@thewyz.net for more information.
Example CLI Call
1 2 3 |
export REGION='us-east-1' export START='2022-12-30T00:00:00Z' /usr/local/bin/aws cloudtrail lookup-events --region $REGION --lookup-attributes AttributeKey=ReadOnly,AttributeValue=false --output json --start-time $START |
Example Crontab Entry
1 |
* * * * * /Users/wyzaerd/bin/trailwatch -c 2>&1 >> /Users/wyzaerd/logs/trailwatch.log |
Example Parsing Logic (Perl)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
################################################## my @data = ( $region, $sourceIPAddress, $eventName, $accessKeyId, $userName, $eventTime ); ################################################## ## source IP rules ################################################## if ($sourceIPAddress eq $homeIPAddress) { ## HOME IP $msg = &::makeMsg('HOME',\@data); } elsif ( $sourceIPAddress eq 'ec2.amazonaws.com' or $sourceIPAddress eq 'guardduty.amazonaws.com' ) { ## AWS MGMT CALL $msg = &::makeMsg('GOOD',\@data); } elsif ( $sourceIPAddress eq 'ssm.amazonaws.com' or $sourceIPAddress eq 'workmail.amazonaws.com' ) { ## AWS MGMT CALL $msg = &::makeMsg('WARNING',\@data); ## I choose to not actually warn for these, ## so the next line is commented out: ##$warnings{$TrailEventID} = $msg; ################################################## ## source IP did NOT match any rules - check user ################################################## } else { if ($userName eq 'myBackupUser') { ## BACKUP USER "myBackupUser" $msg = &::makeMsg('BACKUP',\@data); $warnings{$TrailEventID} = $msg; ################################################## } else { my $userName = ( defined($CloudTrailEvent->{userIdentity}->{userName}) and $CloudTrailEvent->{userIdentity}->{userName} ) ? $CloudTrailEvent->{userIdentity}->{userName} : ''; $msg = &::makeMsg('BAD',\@data); $errors{$TrailEventID} = $msg; } } ## end if |
List of AWS Regions
ap-south-1
eu-north-1
eu-west-3
eu-west-2
eu-west-1
ap-northeast-3
ap-northeast-2
ap-northeast-1
ca-central-1
sa-east-1
ap-southeast-1
ap-southeast-2
eu-central-1
us-east-1
us-east-2
us-west-1
us-west-2
Leave Your Comment
All fields marked with "*" are required.