Showing posts with label python. Show all posts
Showing posts with label python. Show all posts

Friday, September 5, 2014

Sniffing BitTorrent DHT Traffic

Introduction

I've been playing with some of the protocols that power BitTorrent recently just for my own knowledge. While digging into the Distributed Hash Table, I decided to whip up a quick packet sniffer to decode the queries and responses. This gives a quick insight into how your client is interacting with the nodes around it.

The Source

Joining the Swarm

The code is available on github. The default monitoring port is 51413 (default for transmission). Consult your client's documentation or use lsof to find the listening port.

$ lsof -i | grep UDP
transmiss   999 debian-transmission   12u  IPv4 16474843      0t0  UDP *:51413
$ sudo python dht_sniff.py 51413
127.0.0.1:51413 -> 127.0.0.1:6969 (94 bytes): {'a': {'id': '\xab/Da\xcd\x7f\xbcI\xef[E\\\x88m6\xae\xab\xbd<\xd6', 'target': "\x12\x34\\'\xab5\xfbGj\x96M\x15\xce\xad\x91@\xb9' E"}, 'q': 'find_node', 't': 'fn\x00\x00', 'y': 'q'}

Going Beyond

I didn't implement it yet, but decoding the node list returned by find_node and get_peers is relatively straight forward. This would give an even more in depth look at how your client / nodes around you are communicating. Refer to the documentation above for how node lists are constructed and returned.

Thursday, January 16, 2014

Flaskgur: Simple Imgur clone with Flask and Python

Introduction

As part of a previous post I wrote about using PyTinyDNS to dynamically assign DNS subdomains with updated IP addresses every time a user connects. Another service that we were missing was a simple image sharing site for use exclusively on the VPN. I've been intending to play with Flask for a while now this seemed like a good excuse to dive in.

Enter Flask

Flask is a microframework for Python based on Werkzeug and Jinja2. This combination of tools allows rapid development of web applications through Jinja's modern extensible template language and Werkzeug's URL routing modules.

Setup Flaskgur

If this is your first time playing with flask, go ahead grab virtualenv. Flask will be installed later.

$ sudo pip install virtualenv
$ 

Now grab a copy of Flaskgur and setup the new virtual environment.

$ git clone https://github.com/chokepoint/flaskgur.git
$ cd flaskgur
$ virtualenv venv
$ . venv/bin/activate
$ pip install Flask

Flaskgur relies on a small sqlite database with an id field and filename field. The git repository comes with a copy of the database schema.

$ sqlite3 flaskgur.db < schema.sql

Now edit flaskgur.py and point the base directory to your desired location and start Flaskgur.

$ python flaskgur.py 
 * Running on http://0.0.0.0:5000/
 * Restarting with reloader

Flaskgur Layout

The directories are broken down into the following list.

  • pics - Where the uploaded pictures are saved
  • static - Static formatting files (css) and custom 404 image
  • templates - Jinja2 formatted template files

For simplicity, the custom 404 page doubles as error page for bad file types. base.html is the base template, and data is filled in respectively by upload.html and 404.html thanks to Jinja. I included a simple macro in upload.html as well for a basic example.

Going Beyond

This is not a complete product by any means, and I do not profess to be a CSS guru. If you'd like to clean up some of the formatting, pull requests are always welcome. The database could easily be upgraded to include a description of photos or incorporate a ranking system. If you're interested in extending this project, head on over to github and add whatever you feel is necessary.

Wednesday, January 8, 2014

More fun with PAM-Python -- Logging Failed SSH Passwords

Introduction

Following up from my last post about a two-factor SSH authentication python module, I thought I'd add one more module that has yielded some interesting results as well. If you watch your authorization log closely you'll undoubtedly notice multiple failed login attempts per day primarily for root/privileged users. These scans are largely automated by using lists of popular passwords and SSH cracking programs such as Beleth. Typical authorization logs from SSHd look similar to the entry below.

Jan  1 18:10:51 lan sshd[19972]: Failed password for root from 61.xxx.xxx.34 port 33302 ssh2

I thought it would be interesting to see not only which users were commonly targets of attack, but also the wordlists being actively used by these bots. The module below will log a copy of the attempted username and password for all failed login attempts. I've also included a copy of all failed attempts logged after running this module for a week. The log entry below is how auth.log will look while using the module.

