Facebook WTF?

I do not use Facebook and after a few years, I finally convinced my wife to give it up. In my opinion, the social benefits of Facebook are far outweighed by the privacy and security concerns. To demonstrate, my father-in-law recently received a phishing message through facebookmail, see the screenshot below.

facebook_phish

The email has all the typical signs of a phishing email including the bad grammar and the FUD meant to get you to click on the link. The only problem is the link is a legitimate Facebook URL. Confused, I fired up a VM and visited the link, which took me to this page.

facebook_leaving

The page appears to be a security warning with a URL at the bottom. I think most Facebook users would see this as normal and click Continue. In fact, the page is designed to let you know you are leaving Facebook to go to the displayed URL but the only indication that you are leaving Facebook is the title of the page.

facebook_leaving_title

I thought to myself, “That can’t be right, maybe a logged in user gets a different message”. So, I created an account and visited the link again. This time I got a warning message letting me know the link was potentially spammy.

facebook_leaving_warning

Excellent, Facebook is watching out for it’s users and protecting them from spammy links. Not so fast. If you look at the phishing URL closely, you can see it has three parts: http://www.facebook.com/l/, a random string, and the redirect URL. I decide to make some changes to the phishing URL and see what would happen.

If you modify the random string the warning message is no longer displayed because Facebook doesn’t recognize this new URL as malicious. This means that Facebook is detecting the malicious link on the full URL and not on the redirected URL. Based on this, it seems that scammers could setup one site and create many different URLs to redirect to this one site and they would likely never be caught by Facebook.

To prevent problems With these type of links, Facebook should make it very clear that the user is leaving Facebook to go to a new site, a message in the page title is not enough. In addition, Facebook should determine if a link is “spammy” based on the destination URL not based on the original URL.

I reported this as a potential bug but Facebook didn’t seem to see it as a bug. Maybe I’m crazy, what do you think?

Why Evolution is True

I know this is off topic for this blog but it’s my blog so here goes. I was raised as a Christian, and still hold to the Christian faith. As a part of my upbringing I was always taught that evolution was not true. Not wanting to blindly believe this, I decided to learn more about it and see if the arguments for evolution stand to reason. With this end in mind I bought the book “Why Evolution is True” by Jerry A. Coyne and started reading it tonight. I have read the Preface, Introduction, and first chapter. Evolution, like many other scientific theories has been studied for over a hundred years by people much smarter than I so I don’t expect to read this book and be able to prove or disprove the arguments. My primary goal, for now, is to ask questions and once those questions have been satisfactorily answered, then draw my conclusions. With that in mind, these are some of my observations and questions from what I’ve read so far.

Chapter 1:
On pages 9 and 10, Dr. Coyne says,

“Matchbooks resemble the kinds of creatures expected under a creationist explanation of life. In such a case, organisms would not have common ancestry, but would simply result from an instantaneous creation of forms designed de novo to fit their environments. Under this scenario, we wouldn’t expect to see species falling into a nested hierarchy of forms that is recognized by all biologists.”

My question here is why does creation imply disorder. Is it not feasible for an entity that is powerful enough to create everything we know to also do it in an orderly manner? If creation was done in an orderly manner, why would we not expect to see a nested hierarchy of forms and similarities in DNA structure?

On page 11, Dr. Coyne says,

“Over time, the population will gradually become more and more suited to its environment as helpful mutations arise and spread through the population, while deleterious ones are weeded out.”

This would seem to imply that over the course of tens of thousands of years there were no drastic changes to the environment otherwise natural selection would not be able to keep up. Do other areas of science show these long periods of time with no drastic changes to the earth’s environment?

On page 18, Dr. Coyne says,

Imperfection is the mark of evolution, not of conscious design. We should then be able to find cases of imperfect adaptation, in which evolution has not been able to achieve the same degree of optimality as would a creator.”

This statement doesn’t seem to be provable and it also assumes that optimality is the ultimate goal of a creator. There is no reason that what we see as imperfection could not be purposely designed.

The next chapters get into the science behind evolution and I look forward to reading them. I hope to do additional blog posts as I move through the book.

PTArticlegen.com: Behind the Scenes

