Skip to content

Commit

Permalink
Merge pull request #7 from BountyStrike/feature/Custom-Webhooks
Browse files Browse the repository at this point in the history
Feature/custom webhooks
  • Loading branch information
dubs3c authored Jan 29, 2021
2 parents 87fa00c + 30dfe6a commit 86027af
Show file tree
Hide file tree
Showing 9 changed files with 390 additions and 402 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
bin/
dist/
*.tmp
.vscode/
*.exe
91 changes: 62 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,29 @@ Usage:
emissary [channel] [message]
Options:
-s, --slack Send via Slack
-t, --telegram Send via Telegram
-e, --email Send via Email
-ms, --teams Send via Microsoft Teams
-si, --stdin Get message from stdin
-m, --message Message to send
-r, --rows Max rows/lines to send, 0 for unlimited. Default 20
-v, --version Show version
-ch, --channel Specify a custom channel you have defined emissary.ini
-in, --inline Specify channel directly in the commandline
-m, --message Message to send
-si, --stdin Get message from stdin
-e, --email Send via Email
-txt, --text Specify the field that contains the message. Default is 'message'
-d, --data Specify additional data in json format that should be sent
-r, --rows Max rows/lines to send, 0 for unlimited. Default 20
-v, --version Show version
Examples:
emissary -telegram --message "Hello telegram"
cat domins.txt | emissary --slack --stdin
emissary --channel Telegram --message "Hello telegram"
cat domins.txt | emissary -ch Slack --stdin
emissary -ch Discord -ch Telegram -m "Your message"
emissary -in "webhook:=https://api.telegram.org/botxxxxx/sendMessage§data:={'chat_id': 'xxxx'}" -in "webhook:=https://hooks.slack.com/services/xxxxx" -m "Hack the planet!"
```

**Create ~/.config/emissary.ini with the following:**
### Create ~/.config/emissary.ini with the following:
```
[Telegram]
chat_id=xxxxxx
api_key=xxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
webhook=https://api.telegram.org/botxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxx/sendMessage
textField=text
data={"chat_id": "xxxxxx"}
[Slack]
webhook=https://hooks.slack.com/services/xxxxxxxxxx/xxxxxxxxxx/xxxxxxxxxx
Expand All @@ -53,47 +57,76 @@ server=smtp.gmail.com
port=587
subject="New domains found!"
```

*When using gmail, you need to activate less secure apps on your account: [https://myaccount.google.com/lesssecureapps](https://myaccount.google.com/lesssecureapps)*

Now you can start using emissary :)

**Pipe data via stdin:**

### Custom Webhooks

It's possible to add your own channels as well, adding Discord as a custom channel looks like this:

```
[Discord]
webhook=https://discord.com/api/webhooks/xxxxxxxxxxxxxxxxxxxxxxxxxx
textField=content
```

And can be executed with `emissary --channel Discord -m "It works!!!"`.

The following fields can be used for a given channel:

| field | description |
| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| webhook | The actual webhook to send data to |
| textField | Some API's have a specific json key where the message goes, here you can define that. Default key is `text`, e.g. `{"text": "Your message"}`. |
| data | If you want to send additional data, you can specify that here as a json formatted string, e.g. `data={"someKey": "someValue", "otherKey": "otherValue"}`. |


### Pipe data via stdin:
```
$ cat domains.txt | emissary --telegram --stdin
```

**Specify a message as an argument:**
### Specify a message as an argument:
```
$ emissary --telegram --message "This is a very cool message"
```

**Send to multiple channels:**
### Send to multiple channels:
```
$ cat domains.txt | emissary -t -s -si
```

**Send only 10 lines:**
### Send only 10 lines:
```
$ cat domains.txt | emissary -t -si --rows 10
```

**Send everything from the file:**
### Send everything from the file:
```
$ cat domains.txt | emissary -t -si -r 0
```

Right now the Emissary will only deliver 20 rows, to protect against accidentally sending a gazillion domains :)
Emissary will only send 20 rows by default, this is to protect against accidentally sending a gazillion domains :) It can be overwritten with `--rows 0` which means unlimited rows.

### Multiple inline webhooks

It's possible use multiple webhooks directly on the command line without specifying them in `config.ini`.

The following command will send `Hack the planet` to Telegram and Slack:

```
emissary -in "webhook:=https://api.telegram.org/botxxxxx/sendMessage§data:={'chat_id': 'xxxx'}" -in "webhook:=https://hooks.slack.com/services/xxxxx" -m "Hack the planet!"
```

The same fields in `config.ini` are used inline as well. They can be used like so:

- `webhook:=<url>`
- `textField:=<key>`
- `data:=<additional json>`

## Todo
Some stuff that I plan to implement:
- [X] Slack
- [X] Telegram
- [X] Microsoft Teams
- [ ] Discord
- [X] Email
- [X] Let user decide max rows to be sent
- [X] Place config file in ~/.config/emissary.ini
The character `§` is used as a delimiter between each field.

## Contributing
Any feedback or ideas are welcome! Want to improve something? Create a pull request!
Expand Down
110 changes: 84 additions & 26 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,86 @@ import (
"flag"
"fmt"
"os"
"strings"
)

type channels []string

func (c *channels) String() string {
return ""
}

func (c *channels) Set(value string) error {
*c = append(*c, value)
return nil
}

type inlines struct {
hooks []inline
}

type inline struct {
webhook string
textField string
data string
}

func (i *inlines) String() string {
return "{'lol':1}"
}

func (i *inlines) Set(value string) error {
split := strings.Split(value, "§")
mul := inlines{}
final := inline{}
for _, val := range split {
s := strings.Split(val, ":=")

if strings.ToLower(s[0]) == "webhook" {
final.webhook = s[1]
}

if strings.ToLower(s[0]) == "textField" {
final.textField = s[1]
}

if strings.ToLower(s[0]) == "data" {
final.data = s[1]
}

}
mul.hooks = append(mul.hooks, final)
*i = mul

return nil
}

type cliOptions struct {
telegram bool
discord bool
slack bool
email bool
teams bool
version bool
stdin bool
message string
rows int
email bool
version bool
stdin bool
message string
channel channels
inline inlines
data string
text string
rows int
}

func processArgs() cliOptions {

opts := cliOptions{}
flag.BoolVar(&opts.telegram, "telegram", false, "Send via telegram")
flag.BoolVar(&opts.telegram, "t", false, "Send via telegram")
flag.BoolVar(&opts.slack, "slack", false, "Send via slack")
flag.BoolVar(&opts.slack, "s", false, "Send via slack")
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
flag.Var(&opts.channel, "channel", "Specify a custom channel you have defined in ~/.config/emissary.ini")
flag.Var(&opts.channel, "ch", "Specify a custom channel you have defined in ~/.config/emissary.ini")
flag.Var(&opts.inline, "inline", "Specify channel directly in the command line")
flag.Var(&opts.inline, "in", "Specify channel directly in the command line")
flag.BoolVar(&opts.email, "email", false, "Send via smtp")
flag.BoolVar(&opts.email, "e", false, "Send via smtp")
flag.BoolVar(&opts.teams, "teams", false, "Send via Microsoft Teams")
flag.BoolVar(&opts.teams, "ms", false, "Send via Send via Microsoft Teams")
flag.StringVar(&opts.text, "text", "", "Specify the field that contains the message. Default is 'text'")
flag.StringVar(&opts.text, "txt", "", "Specify the field that contains the message. Default is 'text'")
flag.StringVar(&opts.data, "data", "", "Specify json data that should be sent")
flag.StringVar(&opts.data, "d", "", "Specify json data that should be sent")
flag.BoolVar(&opts.version, "version", false, "Show version number")
flag.BoolVar(&opts.version, "v", false, "Show version number")
flag.BoolVar(&opts.stdin, "stdin", false, "Take input from stdin")
Expand All @@ -48,21 +103,24 @@ func init() {
h := "\nSend data through chat channels. Made by @dubs3c.\n\n"

h += "Usage:\n"
h += " emissary [channel] [message]\n\n"
h += " emissary [options] [message]\n\n"

h += "Options:\n"
h += " -s, --slack Send via Slack\n"
h += " -t, --telegram Send via Telegram\n"
h += " -e, --email Send via Email\n"
h += " -ms, --teams Send via Microsoft Teams\n"
h += " -si, --stdin Get message from stdin\n"
h += " -m, --message Message to send\n"
h += " -r, --rows Max rows/lines to send, 0 for unlimited. Default 20\n"
h += " -v, --version Show version\n"
h += " -ch, --channel Specify a custom channel you have defined emissary.ini\n"
h += " -in, --inline Specify channel directly in the commandline\n"
h += " -m, --message Message to send\n"
h += " -si, --stdin Get message from stdin\n"
h += " -e, --email Send via Email\n"
h += " -txt, --text Specify the field that contains the message. Default is 'message'\n"
h += " -d, --data Specify additional data in json format that should be sent\n"
h += " -r, --rows Max rows/lines to send, 0 for unlimited. Default 20\n"
h += " -v, --version Show version\n"

h += "\nExamples:\n"
h += " emissary -telegram --message \"Hello telegram\"\n"
h += " cat domins.txt | emissary --slack --stdin \n\n"
h += " emissary --channel Telegram --message \"Hello telegram\"\n"
h += " cat domins.txt | emissary -ch Slack --stdin \n"
h += " emissary -ch Discord -ch Telegram -m \"Your message\" \n"
h += " emissary -in \"webhook:=https://api.telegram.org/botxxxxx/sendMessage§data:={'chat_id': 'xxxx'}\" -in \"webhook:=https://hooks.slack.com/services/xxxxx\" -m \"Hack the planet!\" \n"

fmt.Fprintf(os.Stderr, h)
}
Expand Down
51 changes: 24 additions & 27 deletions channels.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bytes"
"encoding/json"
"net/http"
"net/smtp"
Expand All @@ -19,26 +20,30 @@ type EmailConfig struct {
message string
}

// Telegram Send messages via telegram
func Telegram(chatID string, apiKey string, message string) (*http.Response, error) {
// PreparePayload Prepare a json payload to be sent to channel
func PreparePayload(message string, msgField string, additionalData string) ([]byte, error) {

jayson := map[string]interface{}{
"chat_id": chatID,
"text": message,
msgField: message,
}
js, _ := json.Marshal(jayson)
endpoint := "https://api.telegram.org/bot" + apiKey + "/sendMessage"

return request(endpoint, string(js))
}

// Slack Send messages via Slack
func Slack(message string, webhook string) (*http.Response, error) {
jayson := map[string]interface{}{
"text": message,
// Required for valid json
additionalData = strings.ReplaceAll(additionalData, "'", "\"")
if additionalData != "" {
data := []byte(`` + additionalData + ``)
var f interface{}
if err := json.Unmarshal(data, &f); err != nil {
return nil, err
}
m := f.(map[string]interface{})
for k, v := range m {
jayson[k] = v
}
}
js, err := json.Marshal(jayson)
if err != nil {
return []byte{}, err
}
js, _ := json.Marshal(jayson)
return request(webhook, string(js))
return js, nil
}

// Email Send messages via email
Expand All @@ -61,16 +66,8 @@ func Email(email EmailConfig) error {
return nil
}

// Teams Send messages via Microsoft Teams
func Teams(message string, webhook string) (*http.Response, error) {
jayson := map[string]interface{}{
"text": message,
}
js, _ := json.Marshal(jayson)
return request(webhook, string(js))
}

func request(endpoint string, data string) (*http.Response, error) {
// SendRequest Send the request to the webhook
func SendRequest(endpoint string, data []byte) (*http.Response, error) {

tr := &http.Transport{
MaxIdleConns: 10,
Expand All @@ -83,7 +80,7 @@ func request(endpoint string, data string) (*http.Response, error) {

client := &http.Client{Transport: tr}

resp, err = client.Post(endpoint, "application/json", strings.NewReader(data))
resp, err = client.Post(endpoint, "application/json", bytes.NewBuffer(data))

if err != nil {
return resp, err
Expand Down
Loading

0 comments on commit 86027af

Please sign in to comment.