Skip to content

Commit

Permalink
Add project
Browse files Browse the repository at this point in the history
  • Loading branch information
nodauf committed Jul 8, 2021
1 parent 5cc6efb commit f4a0bef
Show file tree
Hide file tree
Showing 11 changed files with 980 additions and 0 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: goreleaser

on:
push:
tags:
- v*.*

permissions:
contents: write

jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
-
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
distribution: goreleaser
version: latest
args: release --rm-dist
workdir: src/
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# UserEnumTeams

## Description

Sometimes user enumeration could be sometimes useful during the reconnaissance of an assessment. This tool will determine if an email is registered on teams or not.

## Usage

```
> .\UserEnumTeams userenum --help
Users can be enumerated on Microsoft Teams with the search features.
This tool validates an email address or a list of email addresses.
If these emails exist the presence of the user is retrieved as well as the device used to connect
Usage:
UserEnumTeams userenum [flags]
Flags:
-e, --email string Email address
-f, --file string File containing the email address
-h, --help help for userenum
-t, --token string Bearer token (only the base64 part: eyJ0...)
Global Flags:
-v, --verbose Verbose
```

```
.\UserEnumTeams userenum -u emails.txt -t eyJ0eXAiOiJKV1QiLCJub25jZSI6IlpNc3FVTnJDeUJaYTBJZ3RXSmFsNUZWVjRU……vKiXYtCir3GJ9rMPAhPXiXSzSMeOPiSaM7SDoCg
```

![User enumeration](./images/enumeration-users.png)

```
.\UserEnumTeams userenum -u emails.txt -t eyJ0eXAiOiJKV1QiLCJub25jZSI6IlpNc3FVTnJDeUJaYTBJZ3RXSmFsNUZWVjRU……vKiXYtCir3GJ9rMPAhPXiXSzSMeOPiSaM7SDoCg -v
```

![User enumeration verbose](./images/enumeration-users-verbose.png)
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module TeamsUserEnum

go 1.16

require (
github.com/fatih/color v1.12.0
github.com/spf13/cobra v1.1.3
)
654 changes: 654 additions & 0 deletions go.sum

Large diffs are not rendered by default.

Binary file added images/enumeration-users-verbose.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/enumeration-users.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions src/.goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
builds:
- goos:
- darwin
- linux
- windows
goarch:
- amd64
- arm64
archives:
-
format_overrides:
- goos: windows
format: zip
27 changes: 27 additions & 0 deletions src/cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package cmd

import (
"fmt"
"os"

"github.com/spf13/cobra"
)

var verbose bool