Jan  1 19:58:39 lan sshd: SSH Attack Logged: Remote Host: 61.xxx.xxx.34 (root:password1)

The Source

import crypt, spwd, syslog

def auth_log(msg):
 """Send errors to default auth log"""
 syslog.openlog(facility=syslog.LOG_AUTH)
 syslog.syslog("SSH Attack Logged: " + msg)
 syslog.closelog()

def check_pw(user, password):
 """Check the password matches local unix password on file"""
 hashed_pw = spwd.getspnam(user)[1]
 
 return crypt.crypt(password, hashed_pw) == hashed_pw

def pam_sm_authenticate(pamh, flags, argv):
 try:
  user = pamh.get_user()
 except pamh.exception, e:
  return e.pam_result
 
 if not user:
  return pamh.PAM_USER_UNKNOWN
  
 try:
  resp = pamh.conversation(pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, 'Password:'))
 except pamh.exception, e:
  return e.pam_result
  
 if not check_pw(user, resp.resp):
  auth_log("Remote Host: %s (%s:%s)" % (pamh.rhost, user, resp.resp))
  return pamh.PAM_AUTH_ERR
 
 return pamh.PAM_SUCCESS

def pam_sm_setcred(pamh, flags, argv):
 return pamh.PAM_SUCCESS

def pam_sm_acct_mgmt(pamh, flags, argv):
 return pamh.PAM_SUCCESS

def pam_sm_open_session(pamh, flags, argv):
 return pamh.PAM_SUCCESS

def pam_sm_close_session(pamh, flags, argv):
 return pamh.PAM_SUCCESS

def pam_sm_chauthtok(pamh, flags, argv):
 return pamh.PAM_SUCCESS

Configuration

Configuration is similar to the STAMP 2-factor authentication module. Instead of adding an additional authentication requirement, we're simply going to replace the standard password entry in /etc/pam.d/sshd with our new module. Save a copy of the source code to /lib/security/pwreveal.py. Now, open up /etc/pam.d/sshd and insert the line below.

#@include common-auth
auth       requisite     pam_python.so pwreveal.py

Resources

You can run similar experiments by using any number of widely available honeypot projects. Kippo is an SSH based honeypot that includes not only password logging, but also places attackers in a sandboxed shell that logs all of their commands. If you don't want to deal directly with the kippo software, it's included as part of Honeydrive, a full featured VM honeypot.

Collected wordlist

Monday, December 30, 2013

Simple SSH 2-Factor Authentication Module

Introduction

I needed a quick 2-factor authentication module for SSH. Instead of going with one of the popular solutions like Duo or Google Authenticator, it seemed like a good excuse to whip up some code. I've written small PAM modules in the past using C, but I've been on a python kick lately so I turned to PAM-Python. The module, we'll call it SSH Two-factor Authentication Module in Python (STAMP to make it catchy), is available over on github

How it Works

STAMP works by generating a one time use personal identification number for each login attempt. The module then looks up the local user's cell phone number, which we'll be storing in the standard Office Phone slot in each pw entry in /etc/passwd. Once it has the user's phone number the module sends the one time use PIN to the user. Instead of storing credentials for a service like Google Voice, I went with one of the first free sites I found, TxtDrop. The source includes a small class for dealing with the TxtDrop SMS form and works with most US carriers that I tried out. Once the correct PIN is entered, the login procedure continues with normal password based authentication.

Setting up the Module

Ensure the following dependencies are already installed on your system.

  • pam-python
  • python-requests

Grab the source and copy stampauth.py to /lib/security

$ git clone https://github.com/chokepoint/stampauth.git
$ cd stampauth
$ sudo cp stampauth.py /lib/security/

Now that the module is in place, we need to configure SSHd to enable Challenge/Response Authentication. In /etc/ssh/sshd_config uncomment the following line.

ChallengeResponseAuthentication yes

We also need to let PAM know the order in which we should process the authentication. I set it up so that the user is first prompted for the one time PIN before being prompted for the password. If you choose to go this route, then in /etc/pam.d/sshd locate the section marked with "@include common-auth" and make it look like the entry below.

auth       requisite     pam_python.so stampauth.py
@include common-auth

You can set a user's Office Phone number with the following command.

$ sudo usermod stderr -c ',,555-555-5555,'

Finally, restart sshd and test it out.

