#!/usr/bin/perl
#
#####################################################################
# Outlook Web Access - Address Book Enumeration 
#####################################################################
#
# Copyright (C) 2010 Joe Mondloch
# JoMo-Kun / jmk@foofus.net
#
# This script retrieves all names from the "Find Names" feature in OWA
# using the "galfind" URL query. If target URL contains "finduser", use
# OWAFindUsersOld.pl.
#
# Tested against OWA 2003
# Tested against IIS 5.0/OWA
#
# Notes: verify logon name matches internal exchange username.
#   Ex. user1/pass1 logs in. However, site is https://$host/exchange/firstname_lastname@domain.com/...
#
# Authen:NTLM -> /usr/lib/perl5/vendor_perl/5.8.6/Authen/
#

use HTTP::Cookies;
use LWP::UserAgent;
use Authen::NTLM;
use Getopt::Long;
#use LWP::Debug qw(+);

my $VERSION = "0.4";
my %opt, %data;
my $ua = new LWP::UserAgent(keep_alive=>1);
$ua->ssl_opts( SSL_verify_mode => SSL_VERIFY_NONE );
my $jar = HTTP::Cookies->new();

$opt{'domain'} = '';
$opt{'ssl'} = 0;
$opt{'proxy'} = 0;
$ua->cookie_jar($jar);
push @{ $ua->requests_redirectable }, 'POST';

GetOptions (
  'host=s'      => \$opt{'host'},
  'username=s'  => \$opt{'username'},
  'password=s'  => \$opt{'password'},
  'domain=s'    => \$opt{'domain'},
  'type=s'      => \$opt{'type'},
  'ssl'         => sub { $opt{'ssl'} = 1 },
  'proxy'       => sub { $opt{'proxy'} = 1 },
);

print "\nJoMo-Kun M\$ OWA Find User Enumerator\n\n";

if ( !( defined($opt{'host'}) && defined($opt{'username'}) &&
     defined($opt{'password'}) && defined($opt{'type'}) ) ) {

  print "getOWAnames V. $VERSION\n";
  print "Usage:\n";
  print " $0\n";
  print "   --host <IP Address>\n";
  print "   --username <username>\n";
  print "   --password <password>\n";
  print "   --domain <domain>\n";
  print "   --type <1,2,3>\n";
  print "     1: basic auth mode\n";
  print "     2: basic-auth/owaauth.dll mode\n";
  print "     3: NTLM-auth mode\n";
  print "     4: CookieAuth.dll mode\n";
  print "   --ssl\n";
  print "   --proxy\n";
  print "\n\n";
  exit(1);
}

###############################################################################
# Creating initial connection to OWA
if ($opt{'ssl'}) {
  $opt{'port'} = '443';
  $req = new HTTP::Request GET => "https://$opt{'host'}/exchange/";
} else {
  $opt{'port'} = '80';
  $req = new HTTP::Request GET => "http://$opt{'host'}/exchange/";
}

if ($opt{'type'} eq "3") {
  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";
  $ua->credentials("$opt{'host'}:$opt{'port'}", '', "$opt{'domain'}\\$opt{'username'}", "$opt{'password'}");
} else {
  #$req->authorization_basic($opt{'username'}, $opt{'password'});
  $req->authorization_basic("$opt{'domain'}\\$opt{'username'}", $opt{'password'});
}

if ($opt{'proxy'}) {
  $ua->proxy(['http', 'https'], 'http://localhost:8008/');
}

my $res = $ua->request($req);
$jar->extract_cookies($res);

print "Connecting to OWA: ";
if (($res->is_success) && ($res->code != 302)) { print "Success\n"; }
else { print $res->status_line, "\n"; exit(1); }  

###############################################################################
# Not sure if this is a normal 2003 or installation specific setup...
if ($opt{'type'} eq "2") {
  if ($opt{'ssl'}) {
    $req = new HTTP::Request POST => "https://$opt{'host'}/exchweb/bin/auth/owaauth.dll";
    $req->content("destination=https://$opt{'host'}/exchange/&flags=0&username=$opt{'username'}&password=$opt{'password'}&SubmitCreds=Log+On&trusted=0");
  } else {
    $req = new HTTP::Request POST => "http://$opt{'host'}/exchweb/bin/auth/owaauth.dll";
    $req->content("destination=http://$opt{'host'}/exchange/&flags=0&username=$opt{'username'}&password=$opt{'password'}&SubmitCreds=Log+On&trusted=0");
  }
  
  $req->content_type('application/x-www-form-urlencoded');
  $req->authorization_basic($opt{'username'}, $opt{'password'});
  $res = $ua->request($req);
  $jar->extract_cookies($res);

  print "Submitting Credentials to OWA: ";
  if ($res->is_success) { print "Success\n"; }
  elsif ($res->code == 302) { print "302 Move Temporarily\n"; }
  else { print $res->status_line, "\n"; exit(1); }  
}