var rootCmd = &cobra.Command{
Use: "UserEnumTeams",
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

func init() {
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Verbose")
}
47 changes: 47 additions & 0 deletions src/cmd/userenum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package cmd

import (
"errors"

"TeamsUserEnum/src/teams"

"github.com/spf13/cobra"
)

var emailFile string
var email string
var token string

// userenumCmd represents the userenum command
var userenumCmd = &cobra.Command{
Use: "userenum",
Short: "User enumeration on Microsoft Teams",
Long: `Users can be enumerated on Microsoft Teams with the search features.
This tool validates an email address or a list of email addresses.
If these emails exist the presence of the user is retrieved as well as the device used to connect`,
PreRunE: func(cmd *cobra.Command, args []string) error {
if emailFile == "" && email == "" {
return errors.New("Argument -f or -e required")
} else if emailFile != "" && email != "" {
return errors.New("Only argument -f or -e should be specified")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
token = "Bearer " + token
if email != "" {
teams.Enumuser(email, token, verbose)
} else {
teams.Parsefile(emailFile, token, verbose)
}
},
}

func init() {
rootCmd.AddCommand(userenumCmd)

userenumCmd.Flags().StringVarP(&emailFile, "file", "f", "", "File containing the email address")
userenumCmd.Flags().StringVarP(&email, "email", "e", "", "Email address")
userenumCmd.Flags().StringVarP(&token, "token", "t", "", "Bearer token (only the base64 part: eyJ0...)")
userenumCmd.MarkFlagRequired("token")
}
137 changes: 137 additions & 0 deletions src/teams/teams.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package teams

import (
"bufio"
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"reflect"
"strconv"
"strings"

color "github.com/fatih/color"
)

var URL_PRESENCE_TEAMS = "https://presence.teams.microsoft.com/v1/presence/getpresence/"
var URL_TEAMS = "https://teams.microsoft.com/api/mt/emea/beta/users/%s/externalsearchv3"
var CLIENT_VERSION = "27/1.0.0.2021011237"

// Enumuser request the Teams API to retrieve information about the email
func Enumuser(email string, bearer string, verbose bool) error {

url := fmt.Sprintf(URL_TEAMS, email)
req, err := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", bearer)
req.Header.Add("x-ms-client-version", CLIENT_VERSION)

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Println("Error on response.\n[ERRO] -", err)
}

body, _ := ioutil.ReadAll(resp.Body)

var jsonInterface interface{}
var usefulInformation []struct {
DisplayName string `json:"displayName"`
Mri string `json:"mri"`
}

json.Unmarshal([]byte(body), &jsonInterface)
json.Unmarshal([]byte(body), &usefulInformation)

if verbose {

fmt.Println("Email: " + email)
fmt.Println("Status code: " + strconv.Itoa(resp.StatusCode))
fmt.Println("Response: ")

bytes, _ := json.MarshalIndent(jsonInterface, "", " ")
fmt.Println(string(bytes))
}

if resp.StatusCode == 200 {
if reflect.ValueOf(jsonInterface).Len() > 0 {
presence, device := getPresence(usefulInformation[0].Mri, bearer, verbose)
color.Green("[+] " + email + " - " + usefulInformation[0].DisplayName + " - " + presence + " - " + device)
} else {
fmt.Println("[-] " + email)
}
} else if resp.StatusCode == 403 {
color.Green("[+] " + email)
} else if resp.StatusCode == 401 {
fmt.Println("[-] " + email)
fmt.Println("The token may be invalid or expired. The status code returned by the server is 401")
return errors.New(string(resp.StatusCode))
} else {
fmt.Println("[-] " + email)
fmt.Println("Something went wrong. The status code returned by the server is " + strconv.Itoa(resp.StatusCode))
return errors.New(string(resp.StatusCode))
}

return nil

}

// Parsefile will call the function Enumuser with the line as email's argument
func Parsefile(filenPath string, bearer string, verbose bool) {
file, err := os.Open(filenPath)
if err != nil {
log.Fatalf("failed to open")

}
scanner := bufio.NewScanner(file)

scanner.Split(bufio.ScanLines)
for scanner.Scan() {
line := scanner.Text()
email := string(bytes.Trim([]byte(line), "\x00"))

email = strings.ToValidUTF8(email, "")
email = strings.Trim(email, "\r")
email = strings.Trim(email, "\n")
Enumuser(email, bearer, verbose)

}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
file.Close()
}

// getPresence request the Teams API to get additional details about the user with its mri
func getPresence(mri string, bearer string, verbose bool) (string, string) {

var json_data = []byte(`[{"mri":"` + mri + `"}]`)
req, err := http.NewRequest("POST", URL_PRESENCE_TEAMS, bytes.NewBuffer(json_data))
req.Header.Add("Authorization", bearer)
req.Header.Add("x-ms-client-version", CLIENT_VERSION)
req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Println("Error on response.\n[ERRO] -", err)
}

body, _ := ioutil.ReadAll(resp.Body)

var status []struct {
Mri string `json:"mri"`
Presence struct {
Availability string `json:"availability"`
DeviceType string `json:"deviceType"`
} `json:"presence"`
}

json.Unmarshal([]byte(body), &status)

return status[0].Presence.Availability, status[0].Presence.DeviceType

}
22 changes: 22 additions & 0 deletions src/userEnum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
Copyright © 2021 NAME HERE <EMAIL ADDRESS>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main

import cmd "TeamsUserEnum/src/cmd"

func main() {
cmd.Execute()
}

0 comments on commit f4a0bef

Please sign in to comment.