$ sudo service ssh restart
$ ssh stderr@localhost
Enter one time PIN: 
Password:
Welcome!

Disclaimer

An attacker could potentially lock you out of your system by repeatedly connecting to your SSH server and failing the PIN test. This occurs because TxtDrop limits the number of SMSes sent by your IP. Feel free to switch to a different SMS gateway.

Saturday, October 12, 2013

Dynamic Subdomains with OpenVPN and PyTinyDNS

Introduction

As I have covered in a previous post about PyTinyDNS there are multiple uses for a dynamic DNS service like this. One of my side projects is hosting a private Virtual Private Network (VPN). Along with hosting my own TLDs, I also wanted to have a custom solution for dynamically assigning a subdomain to each individual client that connects to the network. This way if the user happens to get assigned a different IP, others on the net will be able to easily connect to their services without any issues.

Redis

PyTinyDNS uses redis to make dynamic additions of domains and subdomains possible without adding extra config files or restarting the daemon. PyTinyDNS also comes with an import script that allows you to add either a text file full of domains or individual domains via the command line arguments. We'll strip the functionality from the import tool and use it to assign dynamic subdomains through OpenVPN's scripting features.

OpenVPN

OpenVPN comes with built-in options to add custom scripts that are triggered by multiple events. The following are example events that you can configure in order to take advantage of endless custom solutions.

  • --up (Executed after TCP/UDP socket bind and TUN/TAP open.)
  • --tls-verify (Executed when we have a still untrusted remote peer.)
  • --ipchange (Executed after connection authentication, or remote IP address change.)
  • --client-connect (Executed in --mode server mode immediately after client authentication.)
  • --route-up (Executed after connection authentication, either immediately after, or some number of seconds after as defined by the --route-delay option.)
  • --client-disconnect (Executed in --mode server mode on client instance shutdown.)
  • --down (Executed after TCP/UDP and TUN/TAP close.)
  • --learn-address (Executed in --mode server mode whenever an IPv4 address/route or MAC address is added to OpenVPN's internal routing table.)
  • --auth-user-pass-verify (Executed in --mode server mode on new client connections, when the client is still untrusted.)

The only event that we need to watch in order to add custom A PTRs to PyTinyDNS is --client-connect. Go ahead and create a directory to store the custom script in.

$ mkdir /etc/openvpn/scripts

Now at the bottom of your OpenVPN server.conf, add the following lines.

script-security 2
client-connect '/usr/bin/python /etc/openvpn/scripts/connect.py'

The Script

The following is a quick Python script used to add the correct subdomain based on the user's common name used in the client's certificate.

#!/usr/bin/python
import redis
import os
 
def insert_record(domain, ip, redis_server):
 r_server = redis.Redis(redis_server)

 try:
  r_server.hset('pytinydns.domains', domain, ip) 
 except:
  pass

def main():
 redis_server = 'localhost'

 try:
  insert_record(os.environ['common_name'] + ".myvpn.net.",os.environ['ifconfig_pool_remote_ip'],redis_server) 
 except:
  pass

 return 0

if __name__ == '__main__':
 main()

The reason for the pass statements is that we must return the value 0 or OpenVPN will deny the client entry. If records are not being added, check to make sure that the server is running and that it is in fact running on localhost.

Now you need to restart OpenVPN in order for the changes to server.conf to go into effect.

$ sudo service openvpn restart

Considerations

Using the default configs with PyTinyDNS, non locally resolved domains are forwarded to the system's default DNS server. In order to avoid information leakage of local common names, you could implement a method to not forward any requests with a particular domain name or disable this option completely by setting the PyTinyDNS option "Resolve_Nonmatch" to no.

Friday, September 13, 2013

Linux Kernel Structure Definition Lookup Script

Introduction

If you've ever written anything kernel side for Linux, I'm sure you've bashed your head on the keyboard as many times as I have looking through lackluster documentation and scouring source files to find structure definitions. Here's a little script that will show you the source file and line number of the given structure definition.

First of all, this script relies on a fantastic website that allows you to easily search through the Linux source files http://lxr.linux.no. I simply wrote a script to parse the output to show structure definition locations. Change the URL in the script depending on your current kernel version number.

Example

$ ./structure.py crypto_tfm
[-] Searching for all structure definitions of: crypto_tfm
[+] drivers/staging/rtl8192e/rtl8192e/rtl_crypto.h, line 186
[+] drivers/staging/rtl8192u/ieee80211/rtl_crypto.h, line 189
[+] include/linux/crypto.h, line 413

The Source

#!/usr/bin/python
# Linux Kernel Structure Search

import sys
from BeautifulSoup import BeautifulSoup
import requests

def main(argv):
 struct_search = "http://lxr.linux.no/linux+v3.11/+code=" + argv[0]
 in_struct = 0
 
 print "[-] Searching for all structure definitions of: " + argv[0]
 
 req = requests.get(struct_search)
 soup = BeautifulSoup(req.text)

 spanTag = soup.findAll('span')
 for tag in spanTag:
  try: 
   myclass = tag['class']
   if myclass == 'identtype':
    if tag.string == "Structure":
     in_struct = 1
    elif in_struct:
     break
  
   if myclass == "resultline" and in_struct:
    aTag = tag.find('a')
    print "[+] " + aTag.text
  except: 
   ohnoez=1

 if not in_struct:
  print "[-] No Structures Found"
  
if __name__ == "__main__":
 main(sys.argv[1:])

Wednesday, August 21, 2013

PyTinyDNS Part 2

Introduction

    In the last post, I wrote about the PyTinyDNS project that I had been working on for my VPN setup. PyTinyDNS is a small DNS A record resolver that runs on your standard DNS port (53). Since then I've added some more features that I feel make this an even more powerful and versatile tool. The github repository is being regularly updated as I push out new changes. Please feel free to add any issues or tweaks that you see fit.

Recent Updates

    Since the original push to github, the following features have been added or changed.
  • Flat host configuration files are still in use, but its been moved to a .host file.
  • Config files can now be used instead of specifying each option in the command line.
  • Redis-server is now used as a database storage for A records.
  • Added redis_import.py in order to import A records into a live instance. Updated domain results take effect immediately
  • Redis import tool can now handle single host updates using -u domain:ip

Example Config File

[PyTinyDNS]
DefaultIP = 192.168.1.99
Use_Redis = yes
Redis_Server = localhost
#Host_File = pytinydns.host

    If Use_Redis is set to no, it'll resolve all requests with the DefaultIP or refer to Host_File (if set) in order to resolve the A records.

Possible Uses

  • Resolve internal domains
  • DNS Spoofing
  • Fast Flux implementation
  • Malware analysis

To Do List

  • Add option to resolve real IPs of non matched domains
  • Add time based IP rotation
  • Add option to reply with different IPs based on conditions being met
  • Add wildcard handling for domains
  • Add more than A record resolution

Source

Monday, August 19, 2013

PyTinyDNS -- Simple Python A Record Resolver

The Backstory

Recently I have been working on a side project; setting up OpenVPN for friends and family as a simple means of reasonably secure file sharing, media streaming, chatting, and e-mails. Not everyone that will be connected to the VPN is that technically savvy, so I'm trying to make things as easily accessible as possible. One way to make it easier to navigate would be an internal DNS server. At this point, I'm too lazy to setup and configure BIND just so that I can resolve a handful of local subdomains. 

Time for Python

After doing a little bit of searching, I found the following code. It's a small (under 50 lines of code) A record DNS resolver in Python with sockets being the only module requirement. The script resolves ALL domains to a single IP address for malware analysis purposes. This worked out perfectly for what I needed, so I decided to add a config file to make adding more than one A record a breeze. PyTinyDNS now accepts a default IP as an argument in case the domain is not specifically defined in the config. 

Example Config File

# Comment
google.com.:192.168.1.2
yahoo.com.:192.168.1.3

The Code

You can grab a copy of the code and an example config file on github
git clone https://github.com/chokepoint/pytinydns
I'll be adding extra features as I get bored, but for now this meets my current requirements for the VPN.

Update

I've added redis DB support, so that the DNS records can be updated live. Still supports flat file config, if you don't plan in setting up redis-server.

Friday, August 9, 2013

Facebook Picture to ID

Introduction

Its recently come to my attention that most people assume Facebook pictures can't be tied directly back to the owner given only a direct link to the picture. Well, this little utility was written to show just how easy it could be to use Facebook image links posted in IRC or elsewhere to track down the original owner of the content, and people tagged in the photo.


Parsing the URL


Take the following image link that I pulled from a random Google image search.
https://sphotos-b-ord.xx.fbcdn.net/hphotos-frc1/p480x480/1044981_547409191984903_1858736086_n.jpg
The main portion of the URL that we're interested in is the filename of the JPEG.
1044981_547409191984903_1858736086_n.jpg
The middle number is the FBID of the original photo album. This can be accessed by modifying a simple GET request to Facebook.
https://www.facebook.com/photo.php?fbid=547409191984903
From here it's as simple as parsing out the data that we want from the HTML. For this I decided to use BeautifulSoup and Python for simplicity and readability sake.

Example


# ./fbpic2id.py https://sphotos-b-ord.xx.fbcdn.net/hphotos-frc1/p480x480/1044981_547409191984903_1858736086_n.jpg
 
Album URL: http://www.facebook.com/photo.php?fbid=547409191984903

Owner (Page/Group): Best photos of the world
Owner URL: https://www.facebook.com/Best.Fotos.Of.The.World

Tagged with profiles:
Vikas Gaikwad (https://www.facebook.com/vikas.gaikwad.1612)
Lakhan Bhanuse (https://www.facebook.com/lakhan.bhanuse)
Kiran Ghoble (https://www.facebook.com/kiran.ghoble)
Abin Chakkara (https://www.facebook.com/abin.chakkara)
Vitthal Jadhav (https://www.facebook.com/vitthal.jadhav.52459)
Harshit Kumar (https://www.facebook.com/pammu.kumar.9)
Manish Sharma (https://www.facebook.com/manishmontysharma)
Vivek Bhumkar (https://www.facebook.com/vivek.bhumkar.1)
Sopho Arakishvili (https://www.facebook.com/sopho.arakishvili)
Sunil Kumar (https://www.facebook.com/people/Sunil-Kumar/100004499886492)
Shahib Singh (https://www.facebook.com/shahib.singh.3)
Mita Das (https://www.facebook.com/mukta.das.583)
Sneha Sweet (https://www.facebook.com/sneha.sweet.5872)
Rakesh Biyani (https://www.facebook.com/rakesh.biyani.50)
Zura Arakishvili (https://www.facebook.com/zura.arakishvili)
Dalia Edith Pérez Castañeda (https://www.facebook.com/daliaperezxalapa)
Shivu Vicky (https://www.facebook.com/shivanands.langaatad)
Forgive To Forget (https://www.facebook.com/thuchoa.tran)
Amit Verma (https://www.facebook.com/people/Amit-Verma/100003237125978)
Khursheed Patel (https://www.facebook.com/khursheed.patel)
Munmun Guha (https://www.facebook.com/munmun.guha.71)
Krishna Nk (https://www.facebook.com/krishnamurthy.axn)
Debabrata Paul (https://www.facebook.com/debabrata.paul.505)
Leveny Pradas West (https://www.facebook.com/levywest)
Adele McLeod (https://www.facebook.com/adele.mcleod1)
Leonardo L. Canillas (https://www.facebook.com/virgilio.canillas.18)
Mahshid Rh (https://www.facebook.com/mahshid.rahimi.5)
Ajay Giri (https://www.facebook.com/ajay.giri.3517)
Sudha Bharti (https://www.facebook.com/sudha.bharti.98)
Mehrdad Rahimi (https://www.facebook.com/mehrdad.rahimi.12)
Sameer Sam (https://www.facebook.com/sameer.sam.31945243)
Crystal Flowers (https://www.facebook.com/1.psycho.pixie)
Vesna Nikolcov (https://www.facebook.com/vesna.nikolcov)
Ziba Rahimi (https://www.facebook.com/ziba.rahimi)
Mary Smith White (https://www.facebook.com/mary.s.white.79)
Kỉ Hà Băng (https://www.facebook.com/tathi.thuan)

Tagged without profiles:
Prashant Rammtake
Lawrence Luggionskky
Maria C. Lanza
Vìvék Yàdûwáñshí
Satendra Yadav

Getting the source

 git clone https://github.com/chokepoint/fbpic2id.git

 Limitations

Some results on Facebook are limited to users that aren't logged in. In order to get greater results, adding login capability to the script would likely yield more information. Another idea would be to combine this with Facebook Query Language (FQL) in order to scrape information on each owner and taggee. For more information about FQL scraping check out Blackhat Library