#!/usr/bin/env perl
#
#####################################################################
# Outlook Web Access Light - Address Book Enumeration 
#####################################################################
#
# Copyright (C) 2010 Joe Mondloch
# JoMo-Kun / jmk@foofus.net
#
# This script retrieves all names from the OWA Light Address Book
#

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

$|++; # don't buffer

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

$opt{'domain'} = '';
$opt{'ssl'} = 0;
$opt{'proxy'} = 0;
$ua->cookie_jar($jar);

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: CookieAuth.dll?Logon?GetLogon?url=Z2F (** Z2F **)\n";
  print "     2: owa/auth/owaauth.dll (Outlook Light w/ ISA 2007)\n";
  print "     3: owa/auth.owa (Outlook Web App w/ Exchange 2010)\n";
  print "     4: basic auth only\n";
  print "   --ssl\n";
  print "   --proxy\n";
  print "\n\n";
  exit(1);
}

###############################################################################
# Creating initial connection to OWA
if ($opt{'proxy'}) {
  $ua->proxy(['http', 'https'], 'http://localhost:8008/');
}

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

$req->user_agent('Mozilla/5.0');
$jar->add_cookie_header($req);
if ($opt{'domain'} ne "") {
  print STDERR "Basic Auth: $opt{'domain'}\\$opt{'username'} $opt{'password'}\n";
  $req->authorization_basic("$opt{'domain'}\\$opt{'username'}", $opt{'password'});
} else { 
  $req->authorization_basic($opt{'username'}, $opt{'password'});
}
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";

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


###############################################################################
# Authenticate to OWA
  if ($opt{'domain'} ne "") { $user = "$opt{'domain'}%5C$opt{'username'}"; }
  else { $user = $opt{'username'}; }
 
  if ($opt{'type'} eq "1") {
    if ($opt{'ssl'}) {
      $req = new HTTP::Request POST => "https://$opt{'host'}/CookieAuth.dll?Logon";
      $req->referer("https://$opt{'host'}/CookieAuth.dll?GetLogon?curl=Z2F&reason=2&formdir=1");
    } else {
      $req = new HTTP::Request POST => "http://$opt{'host'}/CookieAuth.dll?Logon";
      $req->referer("http://$opt{'host'}/CookieAuth.dll?GetLogon?curl=Z2F&reason=2&formdir=1");
    }

    #$req->content("curl=Z2FExchangeZ2F&flags=0&forcedownlevel=0&formdir=3&trusted=0&username=$user&password=$opt{'password'}&SubmitCreds=Log+On");
    #$req->content("curl=Z2FowaZ2F&flags=0&forcedownlevel=0&formdir=3&trusted=0&username=$user&password=$opt{'password'}&SubmitCreds=Log+On");
    $req->content("curl=Z2FowaZ2F&flags=0&forcedownlevel=0&formdir=1&trusted=0&username=$user&password=$opt{'password'}&SubmitCreds=Log+On");
  }
  elsif ($opt{'type'} eq "2") {
    # Outlook Web Access Light w/ Microsoft ISA 2007
    print "Type 2: Outlook Web Access Light w/ Microsoft ISA 2007\n";
    $path = "owa/auth/owaauth.dll";
    if ($opt{'ssl'}) {
      $req = new HTTP::Request POST => "https://$opt{'host'}/$path";
      $req->referer("https://$opt{'host'}/owa/auth/logon.aspx?url=https://$host/owa/&reason=0");
      $req->content("destination=https%3A%2F%2F$opt{'host'}%2Fowa%2F&flags=0&forcedownlevel=0&trusted=0&username=$user&password=$opt{'password'}&isUtf8=1");
    } else {
      $req = new HTTP::Request POST => "http://$opt{'host'}/$path";
      $req->referer("http://$host/owa/auth/logon.aspx?url=https://$opt{'host'}/owa/&reason=0");
      $req->content("destination=http%3A%2F%2F$opt{'host'}%2Fowa%2F&flags=0&forcedownlevel=0&trusted=0&username=$user&password=$opt{'password'}&isUtf8=1");
    }
  }
  elsif ($opt{'type'} eq "3") {
    # Outlook Web App w/ Exchange 2010
    print "Type 3: Outlook Web App w/ Exchange 2010\n";
    $path = "owa/auth.owa";
    if ($opt{'ssl'}) { 
      $req = new HTTP::Request POST => "https://$opt{'host'}/$path";
      $req->referer("https://$opt{'host'}/owa/auth/logon.aspx?replaceCurrent=1&reason=2&url=https%3a%2f%2f$opt{'host'}%2fowa%2f");
      $req->content("destination=https%3A%2F%2F$opt{'host'}%2Fowa%2F&flags=0&forcedownlevel=0&trusted=0&username=$user&password=$opt{'password'}&isUtf8=1");
    } else {
      $req = new HTTP::Request POST => "http://$opt{'host'}/$path";
      $req->referer("http://$opt{'host'}/owa/auth/logon.aspx?replaceCurrent=1&reason=2&url=http%3a%2f%2f$opt{'host'}%2fowa%2f");
      $req->content("destination=http%3A%2F%2F$opt{'host'}%2Fowa%2F&flags=0&forcedownlevel=0&trusted=0&username=$user&password=$opt{'password'}&isUtf8=1");
    }

    $req->header(Cookie => "PBack=0");
  }
  elsif ($opt{'type'} eq "4") {
    $req->authorization_basic($opt{'username'}, $opt{'password'});
  } 

