From cc9c5191b8b8f3291ed1988f0a3410fe95e8ccce Mon Sep 17 00:00:00 2001 From: Alessio Perugini Date: Tue, 13 Oct 2020 12:28:29 +0200 Subject: [PATCH] Added c2 blacklist capability --- cmd/main.go | 5 +++-- inspection.go | 54 +++++++++++++++++++-------------------------------- live_mode.go | 10 ++++++---- logic.go | 17 ++++++++++++---- options.go | 2 +- peng.go | 34 +++++++++++--------------------- storage.go | 11 +++++------ utils.go | 44 +++++++++++++++++++++++++++++++++++------ 8 files changed, 97 insertions(+), 80 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 09cc429..ae2b393 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -15,7 +15,7 @@ import ( var ( config = stanislav.Config{ - NumberOfBin: 128, + NumberOfBin: 16, SizeBitmap: 1024, InfluxUrl: "http://localhost", InfluxPort: 9999, @@ -45,7 +45,7 @@ func init() { flag.IntVar(&stanislav.NTwToCompare, "nCompare", 3, "number o time windows to compare to evaluate a possible periodicity") flag.StringVar(&stanislav.IpAddrNF, "ip", "", "ip of netflow collector") flag.StringVar(&stanislav.PortNF, "port", "2055", "port of netflow collector") - flag.IntVar(&stanislav.Verbose, "verbose", 0, "verbosity level. (1=low,2=medium,3=high") + flag.UintVar(&config.Verbose, "verbose", 0, "verbosity level. (1=low,2=medium,3=high") //Bitmap flag.UintVar(&config.NumberOfBin, "bin", 16, "number of bin in your bitmap") @@ -67,6 +67,7 @@ func init() { flag.StringVar(&config.Ja3BlackListFile, "ja3", "", "file path of malicious ja3 fingerprints") flag.StringVar(&config.GeoIpDb, "geoip", "", "file path of geoip db") flag.StringVar(&config.OfflinePcap, "pcap", "", "pcap file to read") + flag.StringVar(&config.IpBlackListFile,"c2","", "file path of malicious ip") } func flagConfig() { diff --git a/inspection.go b/inspection.go index 7507c40..5a6aaa5 100644 --- a/inspection.go +++ b/inspection.go @@ -1,7 +1,6 @@ package stanislav import ( - "fmt" "github.com/google/gopacket" "github.com/google/gopacket/layers" _ "github.com/google/gopacket/layers" //Used to init internal struct @@ -10,7 +9,6 @@ import ( "net" "stanislav/pkg/ja3" "stanislav/pkg/portbitmap" - "time" ) var myIPs = make([]net.IP, 0, 2) @@ -48,15 +46,22 @@ func (p *Peng) inspect(packet gopacket.Packet) { if p.Config.Verbose == 3 { if packetDestToMyPc { - fmt.Printf("[%s] server traffic: %s \n", time.Now().Local().String(), tcp.DstPort.String()) + logger.Printf("server traffic: %s \n", tcp.DstPort.String()) } else { - fmt.Printf("[%s] client traffic: %s \n", time.Now().Local().String(), tcp.DstPort.String()) + logger.Printf("client traffic: %s \n", tcp.DstPort.String()) } } } } GeoIpSearch(externalIp, p.Config.GeoIpDb) + + //BLACKLISTED c2 Server + if name, ok := blackListIp[externalIp]; ok { + AddPossibleThreat(externalIp, "c2 server " + name) + logger.Printf("[%s] appears in the blocked c2 list as %s!\n", externalIp, name) + } + externalIp = ipv4.SrcIP.String() + "/" + ipv4.DstIP.String() //TODO if len(ja3BlackList) != 0 { @@ -66,56 +71,37 @@ func (p *Peng) inspect(packet gopacket.Packet) { if p.Config.Verbose == 2 { if ja3md5 != "" { - fmt.Printf("J: %s\n", ja3md5) + logger.Printf("J: %s\n", ja3md5) } if ja3smd5 != "" { - fmt.Printf("JS: %s\n", ja3smd5) + logger.Printf("JS: %s\n", ja3smd5) } } - + //TODO improvmenet external IP detection, especially in offline mode! Maybe loading a filtering list if name, ok := ja3BlackList[ja3md5]; ok { AddPossibleThreat(externalIp, "ja3 blocklist "+name) - fmt.Printf("[%s] %s appears in the blocked Ja3 list as %s!\n", externalIp, ja3md5, name) + logger.Printf("[%s] %s appears in the blocked Ja3 list as %s!\n", externalIp, ja3md5, name) } if name, ok := ja3BlackList[ja3smd5]; ok { AddPossibleThreat(externalIp, "ja3s blocklist "+name) - fmt.Printf("[%s] %s appears in the blocked Ja3 list as %s!\n", externalIp, ja3smd5, name) + logger.Printf("[%s] %s appears in the blocked Ja3 list as %s!\n", externalIp, ja3smd5, name) } + //TODO add TLS version check //TLS cipher security check switch ja3.Security { case 1: AddPossibleThreat(externalIp, "Weak tls cipher") - fmt.Println("Weak tls cipher") + logger.Println("Weak tls cipher") case 2: AddPossibleThreat(externalIp, "Insecure tls cipher") - fmt.Println("Insecure tls cipher") + logger.Println("Insecure tls cipher") } } //TODO add http numeric ip - // switch hello.CipherSuite { - // case 49169: fallthrough /* TLS_ECDHE_RSA_WITH_RC4_128_SHA */ - // case 5: fallthrough /* TLS_RSA_WITH_RC4_128_SHA */ - // case 4: Security = 2 /* TLS_RSA_WITH_RC4_128_MD5 */ - // /* WEAK */ - // case 157: fallthrough /* TLS_RSA_WITH_AES_256_GCM_SHA384 */ - // case 61: fallthrough /* TLS_RSA_WITH_AES_256_CBC_SHA256 */ - // case 53: fallthrough /* TLS_RSA_WITH_AES_256_CBC_SHA */ - // case 132: fallthrough /* TLS_RSA_WITH_CAMELLIA_256_CBC_SHA */ - // case 156: fallthrough /* TLS_RSA_WITH_AES_128_GCM_SHA256 */ - // case 60: fallthrough /* TLS_RSA_WITH_AES_128_CBC_SHA256 */ - // case 47: fallthrough /* TLS_RSA_WITH_AES_128_CBC_SHA */ - // case 65: fallthrough /* TLS_RSA_WITH_CAMELLIA_128_CBC_SHA */ - // case 49170: fallthrough /* TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA */ - // case 22: fallthrough /* TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA */ - // case 10: fallthrough /* TLS_RSA_WITH_3DES_EDE_CBC_SHA */ - // case 150: fallthrough /* TLS_RSA_WITH_SEED_CBC_SHA */ - // case 7: Security = 1 /* TLS_RSA_WITH_IDEA_CBC_SHA */ - // default: Security = 0 - // } } func GeoIpSearch(ip, dbPath string) { @@ -128,7 +114,7 @@ func GeoIpSearch(ip, dbPath string) { parsedIp := net.ParseIP(ip) record, err := db.Country(parsedIp) if err != nil { - log.Println(err) + logger.Println(err) } if record.Country.IsoCode != "" { @@ -148,14 +134,14 @@ func (p *Peng) PortScanningHandler(port uint16, incomingPck bool) { func addPortToBitmap(port uint16, pBitmap *portbitmap.PortBitmap) { err := pBitmap.AddPort(port) if err != nil { - log.Println(err.Error()) + logger.Println(err.Error()) } } func getMyIp() { addrs, err := net.InterfaceAddrs() if err != nil { - log.Fatal(err.Error()) + logger.Fatal(err.Error()) } for _, a := range addrs { diff --git a/live_mode.go b/live_mode.go index 2a7ba5d..cc633af 100644 --- a/live_mode.go +++ b/live_mode.go @@ -16,15 +16,17 @@ type proto interface { var PossibleThreat = make(map[string][]string) func LiveMode() { - var ( - wg sync.WaitGroup - signalCh = make(chan os.Signal, 1) - ) + var wg sync.WaitGroup + var signalCh = make(chan os.Signal, 1) + opts = GetOptions() runtime.GOMAXPROCS(opts.getCPU()) signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM) logger = opts.Logger + //BlacklistModule + LoadBlockListedC2() + netflow9 := NewNetflowV9() peng := New(Conf) protos := []proto{netflow9, peng} diff --git a/logic.go b/logic.go index b3009cb..cf419d0 100644 --- a/logic.go +++ b/logic.go @@ -3,7 +3,6 @@ package stanislav import ( "encoding/json" "fmt" - "log" "math" "time" ) @@ -72,6 +71,16 @@ func InspectFlow(rf RawFlow) { !ExcludeMultiAndBroadcast(rf.Ipv4SrcAddr) && !ExcludeMultiAndBroadcast(rf.Ipv4DstAddr)) { return } + //TODO create a function that handles C2 server blocklist + if name, ok := blackListIp[rf.Ipv4DstAddr]; ok { + AddPossibleThreat(rf.Ipv4DstAddr, "c2 server " + name) + logger.Printf("[%s] appears in the blocked c2 list as %s!\n", rf.Ipv4DstAddr, name) + } + + if name, ok := blackListIp[rf.Ipv4SrcAddr]; ok { + AddPossibleThreat(rf.Ipv4SrcAddr, "c2 server " + name) + logger.Printf("[%s] appears in the blocked c2 list as %s!\n", rf.Ipv4SrcAddr, name) + } //https://tools.ietf.org/html/rfc5102#section-5 if rf.EndReason == 2 { @@ -134,10 +143,10 @@ func ChangePeriodicStatus(key string, fi *FlowInfo, v bool) { } if v && !fi.CurrentlyPeriodic { - AddPossibleThreat(key, fmt.Sprintf(" periodic frequency: %.2fs seen %d times.", fi.TWDuration, fi.PeriodicityCounter)) - log.Printf("%s \tbecame periodic! Seen %d times. Frequency: %.2fs ", key, fi.PeriodicityCounter, fi.TWDuration) + AddPossibleThreat(key, fmt.Sprintf("periodic frequency: %.2fs seen %d times.", fi.TWDuration, fi.PeriodicityCounter)) + logger.Printf("%s \tbecame periodic! Seen %d times. Frequency: %.2fs ", key, fi.PeriodicityCounter, fi.TWDuration) } else { - log.Printf("%s \tnot periodic anymore! Seen %d times. Frequency: %.2fs ", key, fi.PeriodicityCounter, fi.TWDuration) + logger.Printf("%s \tnot periodic anymore! Seen %d times. Frequency: %.2fs ", key, fi.PeriodicityCounter, fi.TWDuration) } fi.CurrentlyPeriodic = v diff --git a/options.go b/options.go index 52151dc..5cd4644 100644 --- a/options.go +++ b/options.go @@ -21,7 +21,7 @@ var ( Conf *Config logger *log.Logger PercentageDeviation = 5.0 - Verbose = 0 + blackListIp = make(map[string]string) ) var ( diff --git a/peng.go b/peng.go index dc26319..a673134 100644 --- a/peng.go +++ b/peng.go @@ -7,7 +7,6 @@ import ( "github.com/google/gopacket" _ "github.com/google/gopacket/layers" //Used to init internal struct "github.com/google/gopacket/pcap" - "log" "os" "stanislav/pkg/portbitmap" "time" @@ -36,6 +35,7 @@ type Config struct { Ja3BlackListFile string GeoIpDb string OfflinePcap string + IpBlackListFile string } var ja3BlackList map[string]string @@ -73,11 +73,10 @@ func (p *Peng) run() { } if err != nil { - log.Fatal(err) + logger.Fatal(err) } defer pHandle.Close() - //go func() { packet := gopacket.NewPacketSource(pHandle, pHandle.LinkType()) time.AfterFunc(p.Config.TimeFrame, p.handler) @@ -87,17 +86,6 @@ func (p *Peng) run() { } p.inspect(packet) } - //}() - - /* - sig := make(chan os.Signal, 1024) - signal.Notify(sig, os.Interrupt) - <-sig - log.Println("Quitting Peng, bye!") - - for k, v := range topCountryVisit { - fmt.Printf("[%s] %d visit.\n", k, v) - }*/ } func (p *Peng) shutdown() { @@ -106,7 +94,7 @@ func (p *Peng) shutdown() { time.Sleep(1 * time.Second) //TODO - fmt.Println("\nTOP COUNTRY VISIT") + logger.Println("\n\nTOP COUNTRY VISIT") threatJson, _ := json.Marshal(topCountryVisit) fmt.Println(string(threatJson)) } @@ -116,7 +104,7 @@ func (p *Peng) LoadBlackListJa3InMemory() { defer file.Close() if err != nil { - log.Println(err) + logger.Println(err) return } @@ -141,21 +129,21 @@ func (p *Peng) PrintAllInfo() { allPortTraffic := []*portbitmap.PortBitmap{p.ClientTraffic, p.ServerTraffic} for i, v := range allPortTraffic { if p.Config.Verbose == 3 { - fmt.Println(v) //Print all bitmap - fmt.Println("Bit set: ") + logger.Println(v) //Print all bitmap + logger.Println("Bit set: ") for i := 0; i < len(v.InnerBitmap); i++ { - fmt.Println("bin number [", i, "] num (bit at 1): ", v.InnerBitmap[i].GetBitSets()) + logger.Println("bin number [", i, "] num (bit at 1): ", v.InnerBitmap[i].GetBitSets()) } } if p.Config.Verbose >= 1 { if i == 0 { - fmt.Printf("[%s] [CLIENT] ", time.Now().Local().String()) + logger.Printf("[CLIENT] ") } else { - fmt.Printf("[%s] [SERVER] ", time.Now().Local().String()) + logger.Printf("[SERVER] ") } } if p.Config.Verbose >= 2 { - fmt.Printf("entropy of each bin: %f\n", v.EntropyOfEachBin()) + logger.Printf("entropy of each bin: %f\n", v.EntropyOfEachBin()) } totalEntroy := v.EntropyTotal() @@ -163,7 +151,7 @@ func (p *Peng) PrintAllInfo() { AddPossibleThreat("general", fmt.Sprintf("probably a port scan. Total entropy: %.2f", totalEntroy)) } if p.Config.Verbose >= 1 { - fmt.Printf("total entropy: %f\n", totalEntroy) + logger.Printf("total entropy: %f\n", totalEntroy) } } } diff --git a/storage.go b/storage.go index 880e152..ebc9b01 100644 --- a/storage.go +++ b/storage.go @@ -4,7 +4,6 @@ import ( "encoding/csv" "fmt" influxdb2 "github.com/influxdata/influxdb-client-go" - "log" "os" "time" ) @@ -34,7 +33,7 @@ func (p *Peng) PushToInfluxDb() { writeApi.Flush() // Force all unwritten data to be sent if p.Config.Verbose == 3 { - fmt.Printf("[%s] file successfully pushed to influxdb\n", time.Now().Local().String()) + logger.Printf("[%s] file successfully pushed to influxdb\n", time.Now().Local().String()) } } @@ -46,7 +45,7 @@ func (p *Peng) ExportToCsv() { // 1. Open the file file, err := os.OpenFile(p.Config.SaveFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0777) if err != nil { - log.Println("error opening csv file", err.Error()) + logger.Println("error opening csv file", err.Error()) } defer file.Close() @@ -59,15 +58,15 @@ func (p *Peng) ExportToCsv() { // 3. Write all the records err = writer.WriteAll(csvData) // returns error if err != nil { - log.Println("error on writing csv data ", err.Error()) + logger.Println("error on writing csv data ", err.Error()) return } err = file.Chown(65534, 65534) if err != nil { - fmt.Println(err.Error()) + logger.Println(err.Error()) } if p.Config.Verbose == 3 { - fmt.Printf("[%s] data successfully exported to csv\n", time.Now().Local().String()) + logger.Printf("[%s] data successfully exported to csv\n", time.Now().Local().String()) } } diff --git a/utils.go b/utils.go index e70d10c..21f5c78 100644 --- a/utils.go +++ b/utils.go @@ -2,6 +2,7 @@ package stanislav import ( "encoding/binary" + "encoding/csv" "encoding/json" "errors" "fmt" @@ -84,12 +85,6 @@ func lastAddr(n *net.IPNet) (net.IP, error) { // works when the n is a prefix, o return ip, nil } -func DEBUG(v RawFlow, ip, tempo string) { - if v.Ipv4DstAddr == ip { - fmt.Printf("%v ", tempo) - } -} - func ConvFloatToDuration(v float64) time.Duration { xTime, err := time.ParseDuration(fmt.Sprint(v, "s")) if err != nil { @@ -113,8 +108,45 @@ func WriteObjToJSONFile(fname string, obj interface{}) { func AddPossibleThreat(ip, reason string) { if reasons, ok := PossibleThreat[ip]; ok { + for _,v := range reasons {//avoid duplicate reasons + if v == reason { + return + } + } reasons = append(reasons, reason) + PossibleThreat[ip] = reasons } else { PossibleThreat[ip] = []string{reason} } } + +func LoadBlockListedC2(){ + if Conf.IpBlackListFile == "" { + logger.Println("c2 block list not selected") + return + } + + file, err := os.OpenFile(Conf.IpBlackListFile, os.O_RDONLY, 0777) + defer file.Close() + + if err != nil { + logger.Println(err) + return + } + + r := csv.NewReader(file) + r.Comment = '#' + + for { + csvField, err := r.Read() + if err != nil { + break + } + + //Parse csv fields + ip := csvField[1] //ip + name := csvField[4] //malware name + + blackListIp[ip] = name + } +} \ No newline at end of file