###############################################################################
if ($opt{'type'} eq "4") {
  my $path = "CookieAuth.dll?Logon";
  my $user = "";
  if ($opt{'domain'} ne "") { $user = "$opt{'domain'}%5C$opt{'username'}"; }
  else { $user = "$opt{'username'}"; }

  if ($opt{'ssl'}) {
    $req = new HTTP::Request POST => "https://$opt{'host'}/$path";
    $req->referer("https://$opt{'host'}/CookieAuth.dll?GetLogon?url=%2F&reason=0");
    $req->content("destination=Z2F&flags=0&username=$user&password=$opt{'password'}&SubmitCreds=Log+On&trusted=0");
  } else {
    $req = new HTTP::Request POST => "http://$opt{'host'}/$path";
    $req->referer("http://$opt{'host'}/CookieAuth.dll?GetLogon?url=%2F&reason=0");
    $req->content("destination=Z2F&flags=0&username=$user&password=$opt{'password'}&SubmitCreds=Log+On&trusted=0");
  }

  $req->content_type('application/x-www-form-urlencoded');
  $req->authorization_basic($opt{'username'}, $opt{'password'});
  $res = $ua->request($req);
  $jar->extract_cookies($res);

  print "Submitting Credentials to OWA: ";
  if ($res->is_success) { print "Success\n"; }
  elsif ($res->code == 302) { print "302 Move Temporarily\n"; }
  else { print $res->status_line, "\n"; exit(1); }  
}

###############################################################################
# Locate base directory...
# Content-Base: http://111.222.333.444:8888/exchange/FooBar/
# **Directory does not necessarily match username**
if ($res->as_string =~ /Content-Base:/) {
 ($data{'content-base'}) =  $res->as_string =~ /Content-Base:.*$opt{'host'}\/(.*)\//;
 print "Using Content-Base Header: $data{'content-base'}\n";
}


###############################################################################
# Retrieve all user accounts
print "Retrieving OWA user names:\n";

getNames('a'..'z');

# a little recursion to get all those names...
sub getNames {
   my @letters = @_;
   foreach $letter (@letters) {
      if ( ! printNames($letter) ) { 
         foreach ('a'..'z') {
            getNames($letter . $_); 
         } 
      }
   }
}


###############################################################################
# connect to OWA and grab the users
sub printNames {
  my $AliasName = shift @_;
  # Currently matching on "Alias Name". Modify to GET query for different field.
  if ($opt{'ssl'}) {
    #$req = new HTTP::Request GET => "https://$opt{'host'}/$data{'content-base'}/Inbox/?Cmd=galfind&DN=&LN=&FN=&TL=&AN=$AliasName&CP=&DP=&OF=&CY=";
    #$req = new HTTP::Request GET => "https://$opt{'host'}/exchange/someuser/?Cmd=galfind&DN=&LN=&FN=&TL=&AN=$AliasName&CP=&DP=&OF=&CY=";
    $req = new HTTP::Request GET => "https://$opt{'host'}/$data{'content-base'}/?Cmd=galfind&DN=&LN=&FN=&TL=&AN=$AliasName&CP=&DP=&OF=&CY=";
  } else {
    $req = new HTTP::Request GET => "http://$opt{'host'}/$data{'content-base'}/Inbox/?Cmd=galfind&DN=&LN=&FN=&TL=&AN=$AliasName&CP=&DP=&OF=&CY=";
  }

  if ($opt{'proxy'}) {
    $ua->proxy(['http', 'https'], 'http://localhost:8008/');
  }

  if ($opt{'type'} eq "3") {
    $ua->credentials("$opt{'host'}:$opt{'port'}", "", "$opt{'domain'}\\$opt{'username'}", $opt{'password'});
  } else {
    $req->authorization_basic("$opt{'domain'}\\$opt{'username'}", $opt{'password'});
    #$req->authorization_basic($opt{'username'}, $opt{'password'});
  }

  $jar->add_cookie_header($req);
  my $res = $ua->request($req);
  $jar->extract_cookies($res);

  if (($res->is_success) && ($res->code != 302)) { }
  else { print $res->status_line, "\n"; exit(1); }  
  
  @results = split /\n/, $res->content;

  if (grep /This query would return too many addresses!/, @results) { return 0; }
  else {
    for (my $i=0; $i <= $#results; $i++) { 
      next unless $results[$i] =~ /<tr><td><font SIZE="2">/i;

      my @row = split /<\/tr><tr>/, $results[$i];
        foreach(@row) {
          chomp;
          my @col = split /<\/td><td>/i;
          my ($name) = $col[0] =~ /<td><font SIZE="2">(.*)<\/font>/i;
          my ($phone) = $col[1] =~ /<font SIZE="2">(.*)<\/font>/i; 
          my ($office) = $col[2] =~ /<font SIZE="2">(.*)<\/font>/i; 
          my ($title) = $col[3] =~ /<font SIZE="2">(.*)<\/font>/i; 
          my ($company) = $col[4] =~ /<font SIZE="2">(.*)<\/font>/i; 
          my ($userid) = $col[5] =~ /<font SIZE="2">(.*)<\/font>/i; 
          print $name, "::", $phone, "::", $office, "::", $title, "::", $company, "::", $userid, "\n";
       }
       last;
    }
    return 1;
  }
}
