packagesipimport("bufio""context""fmt""log/slog""net""strings""sync""time""honeypot/internal/database""honeypot/internal/honeypot""honeypot/internal/logger""honeypot/internal/types""honeypot/internal/utils")const(HoneypotType=types.HoneypotTypeSIPHoneypotLabel="SIP")// Config holds the configuration for the SIP honeypot.typeConfigstruct{ListenAddrstringPorts[]uint16}// sipHoneypot implements the honeypot.Honeypot interface.typesipHoneypotstruct{configConfiglogger*slog.Logger}// New creates a new SIP honeypot instance.funcNew(cfgConfig)honeypot.Honeypot{// Register fields for top-N trackinglogger.RegisterTopNField("sip","method")logger.RegisterTopNField("sip","user_agent")logger.RegisterTopNField("sip","from")logger.RegisterTopNField("sip","to")logger.RegisterTopNField("sip","auth_username")return&sipHoneypot{config:cfg,}}// Name returns the name of this honeypot.func(h*sipHoneypot)Name()types.HoneypotType{returnHoneypotType}// Label returns the label of this honeypot.func(h*sipHoneypot)Label()string{returnHoneypotLabel}// Start starts the SIP honeypot server.func(h*sipHoneypot)Start(ctxcontext.Context,l*slog.Logger)error{h.logger=lvarwgsync.WaitGroupfor_,port:=rangeh.config.Ports{ifport==0{continue}// Start both TCP and UDP listeners for SIPwg.Add(2)gofunc(puint16){deferwg.Done()h.listenTCP(ctx,p)}(port)gofunc(puint16){deferwg.Done()h.listenUDP(ctx,p)}(port)}wg.Wait()logger.LogInfo(h.logger,HoneypotType,"honeypot shutdown complete",nil)returnnil}func(h*sipHoneypot)listenTCP(ctxcontext.Context,portuint16){addr:=utils.BuildAddress(h.config.ListenAddr,port)ln,err:=net.Listen("tcp",addr)iferr!=nil{logger.LogError(h.logger,HoneypotType,"listen_tcp_failed",err,[]any{"addr",addr})return}deferln.Close()gofunc(){<-ctx.Done()ln.Close()}()logger.LogInfo(h.logger,HoneypotType,"honeypot tcp listening",[]any{"port",port})for{conn,err:=ln.Accept()iferr!=nil{ifctx.Err()!=nil{return}continue}goh.handleTCPConn(conn)}}func(h*sipHoneypot)handleTCPConn(connnet.Conn){deferconn.Close()conn.SetDeadline(time.Now().Add(10*time.Second))reader:=bufio.NewReader(conn)// Detect TLS handshake on non-TLS portpeek,_:=reader.Peek(1)iflen(peek)>0&&peek[0]==0x16{h.logTLSHandshake(conn.RemoteAddr(),conn.LocalAddr())return}for{msg,err:=h.readSIPMessage(reader)iferr!=nil{break}h.processMessage(msg,conn.RemoteAddr(),conn.LocalAddr())// In SIP over TCP, multiple requests can be sent over one connection}}func(h*sipHoneypot)listenUDP(ctxcontext.Context,portuint16){addr:=utils.BuildAddress(h.config.ListenAddr,port)pc,err:=net.ListenPacket("udp",addr)iferr!=nil{logger.LogError(h.logger,HoneypotType,"listen_udp_failed",err,[]any{"addr",addr})return}deferpc.Close()gofunc(){<-ctx.Done()pc.Close()}()logger.LogInfo(h.logger,HoneypotType,"honeypot udp listening",[]any{"port",port})buf:=make([]byte,4096)for{n,remoteAddr,err:=pc.ReadFrom(buf)iferr!=nil{ifctx.Err()!=nil{return}continue}msg:=h.parseSIPMessage(string(buf[:n]))ifmsg.Method==""{continue}h.processMessage(msg,remoteAddr,pc.LocalAddr())}}typesipMessagestruct{MethodstringURIstringHeadersmap[string]stringRawstring}func(h*sipHoneypot)readSIPMessage(r*bufio.Reader)(sipMessage,error){line,err:=r.ReadString('\n')iferr!=nil{returnsipMessage{},err}parts:=strings.Fields(line)iflen(parts)<3{returnsipMessage{},fmt.Errorf("invalid SIP request line")}msg:=sipMessage{Method:parts[0],URI:parts[1],Headers:make(map[string]string),Raw:line,}for{line,err=r.ReadString('\n')iferr!=nil{break}msg.Raw+=linetrimmed:=strings.TrimSpace(line)iftrimmed==""{break}ifidx:=strings.Index(line,":");idx!=-1{key:=strings.TrimSpace(line[:idx])val:=strings.TrimSpace(line[idx+1:])msg.Headers[key]=val}}returnmsg,nil}func(h*sipHoneypot)parseSIPMessage(rawstring)sipMessage{lines:=strings.Split(raw,"\n")iflen(lines)==0{returnsipMessage{}}parts:=strings.Fields(lines[0])iflen(parts)<3{returnsipMessage{}}msg:=sipMessage{Method:parts[0],URI:parts[1],Headers:make(map[string]string),Raw:raw,}fori:=1;i<len(lines);i++{line:=strings.TrimSpace(lines[i])ifline==""{break}ifidx:=strings.Index(line,":");idx!=-1{key:=strings.TrimSpace(line[:idx])val:=strings.TrimSpace(line[idx+1:])msg.Headers[key]=val}}returnmsg}func(h*sipHoneypot)processMessage(msgsipMessage,remoteAddr,localAddrnet.Addr){remoteHost,remotePort:=utils.SplitAddr(remoteAddr.String(),h.logger)_,dstPort:=utils.SplitAddr(localAddr.String(),h.logger)fields:=map[string]any{"method":msg.Method,"uri":msg.URI,"user_agent":msg.Headers["User-Agent"],"from":msg.Headers["From"],"to":msg.Headers["To"],"call_id":msg.Headers["Call-ID"],}event:=types.EventRequestifmsg.Method=="REGISTER"||msg.Method=="INVITE"{ifauth,ok:=msg.Headers["Authorization"];ok{event=types.EventAuthAttemptfields["authorization"]=auth// Extract username from auth headerifidx:=strings.Index(auth,"username=\"");idx!=-1{username:=auth[idx+10:]ifendIdx:=strings.Index(username,"\"");endIdx!=-1{fields["auth_username"]=username[:endIdx]}}}}logger.LogEvent(h.logger,types.LogEvent{Type:HoneypotType,Event:event,RemoteAddr:remoteHost,RemotePort:remotePort,DstPort:dstPort,Fields:fields,})}func(h*sipHoneypot)logTLSHandshake(remoteAddr,localAddrnet.Addr){remoteHost,remotePort:=utils.SplitAddr(remoteAddr.String(),h.logger)_,dstPort:=utils.SplitAddr(localAddr.String(),h.logger)logger.LogEvent(h.logger,types.LogEvent{Type:HoneypotType,Event:types.EventTLSHandshake,RemoteAddr:remoteHost,RemotePort:remotePort,DstPort:dstPort,Fields:map[string]interface{}{"message":"TLS handshake attempt on non-TLS port"},})}// GetScores returns the scored ip addresses for the honeypot.func(h*sipHoneypot)GetScores(db*database.Database,intervalstring)honeypot.ScoreMap{// get SIP scoresquery:=fmt.Sprintf(` SELECT remote_addr, COUNT(*) as count
FROM honeypot_events
WHERE type = 'sip'
AND time >= NOW() - INTERVAL %s
GROUP BY remote_addr
`,interval)rows,err:=db.DB.Query(query)iferr!=nil{returnhoneypot.ScoreMap{}}deferrows.Close()scores:=make(honeypot.ScoreMap)forrows.Next(){varremoteAddrstringvarcountintiferr:=rows.Scan(&remoteAddr,&count);err!=nil{returnhoneypot.ScoreMap{}}scores[remoteAddr]=honeypot.Score{Score:uint(count*100),Tags:[]types.Tag{types.TagAuthAttempt},}}returnscores}func(h*sipHoneypot)Ports()[]uint16{returnh.config.Ports}