Open Question to Yankee Candle

Our local Yankee Candle store at the mall has a policy of asking for an email address and phone number with every transaction. I typically decline to provide the information and if they push I give them obviously false information. Yesterday, my family and I were shopping at Yankee Candle and while my wife and kids were roaming through the store I stood at the door watching. My 11 year old approached the counter to make a purchase. He went through the typical process, the clerk rang up the items and told him the amount, he gave her money and she provided change. Then she asked him for a phone number. At this point my son looks at me, still standing at the door, and says do I give her mama’s phone number. I told him, that he is not to give any strange adult a phone number because he is a minor. (I’ve trained my children not to provide personal info as a general rule. They even have codenames for those stupid restaurants that demand a name with every order.)

Once I told my son he was not allowed to provide information to the clerk, she asked me for a phone number and email address saying it was REQUIRED for the receipt. I gave her an obviously fake phone number and email address and she rolled her eyes and said I could have just opted out. I’m sorry but “REQUIRED for the receipt” and “Could have just opted out” are mutually exclusive. I will keep shopping at Yankee Candle because they have the candles my wife loves but I won’t ever give them valid information and I will train my children to give false information when asked by a strange adult.

My question to Yankee Candle is this, if there is an opt out option, why is it not used automatically for minors? Why are you putting 11 year olds in the position of giving complete strangers their phone numbers and email addresses?

Pentesting Redis Servers

Redis is an in-memory key/value data store used to handle backend data for many web applications. Often, Redis is used to store configuration information, session information, and user profile information. By default the Redis server does not require authentication for client access. This is not a problem if Redis is only listening on localhost but often it is not.

Finding Redis Servers

By default Redis listens on port 6379, which is not in the Nmap top 1000 port list or the /etc/services list used by Nessus. You will need to scan specifically for this service if you want to find it.

Interacting with Redis

The easiest way to interact with Redis is to use the Redis CLI client, redis-cli. On Kali2 you can install the client by installing the redis-tools package with apt-get. After installing redis-cli you can connect to the Redis server using redis-cli -h <hostname> -p <port>.

Once connected you can use the following commands to gather data from the server:

  • info – Outputs server data including version, number of databases, and the number of keys in each database.
  • select <n> – Select a database to work with. By default Redis has 16 databases available, 0 – 15. Typically, only 0 is used.
  • keys <pattern> – Display all keys matching the regex pattern. To see all keys use *.
  • type <key> – Displays the type of the value stored in the key, string, hash, set.
  • get <key> – Print the value of the string key.
  • hgetall <key> – Get all of the field/value pairs stored in the hash key.
  • hget <field> <key> – Get the value of the specified field in the hash key.

The full list of supported commands can be found here: This list is all of the commands supported in the latest version of Redis. Some of the commands may not work in older versions.

In addition to redis-cli, you can also access a Redis server using a number of programming languages. A full list of Redis clients by language is available here:

Simple Python Example

To use the example script below you will need to install the redis-py library using pip install redis. If Pip is not installed you can install it on Kali using apt-get install python-pip.

import redis
db = redis.StrictRedis(host='', port=6379)

# If we have a hash key, print all of the fields and values.
for key in db.keys():
    if db.type(key) == ‘hash’:
        r = db.hgetall(key)
        for k in r:
            print('Field: {0} Value: {1}'.format(k, r['k']))


If you come across a Redis server that is password protected, there is an NSE script that can be used to brute force the password. Once you find the password you can connect to the server using redis-cli -h <host> -p <port> -a <password>.

Update 2015/09/18

Thanks @bonsaiviking for pointing out the redis-info NSE script. So if you are hunting specifically for Redis servers you can use something like this:

nmap -p 6379 --script=redis-info --open

Which should yield results like this:

Starting Nmap 6.49BETA4 ( ) at 2015-09-18 12:02 EDT
Nmap scan report for localhost (
Host is up (0.000062s latency).
6379/tcp open  unknown
| redis-info: 
|   Version            2.8.17
|   Operating System   Linux 4.0.0-kali1-amd64 x86_64
|   Architecture       64 bits
|   Process ID         8020
|   Used CPU (sys)     0.04
|   Used CPU (user)    0.06
|   Connected clients  1
|   Connected slaves   0
|   Used memory        491.84K
|_  Role               master

Nmap done: 1 IP address (1 host up) scanned in 0.45 seconds

You can also scan for Redis servers using Metasploit with the auxiliary/scanner/misc/redis_server.

Beginner’s Guide to Pentesting

A while back I wrote a book called “Hack Yourself First.” I put the book up on LuLu and sold 29 copies. I appreciate all the folks who bought my book and I hope you found it worth the price.

Over the last few years I’ve made an attempt at selling software, selling consulting services, and selling this book but I’ve learned that I’m not a salesman. I have no desire to try to convince you that you need the services or products that I offer. It’s just not in me. So, like other projects that I have created with the intention of selling, I’ve decided this project should open sourced.

My book is no longer for sale on LuLu, instead it is available as a Word document from this site. The book is released under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International license. Feel free to use the book as is for learning or teaching penetration testing. You can also modify the book as long as you give proper attribution, aren’t making money off the sale of the book, and share your modifications under the same license.

I hope you enjoy the book, you can download it as a Word document or as a PDF.

Analysis of a Spam Link

This weekend I received a spam message that wanted to sell me tickets to a comedy show in Louisville, KY. This spam message caught my eye because it made it past Google’s spam filters and I’m planning to go to Derbycon in Louisville next week. I decided to explore the link in the email a bit and see what I could find.

The original link sent in the email was this.

The first thing I noticed was the base64 encoded data in the "j" parameter. I decoded the data and got the following JSON object.


Looking at the "l" key in the JSON object, I decided the special.php page was probably a redirect script so I opened the link using curl -I to get the headers.

HTTP/1.1 302 Moved Temporarily
Date: Mon, 22 Sep 2014 17:37:27 GMT
Server: Apache mod_fcgid/2.3.10-dev
X-Powered-By: PHP/5.4.31
Content-Type: text/html

Sure enough, the special.php page gave me a 302 response and sent me to I also noticed that the base64 data in the "j" parameter was passed to this new page but was URL encoded.

I again used curl -I to get the page at assuming it was a redirect script as well.

HTTP/1.1 302 Found
Server: nginx/1.0.4
Date: Mon, 22 Sep 2014 17:39:29 GMT
Content-Type: text/html
Connection: keep-alive
Keep-Alive: timeout=20
X-Powered-By: PHP/5.2.17
Location: http://
Cache-Control: max-age=259200
Expires: Thu, 25 Sep 2014 17:39:29 GMT

Once again, I’ve been redirected, this time to the URL referenced in the "l" key in the base64 encoded JSON object.

After doing a bit of research on I found that it is used by AtomPark Software as part of its Atomic Email Tracker software. I’m not sure what all of the keys in the JSON object represent but based on the information here, hxxp:// the "u" key is most likely the MD5 hash of the email address of the user account.

I decided to play around with the parameters a bit and see if all of the parameters were required for the redirect to be successful.

With the exception of the "l" key, I replaced all of the values in the JSON object with the letter “a.” For the “l” key I changed the URL to http%3A%2F%2F%20arbitrary.test so that my JSON object now looked like this.


I then base64 encoded the JSON object and once again used curl -I to see what would happen.

curl -I

HTTP/1.1 302 Found
Server: nginx/1.0.4
Date: Mon, 22 Sep 2014 17:45:49 GMT
Content-Type: text/html
Connection: keep-alive
Keep-Alive: timeout=20
X-Powered-By: PHP/5.2.17
Location: http:// arbitrary.test
Cache-Control: max-age=259200
Expires: Thu, 25 Sep 2014 17:45:49 GMT

This time I was redirected to the URL I chose but I did not have to provide a valid user id. This shows the server is an open redirect. Further testing showed that only the "r" and "l" keys were required in the JSON object and that it was not necessary to URL encode the target URL.

So if we base64 encode the following JSON object and pass it as the "j" parameter to we will be redirected to


Further research found two other domains run by the same company that are also vulnerable to the open redirect.

Derbycon Hacknight


We will be meeting in the main lobby of the Hyatt. The Friday night after party starts at 9:00 so I expect there will be plenty of room in the lobby. If not we can break into smaller groups and split up.


This is an opportunity for developers and wannabe developers to talk about projects and write some code. If you want to discuss your latest idea, need help with a current project, or want to learn how to program, this is the place for you.


Friday night, September 26th from 9:00PM – Until

What should I bring?

  • A laptop with a text editor and a compiler/parser for the language of your choice
  • Ideas for a project or a desire to help on an existing project.
  • Desire to write code (No one is going to write it for you.)


TBD – Keep an eye on my Twitter account (@averagesecguy)

What language are you coding in?

I primarily write in Python but can help out with Ruby and PHP. Depending on who else shows up, PowerShell, Ruby, Python, PHP, and C/C++/C# should be well represented.

Gone Phishin’

A couple of weeks ago I was contracted to run a basic phishing campaign. The overall goal of the campaign was to raise awareness, not to gather credentials or to execute code. The requirements included identifying which user clicked on the phishing links and recording any email responses from the targets. The basic process I followed is outlined below.

  1. Register a domain name and configure SPF records.
  2. Setup a phishing server.
  3. Build the phishing site.
  4. Listen for email responses.
  5. Send the phishing emails.

Configure the Domain and SPF Record

A few years ago I started using Namecheap for my domain name registrations and DNS hosting. They have great prices and their service is easy to use. For this campaign I went to NameCheap and registered a domain very similar to the target organization.

For a phishing campaign to be successful you have to bypass the target’s spam filters. Before a spam filter looks at the content of the email it makes sure the mail servers DNS records are in order. First, the IP address you are sending your phishing emails from should have an MX record associated with it. Second, there should be a reverse DNS record associated with the hostname in the MX record. Keep in mind, the reverse DNS record cannot be configured at NameCheap, it has to be configured with the ISP who provides the IP address. Finally, there should be an Sender Policy Framework (SPF) record that specifies the IP address and DNS names of servers allowed to send mail from your domain.

SPF records must be written in a particular format, which I can never remember. So, I use the SPF wizard provided by Microsoft to generate the SPF record for me.

For more information on setting up a phishing domain, check out Raphael Mudge’s excellent blog post, Email Delivery – What Every Pentester Should Know.

Setup a Server

There are a number of ways to get a small server to use for phishing but my preferred option is DigitalOcean because they are cheap and the servers are very easy to setup. First, you will need to register for a DO account or login if you already have one. Once you are at the dashboard, create a new droplet. The hostname for the droplet should be the same as the hostname used in the MX record. DigitalOcean will automatically create a PTR record that maps the droplet’s IP address to the hostname. I use the smallest server size, which is more than enough power to run the phishing campaign. Once the server is running, install and configure a web server.

Build the Phishing Site

For this contract there were two campaigns. For the first campaign the goal was to convince the target to click on a link in the email and visit the phishing site. The phishing site was just a static web page that provided the user with an awareness message. For the second campaign, the goal was to convince the target to visit a fake OWA site and attempt to enter credentials. If a target attempted to enter credentials then they were redirected to the awareness message. I used a bit of Javascript to handle the redirection, onclick="parent.location='http://<phish_server>/awareness.html'". For both campaigns, the client wanted to know which targets followed the links and for the second campaign the client wanted to know who attempted to enter OWA credentials.

Each target user was sent a link with a unique id number so that each web page visit could be tracked to a particular user. On each of the links I simply added a query parameter called id and gave it an incremental value for each user. So user 1 would have a link like http://<phishing_site>/awareness.html?id=1. Since the id value was sent as a query parameter, the web server logs could be used to pull stats on who visited each page and how often. Unfortunately, the client’s Barracuda Spam filter saw the id value and decided the links were tracking links and blocked the messages. The client allowed the quarantined messages to go through but I need to come up with a new strategy for the future.

Listen for Email Responses

Whenever a phishing campaign is run there will inevitably be targets who respond to the phishing emails with questions or automatic replies. The client wanted to track any email responses from the targets. Not wanting to setup sendmail just to listen for emails, I decided to write a Python script that would listen on port 25 and write to a file any emails received. The Python script is available on my GitHub account. The script must be run as root so that it can listen on port 25.

Send the Phishing Emails

Between the two campaigns I needed to send 150 emails. Not wanting to do this by hand, I again turned to Python to get the job done. I created a JSON file with one entry for each target. Each entry contained the variables that I would plug into my phishing template, including the unique identifier for the user, and the phishing campaign for which the target was involved. The script read the JSON file and for each user built the correct email from the template and sent the message. You can find a sample JSON file and the Python script on my GitHub account as well. This script does not need root permission.

Final Thoughts

Once the campaign was over, I was able to gather statistics on which users followed the links in the emails and which users attempted to enter credentials by reviewing the web server logs. Also note, the Javascript used to redirect users to the warning page could easily be rewritten to gather the credentials before redirecting to the warning page or the real site.

User Enumeration Is A Big Deal

I’ve been participating in bug bounties with BugCrowd and one of the first things I check for is username/email enumeration on the login page and the forgotten password page. Some of the companies running the bug bounties explicitly state that they will not pay for user enumeration vulnerabilities. These companies feel username/email enumeration is a low-risk vulnerability because Phishers target large groups of email addresses instead of email addresses associated with a particular target. In addition, they feel it is low-risk because they believe the account lockout policies they have in place will protect them from password attacks. Unfortunately, neither of these assumptions are correct.

Spammers typically send messages indiscriminantly while phishers typically send their messages to a specific set of targets. As an attacker if I can use your login or forgotten password page to narrow my list from 10000 targets to 1000 targets, I will.

While account lockout policies are a good thing and can prevent certain password guessing attacks they can also be worked around with proper timing. Also, depending on how long the account lockout lasts and whether the account must manually be reset, an attacker could easily cause a DoS for your users or your helpdesk personnel.

Finally, an attacker with a large enough set of valid email addresses would only need to try three or four common passwords with each email address to gain access to a significant number of accounts. These three or four failed password attempts will typically not trigger an account lockout.

Username/email enumeration is not the end of the world but it is certainly something that should be fixed and is typically easy to fix. When a user fails to login, don’t tell the user whether the username or the password failed. Simply say the login attempt failed. When a user submits their username/email to the forgotten password form don’t tell them whether the username/email was found or not. Simply tell them that an email is on the way.

UPDATE 6/13/2014:
A couple of people on Twitter pointed out that there will always be at least one username enumeration vulnerability on sites where users self-register. That vulnerability will be in the account creation process. Another user said this vulnerability is not preventable without ruining the user experience. I’m not a UX guru so I have no idea whether this is true or not.

In either case, the danger of username enumeration comes from the fact that an attacker is able to gather one of the two pieces of information needed to login to the site. If we cannot prevent an attacker from getting half of the login information maybe the answer is to require more login information, ie multi-factor authentication. With proper multi-factor authentication you still run the risk of creating a denial of service by locking out accounts but you eliminate the more dangerous vulnerability of user accounts being compromised on a massive scale.

So, I Wrote a Book

Really it’s more of a training manual. Around June or July of last year I decided I wanted to teach an intro level penetration testing class aimed at system administrators. The purpose of the class would be to teach sysadmins how to attack and hopefully better defend their systems. In the months before the class I wrote a detailed training manual covering all of the topics of the class. The training manual included a number of hands on, step-by-step labs to demonstrate the topics covered. Once the class was over, I decided to publish the training manual. After making a number of revisions based on the feedback from the class and feedback from an editor, I finally published the manual.

I would highly recommend this manual for anyone who is interested in learning penetration testing. It provides a nice overview of the common phases of a penetration test and the common vulnerabilities encountered when doing a penetration test. The manual was originally written to be the basis for an in person training event but can be used as a self-study guide as well. You can get a copy of the book for yourself or for the system administrators in your life here.

I plan to offer more live training events based on the material in the book and would encourage others who want to do live training to use this book as the basis for their own events. If you use the book as the basis for a class, each student will need to purchase his or her own copy. To find out about my next live training event keep an eye on the training page at the ASG Consulting web site.

The labs in the manual require Kali Linux and Metasploitable2 which are both freely available here and here. I would like to thank Offensive Security and Rapid7 for making these great tools available.

On Robots and Sitemaps

Robots.txt and Sitemap.xml files are extremely useful when managing web crawlers but they can also be dangerous when they give away too much information. So what are these files and how are they used?

Many years ago, web site owners and web crawlers developed a method to allow web site owners to politely ask web crawlers not to crawl certain portions of a site. This is done by defining a robots.txt file (Robots Exclusion Standard). For the most part, this standard is adhered to by web crawlers.

A typical robots.txt file looks like this.

#Google Search Engine Robot
User-agent: Googlebot
Allow: /?_escaped_fragment_

Allow: /search?q=%23
Disallow: /search/realtime
Disallow: /search/users
Disallow: /search/*/grid

Disallow: /*?
Disallow: /*/followers
Disallow: /*/following

Disallow: /account/not_my_account

#Yahoo! Search Engine Robot
User-Agent: Slurp
Allow: /?_escaped_fragment_

Allow: /search?q=%23
Disallow: /search/realtime
Disallow: /search/users
Disallow: /search/*/grid

Disallow: /*?
Disallow: /*/followers
Disallow: /*/following

Disallow: /account/not_my_account

This is a portion of Twitter’s robots.txt file. Notice how Twitter tells the search engines which portions of the site are allowed to be crawled and not allowed to be crawled.

Tonight, while surfing the web I found this robots.txt file. I’ll let you guess which site it goes to.


Notice that there are no Disallow directives. Based on the accepted convention, the web site owner gives all web robots permission to crawl the entire site. Theoretically, you could write your own robot and legally crawl the entire site.

Along with the agreement to use robots.txt files, web site owners and web crawlers also decided to use a sitemap.xml file to explicitly define the structure of the web site and the URLs “on a website that are available for crawling” (Sitemaps). The Sitemap directive can be added to the robots.txt file to tell web crawlers where to find the sitemap file.

If we look at the sitemap for we can see the URLs, which by convention, we are EXPECTED to crawl or visit as users.

<sitemapindex xmlns="">

This sitemap file tells us about two additional sitemap files. The file looks interesting.

<urlset xmlns="">

This sitemap file tells us the profile link for a number of user accounts. In fact, it provides links to approximately 3900 user accounts. Again, based on convention, robots are EXPECTED to visit each of these links and download the page at the link. You can see that Google did exactly this by running this query.

So, let’s download a page. When you visit a page in a browser, the page is downloaded and rendered by the browser. When a robot or search engine downloads the page they read and parse the HTML code that makes up the page. To see this HTML code, you can right-click on the page in your browser and choose View Source, or something similar depending on your browser. The source code looks like this.


Note, the source code is full of links to other portions of the site, which, by convention, we are allowed to crawl because the robots.txt file does not define any disallowed portions of the site. One such link, /api/users/ta6q-868x?method=contact, is found about 1/3 of the way down the page. Visiting this page produces an error message in JSON format, which means a web crawler like Google will likely ignore this page.

  "code" : "method_not_allowed",
  "error" : true,
  "message" : "GET not allowed"

On a more serious note, a typical attack against websites includes enumerating user accounts and then attempting to brute-force the associated password. Typically, an attacker has to work to find a method to enumerate user accounts but in this case the sitemap file provides a list of user accounts. Personally, I think it would be wise to remove the sitemap file at

Finding Weak Rails Security Tokens

The other day I was reading about the dangers of having your Rails secret token in your version control system. The TL;DR version is secret tokens are used to calculate the HMAC of the session data in the cookie. If you know the secret token you can send arbitrary session data and execute arbitrary code.

So I decided I’d go digging through Github to see if anyone had uploaded secret tokens to the site. Sure enough, there were more than a few secret tokens. This isn’t all bad because Rails allows different configuration settings in the same application depending on whether the app is in production or development and most of the Rails apps used a strong secret_token read from an environment variable or generated by SecureRandom for the production site but a weak secret_token for development the site.

I took a few minutes to record the secret tokens I found and decided to see if I could find any of them in use on Internet facing sites. To test this I went to Shodan to find Rails servers and found approximately 70,000 servers. I downloaded the details for about 20,000 of those servers and looked at the cookies to identify the ones running Rails apps. Rails cookies are distinct because they consist of a base64 encoded string followed by a — and then a HMAC of the base64 string. This gives a cookie, which looks like this.


Of the roughly 20,000 Rails servers, for which I had details, only about 10,000 had cookies that matched the pattern above.

The digest of the cookie is produced by calculating the HMAC of the base64 string using the SHA1 hashing algorithm and the secret token as the salt. To find the secret token we simply calculate the HMAC using each of the potential secret tokens as the salt and see if the calculated digest matches the digest in the cookie. Of the approximately 10,000 cookies, I was able to find 7 secret tokens. This is not very impressive at all but it gave me hope to try a larger test.

I decided to check the Alexa top 1 million web sites to see how many used a cookie with a digest, and for how many I could find the secret token. I’ve tested about 40,000 sites so far and have only found 303 sites that use a cookie that matches the pattern above. Of those 303 sites, I did not find any of the secret tokens. The results are not surprising and I realize this is a long shot that will probably come to nothing but sometimes you just have to test a theory. If I finish the testing I’ll update the blog post with the final stats.

Although I haven’t tried it yet, I believe that if you ran the same test on an internal network you would have more success because there is more likely to be development Rails servers on an internal network. If you’d like to try this on your network you can get the,, and rails_secret_tokens.text files here. The script takes a list of host names or IP addresses and writes any matching cookies to a file. The script takes a file of cookies and the rails_secret_tokens.txt file and tests each token against each cookie.

If you do find a secret token during your testing, Metasploit will get you remote code execution.