The other day I put up a site called ptarticlegen.com that creates a random penetration testing article using Markov chains. If you’ve never heard of a Markov chain, check out the Wikipedia article. Put simply, a Markov chain is generated by making a random choice based on the current state of a system and using that choice to determine the next state of the system. The current state of the system only depends on the previous state and not all the choices leading up to the previous state.

Markov chains can be used to generate sentences by taking a word pair and choosing the next word from a list of words that typically follow that word pair. But first, a set of source data has to be analyzed to find word pairs and create a list of words that typically follow those word pairs.

As an example consider these two sentences:
The fox jumped over the spoon.
The cow jumped over the moon.

The word pairs and the list of following words would look like this:

(The, fox) - [jumped]
(fox, jumped) - [over]
(jumped, over) - [the, the]
(over, the) - [spoon, moon]
(The, cow) - [jumped]
(cow, jumped) -[over]

If we use (The, fox) as our starting word pair we can generate the sentence, “The fox jumped over the moon” by making the following choices:

(The, fox) -> jumped
(fox, jumped) -> over
(jumped, over) -> the
(over, the) -> moon

To create the articles I wrote a Python script to analyze 600 sentences taken from my blog and then generate new sentences based on the analysis. I also used Python and web.py to create the web site. The Markov chain code I wrote is a modification of code from these two excellent resources. You can get the source code for ptarticlegen.com from my Github account.

ADSelfService Plus Account Enumeration

On a recent pentest I came across an externally accessible ADSelfService Plus server. ADSelfService Plus is sold by ManageEngine and is designed to allow users to reset and unlock their own Active Directory (AD) accounts. To use the service, the user is required to register and configure multiple security questions. After playing with the server for a while, I determined that attempting to reset the password of an unregistered user resulted in an error while attempting to reset the password of a registered user displayed the user’s security questions. This meant I could enumerate user accounts and the associated security questions.

Using Python and a list of common first and last names I developed a script, ad_self_service_miner.py, that would enumerate user accounts and capture the security questions. The script is available on my Github account. To use the script you will need to set the server and domain. The domain information is available on the account reset page.

server = 'https://server'
domain = 'DOMAIN'

You will also need to modify the username format. In my case the username was First.Last. Keep in mind that in most Windows environments the AD username typically matches the username portion of the email address, so a quick Google search should yield the correct username format.

user = "{0}.{1}".format(f.capitalize(), l.capitalize())

After you modify the script, get a list of first names and last names and save them to firstnames.txt and lastnames.txt, respectively, in the same directory as the script. Finally, let the script run. All user accounts and security questions will be stored in ad_self_service_miner.log also in the same directory as the script.

I tested approximately 20,000 username combinations and was able to enumerate 132 user accounts along with the associated security questions. The hard part is going through the process of finding the answers to the questions, which I didn’t do on this engagement.

You can find ADSelfService Plus devices using this Google search.

I spoke with ManageEngine about this vulnerability and was informed that there is a configuration setting to place a CAPTCHA on the account reset page, which will help prevent automated brute-forcing.

Introducing KnownPlainText.co

As a pentester, I often gain access to a Windows domain controller and dump the hashes. I can use pass-the-hash to login to other Windows machines with those credentials but if I want to login to web services or databases as those users, I need to crack the passwords. Typically, I would break out JtR, Ophcrack, rcracki_mt, or Hashcat. With Ophcrack or rcracki_mt, it can take anywhere from 30 minutes to many hours to crack all of the passwords, depending on the number of hashes in the file. In addition, you have to store Gigs worth of data files. With JtR or Hashcat, you have a similar wait time and you have to maintain extensive word lists and mangling rules. In addition, most of the passwords you test will not meet the Windows complexity requirements, which are common in large organizations.

KnownPlainText.co is different. It uses a database to store pre-computed hashes based on the most common base words and password mangling rules and all of the passwords meet the Windows complexity requirements. The initial database was built from public password breaches such as, rockyou, and facebook. As users upload new password hashes, the database will be updated with new base words and password mangling rules, becoming more efficient over time.

