1. Internet Scale Port Scan Data and Analysis


    A couple days ago, this was posted:

    Port scanning /0 using insecure embedded devices

    Abstract While playing around with the Nmap Scripting Engine (NSE) we discovered an amazing number of open embedded devices on the Internet. Many of them are based on Linux and allow login to standard BusyBox with empty or default credentials. We used these devices to build a distributed port scanner to scan all IPv4 addresses. These scans include service probes for the most common ports, ICMP ping, reverse DNS and SYN scans. We analyzed some of the data to get an estimation of the IP address usage.

    It is a write up about performing an Internet scale port scan using thousands of compromised busybox embedded devices/linux servers.

    While this is wildly unethical, and almost certainly illegal, the results of this study are pretty interesting and it is more interesting that the author decided to post all his code and data (~9TB uncompressed, 1.5 TB Compressed) online for free downloads.

    The author also posted some interactive web apps that allow exploration of this data set:

    It is definitely interesting to see how more and more network/security data is being collected and made available freely on the Internet. I am undecided whether this helps security or hurts security longterm. It definitely makes the situation worse i the short term.

    —Jason

    2 months ago  /  1 note

  2. Proactive Defense for Evolving Cyber Threats (Sandia Report)



    I stumbled on this recently. It is a small collection of reports/publications from Sandia National Labs on using Machine Learning and Predictive Analytics for Computer Network Defense. Here is what is contained in the PDF:

    [1] Early warning analysis for social diffusion events, Security Informatics, Vol. 1, 2012, SAND 2010-5334C.
    [2] Proactive cyber defense, Chapter in Springer Integrated Series on Intelligent Systems, 2012 Document No. 5299122, SAND 2011-8794P).
    [3] Predictability-oriented defense against adaptive adversaries, Proc. IEEE International Conference on Systems, Man, and Cybernetics, Seoul, Korea, October 2012. [or Predictive moving target defense, Proc. 2012 National Symposium on Moving Target Research, Annapolis, MD, June 2012.], SAND 2012-4007C.
    [4] Leveraging sociological models for prediction I: Inferring adversarial relationships, and II: Early warning for complex contagions, Proc. IEEE International Conference on Intelligence and Security Informatics, Washington, DC, June 2012 [Winner of the 2012 Best Paper Award, IEEE ISI], SAND 2012-6729C.
    [5] Predictive defense against evolving adversaries, Proc. IEEE International Conference on Intelligence and Security Informatics, Washington, DC, June 2012, SAND 2012-4007C. [6] Proactive defense for evolving cyber threats, Proc. IEEE International Conference on Intelligence and Security Informatics, Beijing, China, July 2011 [Winner of the 2011 Best Paper Award, IEEE ISI], SAND 2011-2445C.

    Proactive Defense for Evolving Cyber Threats (PDF)

    —Jason

    3 months ago  /  1 note

  3. Big Data Security Analytics from Packetloop/Hortonworks



    This was a great series of articles from the guys at Packetloop on using PacketPig for large scale pcap analysis including offline intrusion detection using Snort over TBs of pcaps and security analytics.

    —Jason

    3 months ago  /  1 note

  4. Large Scale Malicious Domain Classification with Storm, Random Forrests, and Markov Models




    At Endgame we have been working on a system for large scale malicious DNS detection, and Myself and John Munro recently presented some of this work at FloCon.


    Abstract:

    Clairvoyant Squirrel: Large Scale Malicious Domain Classification

    Large scale classification of domain names has many applications in network monitoring, intrusion detection, and forensics. The goal with this research is to predict a domain’s maliciousness solely based on the domain string itself, and to perform this classification on domains seen in real-time on high traffic networks, giving network administrators insight into possible intrusions. Our classification model uses the Random Forest algorithm with a 22-feature vector of domain string characteristics. Most of these features are numeric and are quick to calculate. Our model is currently trained off-line on a corpus of highly malicious domains gathered from DNS traffic originating from a malware execution sandbox and benign, popular domains from a high traffic DNS sensor. For stream classification, we use an internally developed platform for distributed high speed event processing that was built over Twitter’s recently open sourced Storm project. We discuss the system architecture as well as the logic behind our model’s features and sampling techniques that have led to 97% classification accuracy on our dataset and the model’s performance within our streaming environment.



    Here are the slides in case you’re interested.

    —Jason

    3 months ago  /  0 notes

  5. Packetpig - Open Source Big Data Security Analysis


    A coworker told me about this project today, and I thought I would share since it looks promising.

    Packetpig is an open source project hosted on github by @packetloop that contains Hadoop InputFormats, Pig Loaders, Pig scripts and R scripts for processing and analyzing pcap data. It also has classes that allow you to stream packets from Hadoop to local snort and p0f processes so you can parallelize this type of packet processing.

    Check it out:

    —Jason
    @jason_trost

    12 months ago  /  1 note

  6. Hadoop DNS Mining


    This is another quick post. I have been working on this small framework for a while now and I decided to publish the code before I completely finished.

    The hadoop-dns-mining framework enables large scale DNS lookups using Hadoop. For example, if you had access to zone files from COM, NET, ORG, etc (all free and publicly available), you could take each domain in these files and use this framework to resolve the domains for various record types (A, AAAA, MX, TXT, NS, etc). After resolving the domains, you can run them through an enrichment process to add city, county, lat, long, ASN, and AS Name (via Maxmind’s DBs). And, you could scale out this collection and processing effort using Hadoop, say, running over EC2.

    There are some interesting applications of this type of system, like using the existing zone files names to brute force “generating” zone files for TLDs that do not publish them (most ccTLDs do not). For example, like this company has done: http://viewdns.info/data/

    For more details check out my github repo. The README covers DNS collection and geo enrichment. I have code checked in that will store this data in Accumulo using a few different storage/access patterns, but more explanation will come later as I have time.

    https://github.com/jt6211/hadoop-dns-mining

    —Jason
    @jason_trost

    1 year ago  /  1 note

  7. Hadoop Binary Analysis Framework


    This is a quick post. I wrote this little framework for using Hadoop to analyze lots of small files. This may not be the most optimal way of doing this, but it worked well and makes repeated analysis tasks easy and scalable.

    https://github.com/jt6211/hadoop-binary-analysis

    I recently needed a quick way to analyze millions of small binary files (from 100K-19MB each) and I wanted a scalable way to repeatedly do this sort of analysis. I chose Hadoop as the platform, and I built this little framework (really, a single MapReduce job) to do it. This is very much a work in progress, and feedback and pull requests are welcome.

    The main MapReduce job in this framework accepts a Sequence file of <Text, BytesWritable> where the Text is a name and the BytesWritable is the contents of a file. The framework unpacks the bytes of the BytesWritable to the local filesystem of the mapper it is running on, allowing the mapper to run arbitrary analysis tools that require local filesystem access. The framework then captures stdout and stderr from the analysis tool/script and stores it (how it stores it is pluggable, see io.covert.binary.analysis.OutputParser).

    Building:

    mvn package assembly:assembly
    

    Running:

    JAR=target/hadoop-binary-analysis-1.0-SNAPSHOT-job.jar
    
    # a local directory with files in it (directories are ignored for now)
    LOCAL_FILES=src/main/java/io/covert/binary/analysis/
    INPUT="dir-in-hdfs"
    OUTPUT="output-dir-in-hdfs"
    
    # convert a bunch of relatively small files into one sequence file (Text, BytesWritable)
    hadoop jar $JAR io.covert.binary.analysis.BuildSequenceFile $LOCAL_FILES $INPUT
    
    # Use the config properties in example.xml to basically run the wrapper.sh script on each file using Hadoop
    # as the platform for computation
    hadoop jar $JAR io.covert.binary.analysis.BinaryAnalysisJob -files wrapper.sh -conf example.xml $INPUT $OUTPUT
    

    From example.xml:

      <property>
        <name>binary.analysis.program</name>
        <value>./wrapper.sh</value>
      </property>
    
      <property>
        <name>binary.analysis.program.args</name>
        <value>${file}</value>
      </property>
    
      <property>
        <name>binary.analysis.program.args.delim</name>
        <value>,</value>
      </property>
    


    This block of example instructs the framework to run wrapper.sh using the args of ${file} (where ${file} is replaced by the unpacked filename from the Sequence File. If multiple command line args are required, they can be specified by appending a delimiter and then each arg to the value of the binary.analysis.program.args property

    —Jason

    1 year ago  /  1 note

  8. Accumulo and Pig


    I just posted a functional AccumuloStorage module to github.

    Here’s how you use it (also in the github README)

    1. Build the JAR

    Note: you will need to download the accumulo src, build it, and install it into your maven repo before this will work

    mvn package
    

    This will create a JAR file here: target/accumulo-pig-1.5.0-incubating-SNAPSHOT.jar

    2. Download the JARs needed by pig
    mvn dependency:copy-dependencies -DoutputDirectory=lib  \
        -DincludeArtifactIds=zookeeper,libthrift,accumulo-core,cloudtrace
    

    This should have copied the needed dependency jars into a lib directory.

    3. Print the register statements we will need in pig
    for JAR in lib/*.jar target/accumulo-pig-1.5.0-incubating-SNAPSHOT.jar ; 
    do 
        echo register `pwd`/$JAR; 
    done
    

    Here is some example output

    register /home/developer/workspace/accumulo-pig/lib/accumulo-core-1.5.0-incubating-SNAPSHOT.jar
    register /home/developer/workspace/accumulo-pig/lib/cloudtrace-1.5.0-incubating-SNAPSHOT.jar
    register /home/developer/workspace/accumulo-pig/lib/libthrift-0.6.1.jar
    register /home/developer/workspace/accumulo-pig/lib/zookeeper-3.3.1.jar
    register /home/developer/workspace/accumulo-pig/target/accumulo-pig-1.5.0-incubating-SNAPSHOT.jar
    

    5. Run Pig

    Copy the register statements above and paste them into the pig terminal. Then you can LOAD from and STORE into accumulo.

    $ pig
    

    Here are the pig commands run if you don’t want to look through the output above:

    # load just the web content (from the f:cnt column) from the webpage table
    DATA = LOAD 
    'accumulo://webpage?instance=inst&user=root&password=secret&zookeepers=127.0.0.1:2181&columns=f:cnt' 
       using org.apache.accumulo.pig.AccumuloStorage() AS (row, cf, cq, cv, ts, val);
    
    # basically, remove the ts field since it is not needed
    DATA2 = FOREACH DATA GENERATE row, cf, cq, cv, val;
    
    # store the data as is in a new table called webpage_content
    STORE DATA2 into 
    'accumulo://webpage_content?instance=inst&user=root&password=secret&zookeepers=127.0.0.1:2181' 
       using org.apache.accumulo.pig.AccumuloStorage();
    

    A more detailed blog post going in more detail of how/why this is useful will follow.

    —Jason

    Update (2012/03/04): you may want to run this as the first line of the pig script:

    SET mapred.map.tasks.speculative.execution false
    

    This will avoid ingesting duplicate entries into accumulo. For the data from this post, ingesting duplicate entries wouldn’t cause any real issues because Accumulo’s VersioningIterator would only keep the newest copy, but for columns/tables with aggregation configured (e.g. using LongCombiner) we definitely don’t want this.

    1 year ago  /  0 notes

  9. Accumulo, Nutch, and Gora


    In this post I will outline the steps necessary to use Accumulo and Gora to store content retrieved by Nutch.

    Apache Accumulo

    Accumulo Logo

    For those of you unfamiliar with Accumulo, it is an incubating Apache project and …

    “Accumulo is a sorted, distributed key/value store based on Google’s BigTable design. It is built on top of Apache HadoopZookeeper, and Thrift. It features a few novel improvements on the BigTable design in the form of cell-level access labels and a server-side programming mechanism that can modify key/value pairs at various points in the data management process.”

    Accumulo is conceptually very similar to HBase, but it has some nice features that HBase is currently lacking.  Some of these features are:

    • Cell level security
    • No fat row problem - i.e. entire rows don’t need to fit in RAM
    • No limitation on Column Families or when Column Families can be created
    • Server side, data local, programming abstraction called Iterators.  Iterators are incredibly useful for adding functionality to Tablet Servers such as data local filtering, aggregation, and search.  
    • Check out this example application that shows off some of the features mentioned above: THE WIKIPEDIA SEARCH EXAMPLE EXPLAINED, WITH PERFORMANCE NUMBERS.

    Apache Gora

    Gora Logo

    Gora is a object relational/non-relational mapping for arbitrary data stores including both relational (MySQL) and non-relational data stores (HBase, Cassandra, Accumulo, etc).  It was designed for Big Data applications and has support (interfaces) for Apache Pig, Apache Hive, Cascading, and generic Map/Reduce.

    Apache Nutch

    Nutch Logo

    Nutch is a highly scalable web crawler built over Hadoop Map/Reduce.  It was designed from the ground up to be an Internet scale web crawler.  This is a great overview of Nutch’s architecture: Nutch as a Web Data Mining Platform

    Accumulo + Nutch + Gora

    I generally prefer git over svn, in this post I use the source code hosted on github.

    1. Obtain all sources (and Accumulo patch for GORA)

    git clone git://github.com/apache/nutch.git
    git clone git://github.com/apache/gora.git
    git clone git://github.com/apache/accumulo.git
    wget https://issues.apache.org/jira/secure/attachment/12507986/GORA-65-1.patch

    2. Configure and Build Each Project:

    Standard build and maven install for accumulo

    cd accumulo
    mvn package install

    Patching GORA for Accumulo Support

    Gora needs to be patched for support for Accumulo.  This patch should be considered beta, but I found it works good enough for experimenting with Nutch/GORA.  Note: -DskipTests is used because some of the tests seemed to hang indefinitely, so I skipped them for now.

    cd ../gora
    patch -p0 < ../GORA-65-1.patch
    mvn package install -DskipTests

    Building Nutch/GORA

    So, getting Nutch/GORA to build was a bit of a challenge.  I will outline some of the hoops I had to jump through.  File paths mentioned below are assuming you are in the nutch project directory.

    Run the following commands to checkout the nutchgora branch of Nutch.

       cd ../nutch
       git checkout origin/nutchgora
    
    • Modify the ivy/ivy.xml file. Change gora-core and gora-sql dependencies rev from "0.1.1-incubating" to "0.2-SNAPSHOT".  This is to match the patched version we just installed. Also, add the following lines:
    • Modify the ivy/ivysettings.xml file.  Add the following line to the top of the <resolvers> section. This will configure ant/ivy to use your local maven repository when resolving dependencies.  This is necessary because the patched version of GORA and the latest Accumulo version are not in any public maven repos.

      <ibiblio name="local" root="file://${user.home}/.m2/repository/"
      pattern="${maven2.pattern.ext}"  m2compatible="true"  />


       ant
    


    3. Deploy and Run

    Configure Accumulo and its Dependencies

    For this post I am only going to cover the basics for getting these systems to run on a single machine. Deploying and running over a cluster may be covered in another post.

    Configure and Start Hadoop

    cd ..
    wget ftp://apache.cs.utah.edu/apache.org//hadoop/common/hadoop-0.20.2/hadoop-0.20.2.tar.gz
    tar zxvf hadoop-0.20.2.tar.gz

    Add the following to hadoop-0.20.2/conf/core-site.xml

    <configuration>
      <property>
        <name>fs.default.name</name>
        <value>hdfs://127.0.0.1/</value>
      </property>
      <property>
        <name>hadoop.tmp.dir</name>
        <value>/home/${user.name}/tmp/hadoop</value>
      </property>
    </configuration>
    

    Add the following to hadoop-0.20.2/conf/mapred-site.xml

    <configuration>
      <property>
        <name>mapred.job.tracker</name>
        <value>127.0.0.1:9001</value>
      </property>
    </configuration>
    

    Set the JAVA_HOME variable in hadoop-0.20.2/conf/hadoop-env.sh. Here is an example from my system: export JAVA_HOME=/usr/lib/jvm/java-6-sun-1.6.0.26

    At this point, you should have a bare bones configured hadoop installation. It is time to start it…

    Run the following commands:

    mkdir -p ~/tmp/hadoop
    cd hadoop-0.20.2
    ./bin/hadoop namenode -format
    ./bin/start-all.sh

    If you configured everything properly, you should be able to open http://127.0.0.1:50070/dfshealth.jsp in a web browser and see a page that looks like this. If there is a message saying that the Namenode is in safe mode, wait a minute or two and refresh the page. It should go away.

    You should also be able to open http://127.0.0.1:50030/jobtracker.jsp in a web browser and see a page that looks like this:

    In both of these status webpages you should be able to see a 1 listed after "Live Nodes" and "Nodes" respectively.

    Configure and Start Zookeeper

    cd ..
    wget ftp://apache.cs.utah.edu/apache.org/zookeeper/zookeeper-3.4.3/zookeeper-3.4.3.tar.gz
    tar zxvf zookeeper-3.4.3.tar.gz

    Add the following to zookeeper-3.4.3/conf/zoo.cfg. Create this file if it does not exist. NOTE: replace _USERNAME_ with your username.

    # The number of milliseconds of each tick
    tickTime=2000
    # The number of ticks that the initial
    # synchronization phase can take
    initLimit=10
    # The number of ticks that can pass between
    # sending a request and getting an acknowledgement
    syncLimit=5
    # the directory where the snapshot is stored.
    dataDir=/home/_USERNAME_/tmp/zookeeper-data
    # the port at which the clients will connect
    clientPort=2181
    maxClientCnxns=100
    

    Edit bin/zkEnv.sh. Right after the following lines.

    ZOOBINDIR=${ZOOBINDIR:-/usr/bin}
    ZOOKEEPER_PREFIX=${ZOOBINDIR}/..
    

    Add this line:

    export ZOO_LOG_DIR=${ZOOKEEPER_PREFIX}/logs
    

    cd zookeeper-3.4.3
    export ZOO_LOG_DIR=$HOME/blogpost/zookeeper-3.4.3/logs
    mkdir $ZOO_LOG_DIR
    mkdir ~/tmp/zookeeper-data

    At this point, you should have a configured zookeeper installation, it’s time to start it…

    ./bin/zkServer.sh start

    If you configured zookeeper and it successfully started you should be able to run the following command:

    ./bin/zkCli.sh -server 127.0.0.1:2181

    It will output a bunch of logging messages, this is fine. Press <ENTER>, and then you should be dropped into a shell this a prompt that looks like the following:

    [zk: 127.0.0.1:2181(CONNECTED) 0]

    Type ls / and then <ENTER>. You should see a single line of output (followed again by a prompt) that looks like the following:

    [zookeeper]

    If so, then zookeeper is configured and running properly. When you first run the zkCli.sh command, if you see stack traces that look like this:

    java.net.ConnectException: Connection refused
        at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
        at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:701)
        at org.apache.zookeeper.ClientCnxnSocketNIO.doTransport(ClientCnxnSocketNIO.java:286)
        at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1035)
    

    Then it means zookeeper failed to start for some reason, isn’t listening on 127.0.0.1:2181, or you may have a local firewall blocking access to that port.

    Configure and Start Accumulo

    cd ../accumulo
    mvn package -P assemble
    cp src/assemble/target/accumulo-1.5.0-incubating-SNAPSHOT-dist.tar.gz ../
    cd ../
    tar zxf accumulo-1.5.0-incubating-SNAPSHOT-dist.tar.gz
    cd accumulo-1.5.0-incubating-SNAPSHOT/conf
    rename s/.example// *.example
    # basically disabling the custom security policy file for now
    # since I had issues getting accumulo to work with it enabled
    mv accumulo.policy accumulo.policy.example

    Edit accumulo-env.sh. At the top of the file, define HADOOP_HOME, ZOOKEEPER_HOME, and JAVA_HOME. Here is an example:

    export HADOOP_HOME=$HOME/hadoop-0.20.2
    export ZOOKEEPER_HOME=$HOME/zookeeper-3.4.3
    export JAVA_HOME=/usr/lib/jvm/java-6-sun-1.6.0.26

    Edit accumulo-site.xml.

    Set the logger.dir.walog property.

    <property>
      <name>logger.dir.walog</name>
      <value>$HOME/tmp/walogs</value>
    </property>
    

    Set the instance.secret property.

    <property>
      <name>instance.secret</name>
      <value>SOME-PASSWORD-OF-YOUR-CHOOSING</value>
    </property>
    

    Run the following commands:

    mkdir $HOME/tmp/walogs
    cd ../

    At this point you should have a fully configured Accumulo installation. It is time to initialize it and start it…

    ./bin/accumulo init

    You should see similar output to this. I set my instance name to "inst" and my password to "secret". You may want to do the same for the sake of this tutorial or make sure to set the correct config parameters later.

    23 08:15:26,635 [util.Initialize] INFO : Hadoop Filesystem is hdfs://127.0.0.1/
    23 08:15:26,637 [util.Initialize] INFO : Accumulo data dir is /accumulo
    23 08:15:26,637 [util.Initialize] INFO : Zookeeper server is localhost:2181
    
    Warning!!! Your instance secret is still set to the default, this is not secure. 
    We highly recommend you change it.
    
    You can change the instance secret in accumulo by using: bin/accumulo 
    org.apache.accumulo.server.util.ChangeSecret oldPassword newPassword. 
    You will also need to edit your secret in your configuration file by adding the property 
    instance.secret to your conf/accumulo-site.xml. Without this accumulo will not operate correctly
    Instance name : inst
    Enter initial password for root: ******
    Confirm initial password for root: *****
    23 08:15:34,100 [util.NativeCodeLoader] INFO : Loaded the native-hadoop library
    23 08:15:34,337 [security.ZKAuthenticator] INFO : Initialized root user with 
    username: root at the request of user !SYSTEM
    

    Note: If it appears to hang after you entered the instance name, zookeeper may not be running. <CTRL>-C the accumulo init and make sure zookeeper is running.

    Now run:

    ./bin/start-all.sh

    After this finishes, you should be able to open http://127.0.0.1:50095/ in a web browser and see a page very similar to this:

    The important items to note on this page are the is a 1 after the Tablet Servers, Live Data Nodes, and Trackers in the “Accumulo Master”, “NameNode”, and “JobTracker” tables, respectively. There should also be entry in the “Zookeeper” table.

    4. Crawl

    At this point, you should have a fully functional Hadoop, Zookeeper, and Accumulo install, so we are ready to run a Nutch web crawl. Create a file with URLs, one per line, call it seeds.txt and place it in your home directory. I added the following URLs to my seeds file:

    http://projects.apache.org/indexes/alpha.html
    http://www.dmoz.org/Arts/People/
    

    Run the following commands:

     cd ../nutch/
     ./runtime/local/bin/nutch crawl file://$HOME/seeds.txt -depth 1
    

    You should see some log messages printed to the console, but hopefully no stack traces. If you see a stack trace, you may need to go back and check your configs to make sure they match the ones we created earlier.

    After the crawler finishes, you should be able to explore it using the accumulo shell.

     cd ../accumulo-1.5.0-incubating-SNAPSHOT/bin/
     ./accumulo shell -u root -p secret
    

    Further details and exploration of this data in Accumulo will have to wait for another blog post.

    I ended up posting all the modified code from Gora (accumulo patch) and Nutchgora (patches for getting gora-accumulo working) to my github. Check it out.

    Nutchgora+accumulo
    https://github.com/jt6211/nutch/tree/gora-accumulo

    Gora+accumulo patch
    https://github.com/jt6211/gora/tree/gora-accumulo

    Let me know if you have any questions…

    —Jason
    @jason_trost

    Update (3/2): someone told me that they had problems getting nutch to build and that this patch worked for them (even though the patch is for GORA). I would be curious if anyone else has this same issue. Here is the error they encountered when building with ant:

    [ivy:resolve] :: problems summary ::
    [ivy:resolve] :::: WARNINGS
    [ivy:resolve]           ::::::::::::::::::::::::::::::::::::::::::::::
    [ivy:resolve]           ::          UNRESOLVED DEPENDENCIES         ::
    [ivy:resolve]           ::::::::::::::::::::::::::::::::::::::::::::::
    [ivy:resolve]           :: log4j#log4j;1.2.15: configuration not found in log4j#log4j;1.2.15: 'master'.
    

    1 year ago  /  2 notes

  10. Introductions


    My name is Jason Trost. I am a developer and security researcher deeply interested in Big Data/cloud computing and machine Learning. I have a few years experience using Hadoop and Mapreduce to process and analyze computer network and security data. I also have experience developing applications with Apache Accumulo and Backtype’s Storm. I like Java and python, as well as UNIX shell scripting. This blog is going to cover topics ranging from processing data with Hadoop and MapReduce to applied machine learning techniques to interesting hacking and computer network defense tools I encounter. I am always open to requests for blog posts…

    Feel free to follow me on Twitter and/or Github.

    Github: jt6211

    Twitter: @jason_trost

    —Jason

    1 year ago  /  1 note