$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);

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


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

my $page = 1;
while(1) {

  # sch=b --> useful?
  # <option value="?ae=Folder&t=IPF.Contact&newSch=1&scp=1">Contacts</option>
  # <option value="?ae=Dialog&t=AddressBook&ab=Ad%3b2onjoiYIJ06IiyPfuqJg0w%3d%3d&ctx=1">Address Book</option>
  # <option value="?ae=Dialog&t=AddressBook&ab=Ad%3bYIAR79W3HkGePt%2fLAjqfXA%3d%3d&ctx=1">. All Address Lists</option>
  # <option value="?ae=Dialog&t=AddressBook&ab=Ad%3bldlj2UzXlE%2bdm7xAiK0pMw%3d%3d&ctx=1" selected>. All Contacts</option>
  # <option value="?ae=Dialog&t=AddressBook&ab=Ad%3bYXMO5Faw3kK13fHwMj3p%2fg%3d%3d&ctx=1">. All Groups</option>
  # <option value="?ae=Dialog&t=AddressBook&ab=Ad%3b89WUs85SZUylD0XyvLfx%2fg%3d%3d&ctx=1">. All Rooms</option>
  # <option value="?ae=Dialog&t=AddressBook&ab=Ad%3bogGbWnFBp0G6JRpVU%2btpJQ%3d%3d&ctx=1">. All Users</option>
  # <option value="?ae=Dialog&t=AddressBook&ab=Ad%3bGIxfmlNA60a1WmjeXtYY6A%3d%3d&ctx=1">. Corporate</option>
  # <option value="?ae=Dialog&t=AddressBook&ab=Ad%3bHap%2fKybHhkWcLi%2f0mn6e1Q%3d%3d&ctx=1">. Financial Services</option>
  # <option value="?ae=Dialog&t=AddressBook&ab=Ad%3b3UqyPe9gk0W2BJehIl07Mw%3d%3d&ctx=1">. Homebuilders</option>
  #<option value="?ae=Dialog&t=AddressBook&ab=Ad%3bO4qWvzp61UWXiTyeDLx9Eg%3d%3d&ctx=1">. Public Folders</option>

  if ($page == 1) {
    if ($opt{'ssl'}) { $req = new HTTP::Request GET => "https://$opt{'host'}/OWA/?ae=Dialog&t=AddressBook&ctx=1&sch=b"; }
    else             { $req = new HTTP::Request GET => "http://$opt{'host'}/OWA/?ae=Dialog&t=AddressBook&ctx=1&sch=b"; }

  } 
  else {
    if ($opt{'ssl'}) { 
      $req = new HTTP::Request POST => "https://$opt{'host'}/OWA/?ae=Dialog&t=AddressBook&ctx=1&sch=b";
      $req->referer("https://$opt{'host'}/OWA/?ae=Dialog&t=AddressBook&ctx=1&sch=b");
    } else { 
      $req = new HTTP::Request POST => "http://$opt{'host'}/OWA/?ae=Dialog&t=AddressBook&ctx=1&sch=b";
      $req->referer("http://$opt{'host'}/OWA/?ae=Dialog&t=AddressBook&ctx=1&sch=b");
    }    

    $req->content("hidpnst=&hidss=&hidri=&hidchk=&hidAB=$hidAB&hidpg=$page&hidcid=12&hidso=1&hidid=&hidcmdpst=addrcp&hidrcptid=&hidrw=1&hidpid=AddressBook&hidcanary=$hidcanary");
    $req->content_type('application/x-www-form-urlencoded');
    $req->user_agent('Mozilla/5.0');
  }
 
  $jar->add_cookie_header($req);
  $req->authorization_basic($opt{'username'}, $opt{'password'});
  my $res = $ua->request($req);
  $jar->extract_cookies($res);

  print "Retrieving address book entries from page $page: ";
  if (($res->is_success) && ($res->code != 302)) { print "Success\n"; }
  else { print $res->status_line, "\n"; exit(1); }  

  # Extract hidAB and hidcanary values
  if ($page == 1) {
    # <input type="hidden" name="hidAB" value="Ad;GwwtQrRi1UO2Ic0d3KFWBg==">
    # input type=hidden name="hidcanary" value="cc396cfc71894ed680a485752208b89d">
    if ($res->as_string =~ /name="hidAB" value="(.*)">/) { 
      $hidAB = $1; 
      $hidAB =~ s/;/%3B/;  
      $hidAB =~ s/==/%3D%3D/g;  
      print "hidAB: $hidAB\n"; 
    }

    if ($res->as_string =~ /name="hidcanary" value="(.*)">/) { $hidcanary = $1; print "hidcanary: $hidcanary\n"; }
  }

  @results = split /\n/, $res->content;

  if (grep /There are no items to show in this view./, @results) { exit(0); }
  else {
    for (my $i=0; $i <= $#results; $i++) {
      next unless $results[$i] =~ /<caption>Content Area<\/caption>/i;

      my @row = split /<a href="#" id="/, $results[$i];
      $row[0] =~ s/^.*name="chkRcpt" value="//;
      foreach(@row) {
        chomp;
        my ($id) = $_ =~ /^(.*?)" /;
        my @col = split /<\/td><td/i;
        my ($name) = $col[0] =~ /;">(.*)<\/a><\/h1>/i;
        my ($userid) = $col[1] =~ /nowrap>(.*)&nbsp;/i;
        my ($phone) = $col[2] =~ /nowrap>(.*)&nbsp;/i;
        my ($office) = $col[3] =~ /nowrap>(.*)&nbsp;/i;
        my ($title) = $col[4] =~ /nowrap>(.*)&nbsp;/i;
        my ($company) = $col[5] =~ /nowrap>(.*?)&nbsp;/i;
        my ($email) = "";
        my ($department) = "";

        if (/Distribution List/) { 
          $name =~ s/^.*alt="Distribution List">//;
          $name = $name . " (Distribution List)";
        } else {
          # retrieve user's full information
          $id =~ s/\+/%2b/g;
          $id =~ s/\//%2f/g;
          $id =~ s/=/%3d/g;
        
          if ($opt{'ssl'}) { 
            $req = new HTTP::Request GET => "https://$opt{'host'}//owa/?ae=Item&t=AD.RecipientType.User&id=$id&ctx=1";
          } else { 
            $req = new HTTP::Request GET => "http://$opt{'host'}//owa/?ae=Item&t=AD.RecipientType.User&id=$id&ctx=1";
          }    

          $req->user_agent('Mozilla/5.0');
          $jar->add_cookie_header($req);
          $req->authorization_basic($opt{'username'}, $opt{'password'});
          my $res = $ua->request($req);
          $jar->extract_cookies($res);

          print "Retrieving information for user ID $id\n";
          if (($res->is_success) && ($res->code != 302)) {
            ($email) = $res->as_string =~ / nowrap>E-mail<\/td><td class="txvl">(.*?)<\/td>/;
            ($department) = $res->as_string =~ / nowrap>Department<\/td><td class="txvl">(.*?)<\/td>/;
          }
          else { print $res->status_line, "\n"; }  
        }
        
        print $name, "::", $userid, "::", $phone, "::", $office, "::", $title, "::", $company, "::", $email, "::", $department, "\n";
      }
      last;
    }
  }

  $page++;
}