The value of KnownPlainText.co comes from the time/effectiveness trade off. You can spend hours cracking 100% of passwords or you can crack 10-20% of the passwords immediately. Over the course of the year, the time and money you save will completely pay for the service.

Please checkout the site for more details and feel free to provide constructive feedback.

Twitter Single User Oauth

I was helping a friend with a Python script he was using to query the Twitter search API and I decided I wanted to write a simple Twitter client in Python. Twitter allows users to use two OAuth authorization methods, three-legged and single user. Most OAuth examples and libraries are centered around three-legged authorization, which requires an application to call a Twitter authentication page, so the user can input his or her username and password and then calls back to another URL with the necessary access tokens. This sounded overly complex for what I wanted, so I started looking at single user authorization. I am sure the standard OAuth libraries can handle single user authorization just as well as three-legged authorization but I also wanted to understand the OAuth protocol better, so I wrote my own single user OAuth module using the information here and here. This module integrates with the Python Requests library so accessing Twitter is as easy as:

import requests
import twitter_auth

ssn = requests.Session
ssn.auth = twitter_auth.TwitterSingleOAuth(consumer_key,
                                           consumer_secret,
                                           access_token,
                                           access_token_secret)

resp = ssn.get('https://api.twitter.com/1.1/statuses/mentions_timeline.json')
print resp.json()

To use the library you will need to sign in to dev.twitter.com and create a new application and get your consumer key, consumer secret, access token, and access token secret. Don’t share the consumer secret or the access tokens with anyone, including Github. You can find the Twitter single user OAuth library here.

Have fun, and as always, let me know if there are any problems.

SSH Pwnage

After telling you about the impending SSH Apocalypse and releasing the SSH super virus, I received a number of good suggestions on improving my ssh_super_virus.py script. I didn’t want to modify ssh_super_virus.py though because I want to keep it for posterity’s sake. Instead I rewrote ssh_super_virus.py and included the suggested changes.

So, I give you ssh_pwn.py. This script will read the ‘users’ file and the SSH keys in the current directory and use them to authenticate to the list of hosts in the ‘hosts’ file, also in the current directory. If authentication is successful, the script will attempt to download additional SSH keys, the .bash_history file for the user, and SSL private keys. In addition, the script can be configured to automatically add new users , from /etc/passwd, and new hosts, from .ssh/known_hosts, to the list of users and hosts to test. Finally, you can give ssh_pwn.py your own list of post exploitation commands, which it will attempt to run and will save the output in the ‘postexploit’ file in the current directory.

You can get ssh_pwn.py from my pentest scripts repository at github.com. Enjoy, and as always let me know if there are any problems or if you have any suggested changes. 

Announcing the Impending SSH Apocalypse

The other day I read this article by Shaun Waterman at the Washington Times and it ticked me off a bit because of its obvious FUD (fear, uncertainty, and doubt). Mr. Waterman tells us about a flaw in SSH that will bring about data destruction of apocalyptic proportions. The flaw of impending doom? Key management. Apparently people leave SSH private keys lying around unprotected and because of this “most of the data on the servers of every company in the developed world” could get “wiped out.” I can’t blame Mr. Waterman for that gem, he is quoting Tatu Ylonen, the CEO of SSH Communications Security Corp.

I’m not saying SSH key management is not a problem. As a pentester, I look for unprotected SSH keys to dig my way deeper into a client’s network. What I am saying is Mr Waterman and Mr. Ylonen are blowing this thing way out of proportion for no other reason than to sell SSH key management software, which SSH Communications Security Corp just happens to sell.

My favorite part of the article was this quote:

Mr. Ylonen said a computer programmer could create a virus that would exploit SSH’s weaknesses and spread throughout servers to steal, distort or destroy confidential data.
“It would take days, perhaps only hours,” to write such a virus, he said.

So as not to disappoint Mr. Waterman and Mr. Ylonen, I decided to create an SSH super virus. It’s actually a Python script, which you can get here, and Mr. Ylonen was right, it only took me a few hours.

To use the script, follow the instructions below:

  1. Create a directory and place all the private keys you want to test and ssh_super_virus.py in the directory.
  2. Create a file called ‘hosts’ in the same directory and add each host you want to test to the file, one per line.
  3. Create a file called ‘users’ in the same directory and add each user you want to test to the file, one per line.

