Skip to content

Instantly share code, notes, and snippets.

@TweekFawkes
Created November 12, 2017 13:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save TweekFawkes/554474e78fe24cfe62c4b8b55408bc05 to your computer and use it in GitHub Desktop.
Save TweekFawkes/554474e78fe24cfe62c4b8b55408bc05 to your computer and use it in GitHub Desktop.
YupPhrasing - PortKnocking via AWS APIs - Alpha v0.0.2
#!/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