This commit is contained in:
evilsocket 2024-10-10 14:34:20 +02:00
commit 88d813543a
10 changed files with 7453 additions and 49 deletions

View file

@ -44,14 +44,21 @@ func (mod *EventsStream) viewZeroConfEvent(output io.Writer, e session.Event) {
instPart = fmt.Sprintf(" and instances %s", strings.Join(instances, ", "))
}
fmt.Fprintf(output, "[%s] [%s] %s is browsing (%s) for services %s%s\n",
textPart := ""
if len(event.Text) > 0 {
textPart = fmt.Sprintf("\n text records: %s\n", strings.Join(event.Text, ", "))
}
fmt.Fprintf(output, "[%s] [%s] %s is browsing (%s) for services %s%s\n%s",
e.Time.Format(mod.timeFormat),
tui.Green(e.Tag),
source,
ops.Ternary(event.Query.QR, "RESPONSE", "QUERY"),
strings.Join(services, ", "),
instPart,
textPart,
)
} else {
fmt.Fprintf(output, "[%s] [%s] %v\n", e.Time.Format(mod.timeFormat), tui.Green(e.Tag), e)
}

View file

@ -25,7 +25,13 @@ var TCP_HANDLERS = map[string]Handler{
TLS: true,
Handle: ippClientHandler,
},
// TODO: _http at least
"_http": {
Handle: httpClientHandler,
},
"_https": {
TLS: true,
Handle: httpClientHandler,
},
}
type Acceptor struct {
@ -42,6 +48,7 @@ type Acceptor struct {
ctxCancel context.CancelFunc
handler Handler
ippAttributes map[string]string
httpPaths map[string]string
}
type HandlerContext struct {
@ -52,9 +59,10 @@ type HandlerContext struct {
srvPort int
srvTLS bool
ippAttributes map[string]string
httpPaths map[string]string
}
func NewAcceptor(mod *ZeroGod, service string, srvHost string, port uint16, tlsConfig *tls.Config, ippAttributes map[string]string) *Acceptor {
func NewAcceptor(mod *ZeroGod, service string, srvHost string, port uint16, tlsConfig *tls.Config, ippAttributes map[string]string, httpPaths map[string]string) *Acceptor {
context, ctcCancel := context.WithCancel(context.Background())
proto := ops.Ternary(strings.Contains(service, "_tcp"), "tcp", "udp").(string)
@ -67,11 +75,14 @@ func NewAcceptor(mod *ZeroGod, service string, srvHost string, port uint16, tlsC
ctxCancel: ctcCancel,
srvHost: srvHost,
ippAttributes: ippAttributes,
httpPaths: httpPaths,
}
for svcName, svcHandler := range TCP_HANDLERS {
if strings.Contains(service, svcName) {
if svcHandler.TLS {
acceptor.tlsConfig = tlsConfig
}
acceptor.handler = svcHandler
break
}
@ -81,7 +92,7 @@ func NewAcceptor(mod *ZeroGod, service string, srvHost string, port uint16, tlsC
mod.Warning("no protocol handler found for service %s, using generic %s dump handler", tui.Yellow(service), proto)
acceptor.handler.Handle = handleGenericTCP
} else {
mod.Info("found %s %s protocol handler", proto, tui.Green(service))
mod.Info("found %s %s protocol handler (tls=%v)", proto, tui.Green(service), acceptor.tlsConfig != nil)
}
return &acceptor
@ -114,6 +125,7 @@ func (a *Acceptor) startTCP() (err error) {
srvPort: int(a.port),
srvTLS: a.tlsConfig != nil,
ippAttributes: a.ippAttributes,
httpPaths: a.httpPaths,
})
}
}

View file

@ -172,7 +172,7 @@ func (mod *ZeroGod) startAdvertiser(fileName string) error {
for _, svc := range advertiser.Services {
// if no external responder has been specified
if svc.Responder == "" {
acceptor := NewAcceptor(mod, svc.FullName(), hostName, uint16(svc.Port), tlsConfig, svc.IPP)
acceptor := NewAcceptor(mod, svc.FullName(), hostName, uint16(svc.Port), tlsConfig, svc.IPP, svc.HTTP)
if err := acceptor.Start(); err != nil {
return err
}

View file

@ -27,6 +27,7 @@ type BrowsingEvent struct {
Endpoint *network.Endpoint `json:"endpoint"`
Services []string `json:"services"`
Instances []string `json:"instances"`
Text []string `json:"text"`
Query layers.DNS `json:"query"`
}
@ -258,11 +259,12 @@ func (mod *ZeroGod) onPacket(pkt gopacket.Packet) {
}
instances := make([]string, 0)
text := make([]string, 0)
for _, answer := range append(append(dns.Answers, dns.Additionals...), dns.Authorities...) {
if answer.Class == layers.DNSClassIN && answer.Type == layers.DNSTypePTR {
instances = append(instances, string(answer.PTR))
} else {
instances = append(instances, answer.String())
} else if answer.Type == layers.DNSTypeTXT {
text = append(text, string(answer.TXT))
}
}
@ -271,6 +273,7 @@ func (mod *ZeroGod) onPacket(pkt gopacket.Packet) {
Query: dns,
Services: services,
Instances: instances,
Text: text,
Endpoint: mod.Session.Lan.GetByIp(srcIP.String()),
}

View file

@ -0,0 +1,104 @@
package zerogod
import (
"bufio"
"bytes"
"fmt"
"io"
"net/http"
"strings"
"github.com/evilsocket/islazy/tui"
)
func httpGenericHandler(ctx *HandlerContext) (shouldQuit bool, clientIP string, req *http.Request, err error) {
clientIP = strings.SplitN(ctx.client.RemoteAddr().String(), ":", 2)[0]
buf := make([]byte, 4096)
// read raw request
read, err := ctx.client.Read(buf)
if err != nil {
if err == io.EOF {
ctx.mod.Debug("EOF, client %s disconnected", clientIP)
return true, clientIP, nil, nil
}
ctx.mod.Warning("error while reading from %v: %v", clientIP, err)
return true, clientIP, nil, err
} else if read == 0 {
ctx.mod.Warning("error while reading from %v: no data", clientIP)
return true, clientIP, nil, err
}
raw_req := buf[0:read]
ctx.mod.Debug("read %d bytes from %v:\n%s\n", read, clientIP, Dump(raw_req))
// parse as http
reader := bufio.NewReader(bytes.NewReader(raw_req))
httpRequest, err := http.ReadRequest(reader)
if err != nil {
ctx.mod.Error("error while parsing http request from %v: %v\n%s", clientIP, err, Dump(raw_req))
return true, clientIP, nil, err
}
return false, clientIP, httpRequest, nil
}
func httpLogRequest(ctx *HandlerContext, clientIP string, httpRequest *http.Request) {
clientUA := httpRequest.UserAgent()
ctx.mod.Info("%v (%s) > %s %s",
clientIP,
tui.Green(clientUA),
tui.Bold(httpRequest.Method),
tui.Yellow(httpRequest.RequestURI))
}
func httpClientHandler(ctx *HandlerContext) {
defer ctx.client.Close()
shouldQuit, clientIP, httpRequest, err := httpGenericHandler(ctx)
if shouldQuit {
return
} else if err != nil {
ctx.mod.Error("%v", err)
}
httpLogRequest(ctx, clientIP, httpRequest)
respStatusCode := 404
respStatus := "Not Found"
respBody := `<html>
<head><title>Not Found</title></head>
<body>
<center><h1>Not Found</h1></center>
</body>
</html>`
// see if anything in config matches
for path, body := range ctx.httpPaths {
if httpRequest.RequestURI == path {
respStatusCode = 200
respStatus = "OK"
respBody = body
break
}
}
response := fmt.Sprintf(`HTTP/1.1 %d %s
Content-Type: text/html; charset=utf-8
Content-Length: %d
Connection: close
%s`,
respStatusCode,
respStatus,
len(respBody),
respBody,
)
if _, err = ctx.client.Write([]byte(response)); err != nil {
ctx.mod.Error("error while writing http response data: %v", err)
} else {
ctx.mod.Debug("sent %d of http response to %v", len(response), ctx.client.RemoteAddr())
}
}

View file

@ -67,7 +67,7 @@ func ippOnGetJobAttributes(ctx *HandlerContext, ipp_req *ipp.Request) {
}
ipp_resp.OperationAttributes["job-originating-user-name"] = []ipp.Attribute{
{
Value: "bettercap", // TODO: check if this must match the actual job user from a print operation
Value: "bettercap",
Tag: ipp.TagName,
},
}

View file

@ -1,12 +1,7 @@
package zerogod
import (
"bufio"
"bytes"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/evilsocket/islazy/tui"
@ -42,40 +37,17 @@ type PrintData struct {
func ippClientHandler(ctx *HandlerContext) {
defer ctx.client.Close()
clientIP := strings.SplitN(ctx.client.RemoteAddr().String(), ":", 2)[0]
buf := make([]byte, 4096)
// read raw request
read, err := ctx.client.Read(buf)
if err != nil {
if err == io.EOF {
ctx.mod.Debug("EOF, client %s disconnected", clientIP)
return
}
ctx.mod.Warning("error while reading from %v: %v", clientIP, err)
return
} else if read == 0 {
ctx.mod.Warning("error while reading from %v: no data", clientIP)
shouldQuit, clientIP, httpRequest, err := httpGenericHandler(ctx)
if shouldQuit {
return
} else if err != nil {
ctx.mod.Error("%v", err)
}
raw_req := buf[0:read]
ctx.mod.Debug("read %d bytes from %v:\n%s\n", read, clientIP, Dump(raw_req))
// parse as http
reader := bufio.NewReader(bytes.NewReader(raw_req))
http_req, err := http.ReadRequest(reader)
if err != nil {
ctx.mod.Error("error while parsing http request from %v: %v\n%s", clientIP, err, Dump(raw_req))
return
}
clientUA := http_req.UserAgent()
clientUA := httpRequest.UserAgent()
ctx.mod.Info("%v -> %s", clientIP, tui.Green(clientUA))
ipp_body, err := ippReadRequestBody(ctx, http_req)
ipp_body, err := ippReadRequestBody(ctx, httpRequest)
if err != nil {
ctx.mod.Error("%v", err)
return
@ -84,7 +56,7 @@ func ippClientHandler(ctx *HandlerContext) {
// parse as IPP
ipp_req, err := ipp.NewRequestDecoder(ipp_body).Decode(nil)
if err != nil {
ctx.mod.Error("error while parsing ipp request from %v: %v -> %++v", clientIP, err, *http_req)
ctx.mod.Error("error while parsing ipp request from %v: %v -> %++v", clientIP, err, *httpRequest)
return
}
@ -118,7 +90,7 @@ func ippClientHandler(ctx *HandlerContext) {
ippOnGetJobs(ctx, ipp_req)
// Print-Job
case 0x0002:
ippOnPrintJob(ctx, http_req, ipp_req)
ippOnPrintJob(ctx, httpRequest, ipp_req)
// Get-Job-Attributes
case 0x0009:
ippOnGetJobAttributes(ctx, ipp_req)

File diff suppressed because it is too large Load diff

View file

@ -17,6 +17,7 @@ type ServiceData struct {
Records []string `yaml:"records,omitempty"` // Service DNS text records
Responder string `yaml:"responder,omitempty"` // Optional IP to use instead of our tcp acceptor
IPP map[string]string `yaml:"ipp,omitempty"` // Optional IPP attributes map
HTTP map[string]string `yaml:"http,omitempty"` // Optional HTTP URIs map
server *zeroconf.Server
}

View file

@ -30,11 +30,25 @@ func (mod *ZeroGod) show(filter string, withData bool) error {
}
for _, svc := range entry.Services {
fmt.Fprintf(mod.Session.Events.Stdout, " %s (%s) [%v / %v]:%s\n",
ip := ""
if len(svc.AddrIPv4) > 0 {
ip = svc.AddrIPv4[0].String()
} else if len(svc.AddrIPv6) > 0 {
ip = svc.AddrIPv6[0].String()
} else {
ip = svc.HostName
}
svcDesc := ""
svcName := strings.SplitN(svc.Service, ".", 2)[0]
if desc, found := KNOWN_SERVICES[svcName]; found {
svcDesc = tui.Dim(fmt.Sprintf(" %s", desc))
}
fmt.Fprintf(mod.Session.Events.Stdout, " %s%s %s:%s\n",
tui.Green(svc.ServiceInstanceName()),
tui.Dim(svc.HostName),
svc.AddrIPv4,
svc.AddrIPv6,
svcDesc,
ip,
tui.Red(fmt.Sprintf("%d", svc.Port)),
)