Ssh_super_virus.py will attempt to login to each host with each user/key combination. If a login is successful, ssh_super_virus.py will download all of the private keys in the users .ssh directory and test those against each user/host combination. You can also edit the script to add a list of evil_commands that will be run on successful login.

As always, use the script only for legal purposes and let me know if there are any problems with the script.

A Domain By Any Other Name

So, I discovered a small problem with my Web server the other day. I host the fwcheck.com firewall rule analyzer on an Amazon EC2 server running the Apache web server. Being a security nut, I decided that this server needed to be HTTPS only so I configured my my Apache server to redirect all HTTP traffic to HTTPS like so.

RewriteEngine   on
RewriteCond %{HTTP_HOST} ^fwcheck.com$
RewriteRule ^/(.*)$ https://fwcheck.com/$1 [NC,L,R]
RewriteCond %{HTTP_HOST} ^www.fwcheck.com$
RewriteRule ^/(.*)$ https://www.fwcheck.com/$1 [NC,L,R]
RewriteCond %{HTTP_HOST} ^54.245.114.39$
RewriteRule ^/(.*)$ https://fwcheck.com/$1 [NC,L,R]

After a little bit of testing, I was satisfied the redirection was working. Then I was looking at the server instance in the Amazon AWS console and I noticed the Amazon public DNS* name and decided to connect to my server using that name.

I got a directory listing, which I did not expect. So I went back to my Apache config and added another redirect as well as a directive to disallow directory listings.

<Directory />
    Options -Indexes
    AllowOverride None
</Directory>

RewriteCond %{HTTP_HOST} ^ec2-54-245-114-39.us-west-2.compute.amazonaws.com$
RewriteRule ^/(.*)$ https://fwcheck.com/$1 [NC,L,R]

It was a simple fix but it made me curious as to whether other servers were improperly configured like mine. So, I downloaded the Alexa top 1 million web sites and started writing some Python code. My goal was to find domains that were hosted on Amazon EC2 and that returned different results when accessing the web server with the domain name and with the Amazon DNS name.

First, I wrote a script that finds domains hosted on EC2. The script calls the host command to get a list of IP addresses associated with each domain. It then calls the host command again for each IP address, and parses the results to see which IP addresses were hosted at Amazon. The Amazon addresses are prefixed with ‘ec2_’.

Next, I wrote a script to make an HTTP connection to both the domain name and the Amazon DNS name for each IP address and checked the two responses to see if they differed. I then wrote the results to an HTML file for manual verification.

The results were not as good as I had hoped. Of the 1 million web sites checked, I found 15,877 unique domains on 11,842 unique servers were hosted on Amazon. Of those 15,877 domains only 3,183 domains did not match the results from one or more of the Amazon EC2 servers on which the domain was hosted. Although I was disappointed in the results, I did find a few gems**, which I won’t mention because I don’t want to end up like this guy.

* Amazon assigns a public DNS name to all of its externally accessible instances. The DNS name is based on the IP address and the data center in which the instance is located.
** All identified vulnerabilities were reported to the appropriate people.

Text Tables in Python

At Tenable, I write text-based, single-purpose API tools in Python. I often need to display data in table format and wanted an easy way to do this. So, I wrote a simple Python module to draw text tables. Texttable allows you to add a header, column names, and specify column alignments. You can get the code here.

Usage

import texttable

t1 = texttable.TextTable()
t1.header = 'A Table of Numbers'
t1.add_col_names(['Col1', 'Col2', 'Col3', 'Col4'])
t1.add_col_align(['<', '<', '^', '>'])
rows = [[1, 2, 3, 4],
        [5, 6, 7, 8],
        [9, 10, 11, 12],
        [111111, 22222222, 3333333333, 444444444444]]
t1.add_rows(rows)
print t1
    
t2 = texttable.TextTable()
t2.header = 'Another Table of Numbers'
t2.add_col_names(['Col1', 'Col2', 'Col3', 'Col4'])
t2.add_row([1, 2, 3, 4])
t2.add_row([5, 6, 7, 8])
t2.add_row([9, 10, 11, 12])
print t2