From 738c704ec02f5835176ff29d3df1a4aaf6577748 Mon Sep 17 00:00:00 2001 From: Roman Maksimov Date: Sun, 19 Apr 2020 05:14:48 +0300 Subject: [PATCH 1/2] add misc changes --- autodiscover/autodiscover.go | 7 +++++++ mapi/mapi.go | 11 +++++++++-- rpc-http/rpctransport.go | 11 ++++------- ruler.go | 38 +++++++++++++++++------------------- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/autodiscover/autodiscover.go b/autodiscover/autodiscover.go index 1553c70..e4396fc 100644 --- a/autodiscover/autodiscover.go +++ b/autodiscover/autodiscover.go @@ -161,9 +161,16 @@ func GetRPCHTTP(email, autoURLPtr string, resp *utils.AutodiscoverResp) (*utils. if resp.Response.Account.MicrosoftOnline == true { lindex := strings.LastIndex(resp.Response.Account.Protocol[0].MailStore.ExternalUrl, "=") user = resp.Response.Account.Protocol[0].MailStore.ExternalUrl[lindex+1:] + if user == "" { + return nil, "", "", "", false, fmt.Errorf("The user is undefined") + } + url = "https://outlook.office365.com" } + + return nil, "", "", "", false, fmt.Errorf("The user is undefined") } + RPCURL := fmt.Sprintf("%s/rpc/rpcproxy.dll?%s:6001", url, user) utils.Trace.Printf("RPC URL set: %s\n", RPCURL) diff --git a/mapi/mapi.go b/mapi/mapi.go index 6425753..2059b39 100644 --- a/mapi/mapi.go +++ b/mapi/mapi.go @@ -131,12 +131,16 @@ 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.Printf("%s\n", hex.Dump(rawResp)) + if rawResp != nil { + utils.Debug.Printf("%s\n", hex.Dump(rawResp)) + } return nil, err } } else { if rawResp, err = mapiRequestRPC(mapi); err != nil { - utils.Debug.Printf("%s\n", hex.Dump(rawResp)) + if rawResp != nil { + utils.Debug.Printf("%s\n", hex.Dump(rawResp)) + } return nil, err } } @@ -329,6 +333,9 @@ func specialFolders(folderResponse []byte) { func readResponse(headers http.Header, body []byte) ([]byte, error) { //check to see that the response code was 0, which indicates protocol success if headers.Get("X-ResponseCode") != "0" { + if headers.Get("X-ResponseCode") == "10" { + utils.Error.Println("This is a server-side issue. See details at https://github.com/sensepost/ruler/issues/75") + } return nil, fmt.Errorf("Got a protocol error response: %s", headers.Get("X-ResponseCode")) } //We need to parse out the body to get rid of the meta-tags and additional headers (if any) diff --git a/rpc-http/rpctransport.go b/rpc-http/rpctransport.go index afeaad5..64d388f 100644 --- a/rpc-http/rpctransport.go +++ b/rpc-http/rpctransport.go @@ -172,17 +172,17 @@ func setupHTTP(rpctype string, URL string, ntlmAuth bool, full bool) (net.Conn, } //RPCOpen opens HTTP for RPC_IN_DATA and RPC_OUT_DATA -func RPCOpen(URL string, readySignal chan bool, errOccurred chan error) (err error) { +func RPCOpen(URL string, readySignal chan bool, errOccurred chan error) { //I'm so damn frustrated at not being able to use the http client here //can't find a way to keep the write channel open (other than going over to http/2, which isn't valid here) //so this is some damn messy code, but screw it + var err error rpcInConn, err = setupHTTP("RPC_IN_DATA", URL, AuthSession.RPCNtlm, true) if err != nil { readySignal <- false errOccurred <- err - return err } //open the RPC_OUT_DATA channel, receive a "ready" signal when this is setup @@ -195,7 +195,6 @@ func RPCOpen(URL string, readySignal chan bool, errOccurred chan error) (err err readySignal <- true } else { readySignal <- false - return err } case <-time.After(time.Second * 5): // call timed out readySignal <- true @@ -212,19 +211,18 @@ func RPCOpen(URL string, readySignal chan bool, errOccurred chan error) (err err break } } - return nil } //RPCOpenOut function opens the RPC_OUT_DATA channel //starts our listening "loop" which scans for new responses and pushes //these to our list of recieved responses -func RPCOpenOut(URL string, readySignal chan<- bool, errOccurred chan<- error) (err error) { +func RPCOpenOut(URL string, readySignal chan<- bool, errOccurred chan<- error) { + var err error rpcOutConn, err = setupHTTP("RPC_OUT_DATA", URL, AuthSession.RPCNtlm, true) if err != nil { readySignal <- false errOccurred <- err - return err } scanner := bufio.NewScanner(rpcOutConn) @@ -260,7 +258,6 @@ func RPCOpenOut(URL string, readySignal chan<- bool, errOccurred chan<- error) ( } } - return nil } //RPCBind function establishes our session diff --git a/ruler.go b/ruler.go index f038313..04ebcfa 100644 --- a/ruler.go +++ b/ruler.go @@ -39,7 +39,6 @@ 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") } @@ -103,7 +102,6 @@ func discover(c *cli.Context) error { //var resp *utils.AutodiscoverResp var domain string - if c.Bool("mapi") == true { _, domain, err = autodiscover.MAPIDiscover(url) } else { @@ -174,6 +172,7 @@ func brute(c *cli.Context) error { } else { autodiscover.UserPassBruteForce() } + return nil } @@ -187,7 +186,6 @@ func addRule(c *cli.Context) error { } utils.Info.Println("Rule Added. Fetching list of rules...") - printRules() if c.Bool("send") { @@ -261,13 +259,13 @@ func deleteRule(c *cli.Context) error { func displayRules(c *cli.Context) error { utils.Info.Println("Retrieving Rules") er := printRules() + return er } //sendMessage sends a message to the user, using their own Account //uses supplied subject and body func sendMessage(subject, body string) error { - propertyTags := make([]mapi.PropertyTag, 1) propertyTags[0] = mapi.PidTagDisplayName @@ -287,6 +285,7 @@ func sendMessage(subject, body string) error { //Function to connect to the Exchange server func connect(c *cli.Context) error { var err error + //if no password or hash was supplied, read from stdin if c.GlobalString("password") == "" && c.GlobalString("hash") == "" && c.GlobalString("config") == "" { fmt.Printf("Password: ") @@ -303,6 +302,7 @@ func connect(c *cli.Context) error { return fmt.Errorf("Invalid hash provided. Hex decode failed") } } + //setup our autodiscover service config.Domain = c.GlobalString("domain") config.User = c.GlobalString("username") @@ -379,14 +379,13 @@ func connect(c *cli.Context) error { autodiscover.SessionConfig = &config + //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 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") != "" { var yamlConfig utils.YamlConfig if yamlConfig, err = utils.ReadYml(c.GlobalString("config")); err != nil { @@ -433,10 +432,9 @@ func connect(c *cli.Context) error { } else { mapiURL = fmt.Sprintf("%s?MailboxId=%s", yamlConfig.MapiURL, yamlConfig.Mailbox) } - userDN = yamlConfig.UserDN + userDN = yamlConfig.UserDN } 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") } @@ -467,7 +465,6 @@ func connect(c *cli.Context) error { 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) @@ -476,9 +473,7 @@ func connect(c *cli.Context) error { autodiscover.CreateCache(config.Email, rawAutodiscover) //store the autodiscover for future use } } - } else { - if config.User == "" && config.Email == "" { return fmt.Errorf("Missing username and/or email argument. Use --domain (if needed), --username and --email or the --config") } @@ -521,6 +516,7 @@ func connect(c *cli.Context) error { propertyTags[1] = mapi.PidTagSubfolders mapi.GetFolder(mapi.INBOX, propertyTags) //Open Inbox } + return nil } @@ -532,7 +528,6 @@ func printRules() error { //cols[2] = mapi.PidTagRuleActions rows, er := mapi.FetchRules(cols) - if er != nil { return er } @@ -575,8 +570,9 @@ func printRules() error { } utils.Info.Println() } else { - utils.Info.Printf("No Rules Found\n") + utils.Info.Println("No Rules Found") } + return nil } @@ -623,6 +619,7 @@ func abkList(c *cli.Context) error { } } } + return nil } @@ -669,6 +666,7 @@ func abkDump(c *cli.Context) error { } } } + return nil } @@ -770,6 +768,7 @@ func triggerForm(c *cli.Context) error { return err } utils.Info.Println("Email sent! Hopefully you will have a shell soon.") + return nil } @@ -792,6 +791,7 @@ func displayForms(c *cli.Context) error { utils.Error.Println("Failed to find any forms.") return err } + return nil } @@ -846,6 +846,7 @@ func displayHomePage() error { utils.Info.Println("Webview is set as ENABLED") } } + return e } @@ -893,7 +894,6 @@ func deleteHomePage() error { } func searchFolders(c *cli.Context) error { - utils.Info.Println("Checking if a search folder exists") searchFolderName := "searcher" @@ -992,7 +992,6 @@ func searchFolders(c *cli.Context) error { } func checkFolder(folderName string) ([]byte, error) { - var folderID []byte propertyTags := make([]mapi.PropertyTag, 2) propertyTags[0] = mapi.PidTagDisplayName @@ -1222,7 +1221,7 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` }, cli.BoolFlag{ Name: "verbose", - Usage: "Be verbose and show some of thei inner workings", + Usage: "Be verbose and show some of the inner workings", }, cli.BoolFlag{ Name: "debug", @@ -1759,7 +1758,7 @@ 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) + return cli.NewExitError("You need to supply a valid search term. Use --term", 1) } err := connect(c) if err != nil { @@ -1779,5 +1778,4 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` } app.Run(os.Args) - } From bc7fa08038de2a915349a78b31888f43c197f983 Mon Sep 17 00:00:00 2001 From: Roman Maksimov Date: Sun, 19 Apr 2020 05:21:04 +0300 Subject: [PATCH 2/2] add --useragent option shortcut --- ruler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruler.go b/ruler.go index 04ebcfa..46847b8 100644 --- a/ruler.go +++ b/ruler.go @@ -1191,7 +1191,7 @@ A tool by @_staaldraad from @sensepost to abuse Exchange Services.` Usage: "If you need to use an upstream proxy. Works with https://user:pass@ip:port or https://ip:port", }, cli.StringFlag{ - Name: "useragent", + Name: "useragent,ua", Value: "ruler", Usage: "Custom User-Agent string", },