├── .gitignore ├── LICENSE.md ├── NOTES.md ├── README.md ├── check-all-vpcs.sh ├── json-sec-groups-to-dot.php └── review-security-groups.php /.gitignore: -------------------------------------------------------------------------------- 1 | all-security-groups.* 2 | describe-vpcs.txt 3 | vpc-*.json 4 | vpc-*.txt 5 | vpg-*.png 6 | vpcinfo.txt 7 | 8 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The BSD 2-Clause License 2 | 3 | https://opensource.org/licenses/BSD-2-Clause 4 | 5 | 6 | 7 | Copyright (c) 2016, Mr. Secure 8 | 9 | All rights reserved. 10 | 11 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 12 | 13 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 14 | 15 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 18 | 19 | -------------------------------------------------------------------------------- /NOTES.md: -------------------------------------------------------------------------------- 1 | # Other AWS CLI Commands 2 | 3 | ... to support gathering data for Top 20 CSCs 4 | 5 | Note: collection is kept separate from analysis so that the collected data can be used for multiple analaysis attempts. 6 | 7 | 8 | ## Collect inventory information: Volumes, Instances, Security Groups, Network Interfaces, etc. 9 | 10 | ``` 11 | aws --output json ec2 describe-volumes > volumes.json 12 | aws --output json ec2 describe-instances > instances.json 13 | aws --output json ec2 describe-security-groups > sec-groups.json 14 | aws --output json ec2 describe-network-interfaces > nics.json 15 | aws --output json rds describe-db-instances > rds.json 16 | aws --output json elb describe-load-balancers > elbs.json 17 | ``` 18 | 19 | ## List Unencrypted volumes, and the instance they're attached to 20 | 21 | ``` 22 | cat volumes.json | jq -r '.Volumes[] | select(.Encrypted == false) | {Volume: .VolumeId, Type: .VolumeType, Encryption: .Encrypted, AttachedTo: .Attachments[].InstanceId }' 23 | ``` 24 | 25 | ## List Unencrypted rds instances 26 | 27 | ``` 28 | cat rds.json | jq -r '.DBInstances[] | select(.StorageEncrypted == false) | {DBInstance: .DBInstanceIdentifier, Engine: .Engine, Encrypted: .StorageEncrypted}' 29 | ``` 30 | 31 | ## List Instances - Launch time, Platform, Instance type & ID, security groups 32 | 33 | ``` 34 | cat instances.json | jq '.Reservations[].Instances[] | [.LaunchTime, .Platform, .InstanceType, .InstanceId, .SecurityGroups[].GroupId]' 35 | ``` 36 | 37 | ## List in-use interfaces and key details 38 | 39 | ``` 40 | cat nics.json | jq -C '.NetworkInterfaces[] | select(.Status == "in-use") | {InterfaceId: .NetworkInterfaceId, AttachedTo: .Attachment.InstanceId, Owner: .Attachment.InstanceOwnerId, IP: .PrivateIpAddress, SecurityGroups: .Groups[].GroupId } ' 41 | ``` 42 | 43 | 44 | ## Security Group Info 45 | 46 | 47 | ``` 48 | cat sec-groups.json | jq -C '.SecurityGroups[] | [.GroupId, .VpcId, .IpPermissions[].ToPort, .IpPermissions[].IpRanges[].CidrIp ]' 49 | cat sec-groups.json | jq -C '.SecurityGroups[] | [.GroupId, .VpcId, .IpPermissions[].ToPort, .IpPermissions[].UserIdGroupPairs[].GroupId ]' 50 | cat sec-groups.json | jq -C '.SecurityGroups[] | {gid: .GroupId, port: .IpPermissions[].ToPort, srcgrps: .IpPermissions[].UserIdGroupPairs[].GroupId }' 51 | ``` 52 | ## Sample VPC Flow Log Data Query 53 | 54 | ``` 55 | aws ec2 describe-flow-logs 56 | aws logs filter-log-events --log-group-name 57 | ``` 58 | 59 | 60 | ## Gather IP and DNS names to measure exposed surface 61 | 62 | potentially useful to feed data into vulnerability scanners, etc. 63 | 64 | ``` 65 | cat elbs.json | jq -M '.LoadBalancerDescriptions[] | { Type: .Scheme, Name: .DNSName} ' | tr '{:' '%,' | tr -d "\n}\"" | tr '%' "\n" | sort 66 | cat instances.json | jq -M '.Reservations[].Instances[] | { ip_private: .PrivateIpAddress, ip_public: .PublicIpAddress }' | grep 'ip_' | grep -v null | tr -d '",' | sort 67 | ``` 68 | 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Security Group Review Tool 2 | 3 | ## Prerequisites 4 | 5 | * AWS CLI 6 | * with keys setup 7 | * only your default account / region will be included 8 | * http://docs.aws.amazon.com/cli/latest/userguide/installing.html 9 | 10 | * GraphViz 11 | * http://graphviz.org/Download.php 12 | 13 | * PHP 5.x should suffice 14 | 15 | 16 | ## Usage 17 | 18 | * clone this repo somewhere 19 | * cd to a directory where you want the data dumped 20 | * call the 'check-all-vpcs.sh' script 21 | 22 | ```bash 23 | mkdir /home/mrsecure/git/ 24 | cd /home/mrsecure/git 25 | git clone https://github.com/MrSecure/review-security-groups.git 26 | mkdir /home/mrsecure/data 27 | cd /home/mrsecure/data 28 | /home/mrsecure/git/review-security-groups/check-all-vpcs.sh 29 | ``` 30 | 31 | 32 | 33 | ### Outputs 34 | 35 | * File endings 36 | * json - raw data from aws cli 37 | * txt - greppable / parsable rules, one per line 38 | * dot - graphviz dot formatted file; nodes are SGs/CIDRs, edges are allowed proto/ports 39 | * png - circo output of dot file 40 | * summary.txt - security group id, # in & out rules, description 41 | 42 | * File Groupings 43 | * vpc-XXXXXX - per-VPC data 44 | * all-security-groups.* - collection of all securiy groups accessible with the AWS keys used 45 | 46 | ```bash 47 | [mrsecure@kirchhoff]$ /home/mrsecure/git/review-security-groups/check-all-vpcs.sh 48 | vpc-12312312 49 | vpc-abc4abc4 50 | vpc-abcdabcd 51 | vpc-0a0b0c0d 52 | vpc-d7d7d7d7 53 | vpc-dbdbdbdb 54 | [mrsecure@kirchhoff]$ ls -l 55 | total 9120 56 | -rw-r--r-- 1 mrsecure hackers 23317 Jan 26 12:15 all-security-groups.dot 57 | -rw-r--r-- 1 mrsecure hackers 191018 Jan 26 12:15 all-security-groups.json 58 | -rw-r--r-- 1 mrsecure hackers 6046 Jan 26 12:15 all-security-groups.summary.txt 59 | -rw-r--r-- 1 mrsecure hackers 18350 Jan 26 12:15 all-security-groups.txt 60 | -rw-r--r-- 1 mrsecure hackers 66947 Jan 26 12:15 vpc-abc4abc4-sgs.json 61 | -rw-r--r-- 1 mrsecure hackers 6258 Jan 26 12:15 vpc-abc4abc4-sgs.txt 62 | -rw-r--r-- 1 mrsecure hackers 1298147 Jan 26 12:15 vpc-abc4abc4.png 63 | -rw-r--r-- 1 mrsecure hackers 77278 Jan 26 12:15 vpc-0a0b0c0d-sgs.json 64 | -rw-r--r-- 1 mrsecure hackers 7505 Jan 26 12:15 vpc-0a0b0c0d-sgs.txt 65 | -rw-r--r-- 1 mrsecure hackers 2300886 Jan 26 12:15 vpc-0a0b0c0d.png 66 | -rw-r--r-- 1 mrsecure hackers 3737 Jan 26 12:15 vpc-d7d7d7d7-sgs.json 67 | -rw-r--r-- 1 mrsecure hackers 368 Jan 26 12:15 vpc-d7d7d7d7-sgs.txt 68 | -rw-r--r-- 1 mrsecure hackers 44763 Jan 26 12:15 vpc-d7d7d7d7.png 69 | -rw-r--r-- 1 mrsecure hackers 4199 Jan 26 12:15 vpc-12312312-sgs.json 70 | -rw-r--r-- 1 mrsecure hackers 479 Jan 26 12:15 vpc-12312312-sgs.txt 71 | -rw-r--r-- 1 mrsecure hackers 58184 Jan 26 12:15 vpc-12312312.png 72 | -rw-r--r-- 1 mrsecure hackers 35296 Jan 26 12:15 vpc-dbdbdbdb-sgs.json 73 | -rw-r--r-- 1 mrsecure hackers 3373 Jan 26 12:15 vpc-dbdbdbdb-sgs.txt 74 | -rw-r--r-- 1 mrsecure hackers 430248 Jan 26 12:15 vpc-dbdbdbdb.png 75 | -rw-r--r-- 1 mrsecure hackers 3721 Jan 26 12:15 vpc-abcdabcd-sgs.json 76 | -rw-r--r-- 1 mrsecure hackers 367 Jan 26 12:15 vpc-abcdabcd-sgs.txt 77 | -rw-r--r-- 1 mrsecure hackers 45070 Jan 26 12:15 vpc-abcdabcd.png 78 | [mrsecure@kirchhoff]$ 79 | ``` 80 | -------------------------------------------------------------------------------- /check-all-vpcs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | WORK=$(pwd) 3 | BASE=$(dirname "$0") 4 | DATE=$(date -u "+%Y-%m-%d %T %Z") 5 | FORMAT='png' 6 | 7 | export LC_ALL=C 8 | 9 | ## Verify aws cli tool is ready, keys active 10 | if ! aws --output json sts get-caller-identity > /dev/null ; then 11 | echo "Unable to query user data via AWS CLI tool; unable to continue." 12 | exit 1 13 | fi 14 | 15 | ## List all VPCs, and grab network and name 16 | aws ec2 describe-vpcs --output json | \ 17 | jq -r '.Vpcs[] | .VpcId +" "+ .CidrBlock +" "+ (.Tags[] | select(.Key == "Name") | .Value)' > "${WORK}/vpcinfo.txt" 18 | VPCS=$(cut -d' ' -f1 < "${WORK}/vpcinfo.txt" ) 19 | 20 | for v in $VPCS ; do { 21 | echo "$v"; 22 | EXTRA=$(grep "$v" "${WORK}/vpcinfo.txt") 23 | # Collect SG info in json format 24 | aws ec2 describe-security-groups --filter "Name=vpc-id,Values=${v}" --output json > "${WORK}/${v}-sgs.json" 25 | 26 | # Generate a PNG drawing 27 | php "${BASE}/json-sec-groups-to-dot.php" < "${WORK}/${v}-sgs.json" | circo -T${FORMAT} -Glabel="Security Groups ${EXTRA} \n ${DATE}" -o "${WORK}/${v}.png" 28 | 29 | # Generate a short format repor 30 | php "${BASE}/review-security-groups.php" < "${WORK}/${v}-sgs.json" > "${WORK}/${v}-sgs.txt" 31 | } 32 | done 33 | 34 | # Add capture timestamp to summary file 35 | echo "### -------------- ${DATE}" > "${WORK}/all-security-groups.summary.txt" 36 | 37 | aws ec2 describe-security-groups --output json > "${WORK}/all-security-groups.json" 38 | php "${BASE}/review-security-groups.php" < "${WORK}/all-security-groups.json" | sort > "${WORK}/all-security-groups.txt" 39 | php "${BASE}/json-sec-groups-to-dot.php" < "${WORK}/all-security-groups.json" > "${WORK}/all-security-groups.dot" 40 | grep -e '###' "${WORK}/all-security-groups.txt" >> "${WORK}/all-security-groups.summary.txt" 41 | -------------------------------------------------------------------------------- /json-sec-groups-to-dot.php: -------------------------------------------------------------------------------- 1 | $sg [ label ="$proto / $ports" ] 84 | // $extras = 'decorate=true '; 85 | $extras = ''; 86 | foreach ($nodes as $n) { 87 | if ('out' == $dir) { 88 | $src = $sg; 89 | $dst = $n; 90 | $extras .= 'color="red" fontcolor="red"'; 91 | } else { 92 | $dst = $sg; 93 | $src = $n; 94 | } 95 | $rules .= sprintf(" \"%s\" -> \"%s\" [ label = \"%s / %s\" %s];\n", $src, $dst, $proto, $ports, $extras); 96 | 97 | } 98 | 99 | 100 | } 101 | 102 | return $rules; 103 | } 104 | -------------------------------------------------------------------------------- /review-security-groups.php: -------------------------------------------------------------------------------- 1 | $out\n"; 35 | } 36 | 37 | 38 | } 39 | 40 | // -------------------------------------------------------------------- 41 | 42 | function getRuleText($node) { 43 | if (! is_array($node)) { 44 | return "INVALID"; 45 | } 46 | $rules = array(); 47 | 48 | foreach ($node as $item) { 49 | switch($item['IpProtocol']) { 50 | case '6': $proto = 'TCP'; break; 51 | case '17': $proto = 'UDP'; break; 52 | case '-1': $proto = 'ANY'; break; 53 | default: 54 | $proto = $item['IpProtocol']; 55 | break; 56 | } 57 | 58 | $nets = array(); 59 | foreach ($item['IpRanges'] as $cidr) { 60 | $nets[] = $cidr['CidrIp']; 61 | } 62 | 63 | $ugids = array(); 64 | foreach ($item['UserIdGroupPairs'] as $ugp) { 65 | $ugids[] = $ugp['GroupId']; 66 | } 67 | 68 | // merge the set of sources, and sort them for stability 69 | $sources = array_merge($nets, $ugids); 70 | natsort($sources); 71 | 72 | $ports = ''; 73 | if (array_key_exists('FromPort', $item) && 74 | array_key_exists('ToPort', $item)) { 75 | if ($item['ToPort'] == $item['FromPort']) { 76 | $ports = $item['FromPort']; 77 | } else { 78 | $ports = $item['FromPort'] .'-'. $item['ToPort']; 79 | } 80 | } elseif ('ANY' == $proto){ 81 | $ports = 'ANY'; 82 | } 83 | $sourceList = implode(' ', $sources); 84 | $rules[] = "$proto $ports $sourceList"; 85 | } 86 | 87 | return $rules; 88 | } 89 | --------------------------------------------------------------------------------