-
-
Save TweekFawkes/554474e78fe24cfe62c4b8b55408bc05 to your computer and use it in GitHub Desktop.
YupPhrasing - PortKnocking via AWS APIs - Alpha v0.0.2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
import boto3 | |
import os | |
from argparse import ArgumentParser | |
import requests | |
import time | |
import logging | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
__author__ = '@TweekFawkes' | |
__website__ = 'Stage2Sec.com' | |
__blog__ = 'https://Stage2Sec.com/blog/2017/11/12/portknocking-via-aws-apis' | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
''' | |
--- YupPhrasing - PortKnocking via AWS APIs - Alpha v0.0.2 --- | |
YupPhrasing will add your ip address to the security group of an AWS EC2 instance so you can SSH into the instance from your current location. | |
YupPhrasing is kind of like PortKnocking for AWS but it leverages AWS's APIs to add firewall rules on the fly. | |
YupPhrasing is mostly designed as a proof of concept but could also be used to streamline accessing/debugging AWS instances. | |
--- Example Usage --- | |
... look at the help ... | |
$ yupPhrasing.py -h | |
... set your AWS API keys so yupPhrasing can change the security group rules ... | |
$ export AWS_ACCESS_KEY_ID="REDACTED" | |
$ export AWS_SECRET_ACCESS_KEY="REDACTED" | |
$ printenv | grep "AWS_" | |
... yupPhrasing will find your current external ip address and then leverage AWS APIs to open tcp port 22 for this external ip address for the instance assoicated with 54.183.78.154 ... | |
$ ./yupPhrasing-v0_0_2.py Tweek 35.167.178.36 | |
root : CRITICAL | |
__ __ ____ _ _ | |
\ \ / / _ _ __ | _ \| |__ _ __ __ _ ___(_)_ __ __ _ | |
\ V / | | | '_ \| |_) | '_ \| '__/ _` / __| | '_ \ / _` | | |
| || |_| | |_) | __/| | | | | | (_| \__ \ | | | | (_| | | |
|_| \__,_| .__/|_| |_| |_|_| \__,_|___/_|_| |_|\__, | | |
|_| |___/ | |
root : CRITICAL Alpha v0.0.2 | |
root : CRITICAL [+] Deleting Unused Security Group: yupPhrasing_Tweek_1510493232.49 | |
root : CRITICAL [+] IP Address WhiteListed by Adding a Security Group to the Instance! | |
root : CRITICAL [+] CIDR Added: 104.154.74.221/32 | |
... yupPhrasing will leverage AWS APIs to allow 34.209.82.230 on tcp port 22 to access the SSH service hosted on the 54.183.78.154 instance ... | |
$ yupPhrasing.py -m 34.209.82.230 Tweek001 54.183.78.154 | |
--- Setup on Ubuntu 14.04 --- | |
$ apt-get update | |
... | |
$ apt-get -y install python | |
... | |
$ apt-get -y install python-pip | |
... | |
$ pip install boto3 | |
... | |
$ python | |
Python 2.7.6 (default, Oct 26 2016, 20:30:19) | |
... | |
>>> exit() | |
--- References --- | |
- https://martin-thoma.com/how-to-parse-command-line-arguments-in-python/ | |
- https://stackoverflow.com/questions/39352349/boto3-attach-detach-security-group-from-ec2-instance | |
- http://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.Client.create_security_group | |
- https://docs.python.org/2/library/logging.html | |
- http://serverfault.com/questions/729457/python-boto3-allow-ingress-security-groups | |
''' | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
# User Defined Variables | |
sKeyWord = 'yupPhrasing' | |
#os.environ["AWS_ACCESS_KEY_ID"] = 'REDACTED' # INSECURE # It is better to set these as environment variables using the export command, for example: export AWS_ACCESS_KEY_ID="REDACTED" | |
#os.environ["AWS_SECRET_ACCESS_KEY"] = 'REDACTED' # INSECURE # It is better to set these as environment variables using the export command, for example: export AWS_SECRET_ACCESS_KEY="REDACTED" | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
# Automatically Defined Variables | |
sScriptName = os.path.basename(__file__) | |
sLogDirectory = 'logs' | |
sVersion = 'Alpha v0.0.2' | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
# Get the Arguments | |
parser = ArgumentParser(add_help=True) | |
parser.add_argument('user', | |
action="store", | |
help="[required] unique user id of the operator") | |
parser.add_argument('ip', | |
action="store", | |
help="[required] public ip address of the server") | |
parser.add_argument("-m", "--myip", | |
action="store", dest="sMyIp", default="unknown", | |
help="the ip to add to the security group") | |
parser.add_argument("-v", "--verbose", | |
action="store_false", dest="fVerbose", default=False, | |
help="print verbose status messages to stdout") | |
parser.add_argument("-d", "--debug", | |
action="store_false", dest="fDebug", default=False, | |
help="save debug status messages to a file") | |
args = parser.parse_args() | |
#parser.add_argument('count', action="store", type=int) | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
# Start Logging | |
if not os.path.exists(sLogDirectory): | |
os.makedirs(sLogDirectory) | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
# Logging to a file | |
if args.fDebug is True: | |
logging.basicConfig(level=logging.DEBUG, | |
format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', | |
datefmt='%m-%d %H:%M', | |
filename=sLogDirectory+"/"+str(int(time.time()))+'-'+sScriptName+'.log', | |
filemode='w') | |
elif args.fDebug is False: | |
logging.basicConfig(level=logging.INFO, | |
format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', | |
datefmt='%m-%d %H:%M', | |
filename=sLogDirectory+"/"+str(int(time.time()))+'-'+sScriptName+'.log', | |
filemode='w') | |
logger = logging.getLogger(__name__) | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
# Logging to a stdout | |
console = logging.StreamHandler() | |
if args.fVerbose is True: | |
console.setLevel(logging.DEBUG) | |
elif args.fVerbose is False: | |
console.setLevel(logging.WARNING) | |
#console.setLevel(logging.INFO) | |
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') | |
console.setFormatter(formatter) | |
logging.getLogger('').addHandler(console) | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
''' | |
CRITICAL 50 | |
ERROR 40 | |
WARNING 30 | |
INFO 20 | |
DEBUG 10 | |
NOTSET 0 | |
logging.debug("DEBUG") | |
logging.info("INFO") | |
logging.warning("WARNING") | |
logging.error("ERROR") | |
logging.critical("CRITICAL") | |
''' | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
sIp = str(args.ip).strip() | |
logging.debug("sIp: " + sIp) | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
sUnique = str(args.user).strip() | |
logging.debug("sUnique: " + sUnique) | |
sKeyWord = sKeyWord + '_' + sUnique | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
sMyIp = str(args.sMyIp).strip() | |
if sMyIp == "unknown": | |
headers = {'User-Agent': 'curl/7.37.0'} | |
r = requests.get('https://ipcurl.net', headers=headers) | |
sMyIp = str(r.text).strip() | |
else: | |
sMyIp = str(args.sMyIp).strip() | |
logging.debug('sMyIp: ' + str(sMyIp)) | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
logging.critical(""" | |
__ __ ____ _ _ | |
\ \ / / _ _ __ | _ \| |__ _ __ __ _ ___(_)_ __ __ _ | |
\ V / | | | '_ \| |_) | '_ \| '__/ _` / __| | '_ \ / _` | | |
| || |_| | |_) | __/| | | | | | (_| \__ \ | | | | (_| | | |
|_| \__,_| .__/|_| |_| |_|_| \__,_|___/_|_| |_|\__, | | |
|_| |___/ | |
""") | |
logging.critical(sVersion) | |
# --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # --- # | |
lAwsRegionsEc2 = ['us-east-1', 'us-east-2', 'us-west-1', 'us-west-2', 'ca-central-1', 'eu-west-2', 'eu-central-1', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-2', 'ap-northeast-1', 'ap-south-1', 'sa-east-1'] | |
for sAwsRegionsEc2 in lAwsRegionsEc2: | |
sAwsRegionsEc2 = sAwsRegionsEc2.strip() | |
logging.debug('sAwsRegionsEc2: ' + sAwsRegionsEc2) | |
# ### # | |
# Remove unused YP SecGroups | |
ec2client = boto3.client("ec2", region_name=sAwsRegionsEc2) | |
# In boto3, if you have more than 1000 entries, you need to handle the pagination | |
# using the NextToken parameter, which is not shown here. | |
all_instances = ec2client.describe_instances() | |
all_sg = ec2client.describe_security_groups() | |
instance_sg_set = set() | |
sg_set = set() | |
for reservation in all_instances["Reservations"] : | |
for instance in reservation["Instances"]: | |
for sg in instance["SecurityGroups"]: | |
instance_sg_set.add(sg["GroupName"]) | |
for security_group in all_sg["SecurityGroups"] : | |
sg_set.add(security_group ["GroupName"]) | |
idle_sg = sg_set - instance_sg_set | |
logging.debug("idle_sg: " + str(idle_sg)) | |
for sIdleSecGroupName in idle_sg: | |
if sKeyWord in sIdleSecGroupName: | |
logging.critical('[+] Deleting Unused Security Group: ' + sIdleSecGroupName) | |
ec2client.delete_security_group(GroupName=sIdleSecGroupName) | |
# ### # | |
# Find Instance that Matches the IP, remove existing YP secgroups, and add a new one | |
ec2resource = boto3.resource('ec2', region_name=sAwsRegionsEc2) | |
for i in ec2resource.instances.all(): | |
if i.public_ip_address == sIp: | |
logging.debug('MATCH!') | |
# | |
timeOfRun_Str = str(time.time()) | |
# | |
secGroupName_Str = sKeyWord+'_'+timeOfRun_Str | |
secGroupDesc_Str = sKeyWord+'_'+timeOfRun_Str | |
# | |
mysg = ec2resource.create_security_group(GroupName=secGroupName_Str,Description=secGroupDesc_Str) | |
sCidrIp = sMyIp + '/32' | |
mysg.authorize_ingress(IpProtocol="tcp", CidrIp=sCidrIp, FromPort=22, ToPort=22) # http://serverfault.com/questions/729457/python-boto3-allow-ingress-security-groups | |
# | |
logging.debug("mysg: " + str(mysg)) | |
logging.debug("mysg.id: " + str(mysg.id)) | |
# | |
logging.debug(i.id, i.instance_type) | |
lAllNormalSecGroups = [] | |
lAllYupSecGroups = [] | |
# | |
for sg in i.security_groups: | |
logging.debug("sg: " + str(sg)) | |
logging.debug("sg['GroupId']: " + str(sg['GroupId'])) | |
logging.debug("sg['GroupName']: " + str(sg['GroupName'])) | |
if sKeyWord in sg['GroupName']: | |
lAllYupSecGroups.append(sg['GroupId']) | |
else: | |
lAllNormalSecGroups.append(sg['GroupId']) | |
# | |
logging.debug("lAllYupSecGroups: " + str(lAllYupSecGroups)) | |
logging.debug("lAllNormalSecGroups: " + str(lAllNormalSecGroups)) | |
lAllNormalSecGroups.append(mysg.id) | |
i.modify_attribute(Groups=lAllNormalSecGroups) | |
# | |
all_sg_ids = [sg['GroupId'] for sg in i.security_groups] # Get a list of ids of all securify groups attached to the instance | |
logging.debug("all_sg_ids: " + str(all_sg_ids)) | |
# | |
if mysg.id in all_sg_ids: | |
logging.critical("[+] IP Address WhiteListed by Adding a Security Group to the Instance!") | |
logging.critical("[+] CIDR Added: " + sCidrIp) | |
exit() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment