Skip to content

Commit

Permalink
fix allowlist bug + fix user-agent bug
Browse files Browse the repository at this point in the history
  • Loading branch information
sudosammy committed Nov 20, 2023
1 parent c83b6cb commit a46e38a
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 39 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ If this were a denylist, it would stop knary from alerting on `www.knary.tld` bu

If this were an allowlist, knary would alert on exact matches (`sam.knary.tld`) and subdomain matches (`website1.sam.knary.tld`). Use `ALLOWLIST_STRICT=true` to prevent this fuzzy matching and only alert on hits to `sam.knary.tld`.

You can use both a deny and allowlist simultaneously. **Note:** wildcards in these files are not supported. An entry of `*.knary.tld` will match that string exactly.
You can use both a deny and allowlist simultaneously, note the denylist always has the higher order of precedence. For example, a request to a subdomain that matches the allowlist, would still be denied if the User-Agent matches something in the denylist. **Note:** wildcards in these files are not supported. An entry of `*.knary.tld` will match that string exactly.

2. The `DNS_SUBDOMAIN` configuration allows you to specify a subdomain that knary must fuzzy match (i.e. `*.DNS_SUBDOMAIN.knary.tld`) before alerting on DNS hits. This configuration does not affect HTTP(S) requests and remains primarily to mimic legacy knary v2 functionality. **Consider using a deny/allowlist instead.**

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.4.6
3.4.7
6 changes: 5 additions & 1 deletion libknary/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ func goSendMsg(ipaddr, reverse, name, record string) bool {
Printy("Got "+record+" question for: "+name, 3)
}

if !inAllowlist(name, ipaddr) || inBlacklist(name, ipaddr) {
if inBlacklist(name, ipaddr) {
return false // we check denylist first for consistent 'order of precedence' with the HTTP allow/denylist checking
}

if !inAllowlist(name, ipaddr) {
return false
}

Expand Down
68 changes: 40 additions & 28 deletions libknary/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ func Accept443(ln net.Listener, wg *sync.WaitGroup, restart <-chan bool) {
}
}

func httpRespond(conn net.Conn) bool {
conn.Write([]byte(" ")) // necessary as a 0 byte response triggers some clients to resend the request
conn.Close() // v. important lol
return true
}

func handleRequest(conn net.Conn) bool {
// set timeout for reading responses
_ = conn.SetDeadline(time.Now().Add(time.Second * time.Duration(2))) // 2 seconds
Expand Down Expand Up @@ -243,43 +249,49 @@ func handleRequest(conn net.Conn) bool {
}
}

// take off the header name for the user agent
userAgent = strings.TrimPrefix(strings.ToLower(userAgent), "user-agent:")
hostDomain := strings.TrimPrefix(strings.ToLower(host), "host:") // trim off the "Host:" section of header
// take off the headers for the allow/denylist search
searchUserAgent := strings.TrimPrefix(strings.ToLower(userAgent), "user-agent:")
searchDomain := strings.TrimPrefix(strings.ToLower(host), "host:") // trim off the "Host:" section of header

if inAllowlist(hostDomain, conn.RemoteAddr().String(), fwd) && !inBlacklist(hostDomain, conn.RemoteAddr().String(), fwd) && inAllowlist(userAgent) && !inBlacklist(userAgent) {
var msg string
var fromIP string
// these conditionals were bugged in <=3.4.6 whereby subdomains/ips in the allowlist weren't allowed unless the user-agent was ALSO in the allowlist
// it should be easier to grok now
if inBlacklist(searchUserAgent, searchDomain, conn.RemoteAddr().String(), fwd) { // inBlacklist returns false on empty/unused denylists
return httpRespond(conn)
}

if !inAllowlist(searchUserAgent, searchDomain, conn.RemoteAddr().String(), fwd) { // inAllowlist returns true on empty/unused allowlists
return httpRespond(conn)
}

var msg string
var fromIP string

if fwd != "" {
fromIP = fwd // use this when burp collab mode is active
} else {
fromIP = conn.RemoteAddr().String()
}

if fwd != "" {
fromIP = fwd // use this when burp collab mode is active
if cookie != "" {
if os.Getenv("FULL_HTTP_REQUEST") != "" {
msg = fmt.Sprintf("%s\n```Query: %s\n%s\n%s\nFrom: %s\n\n---------- FULL REQUEST ----------\n%s\n----------------------------------", host, query, userAgent, cookie, fromIP, response)
} else {
fromIP = conn.RemoteAddr().String()
msg = fmt.Sprintf("%s\n```Query: %s\n%s\n%s\nFrom: %s", host, query, userAgent, cookie, fromIP)
}

if cookie != "" {
if os.Getenv("FULL_HTTP_REQUEST") != "" {
msg = fmt.Sprintf("%s\n```Query: %s\n%s\n%s\nFrom: %s\n\n---------- FULL REQUEST ----------\n%s\n----------------------------------", host, query, userAgent, cookie, fromIP, response)
} else {
msg = fmt.Sprintf("%s\n```Query: %s\n%s\n%s\nFrom: %s", host, query, userAgent, cookie, fromIP)
}
} else {
if os.Getenv("FULL_HTTP_REQUEST") != "" {
msg = fmt.Sprintf("%s\n```Query: %s\n%s\nFrom: %s\n\n---------- FULL REQUEST ----------\n%s\n----------------------------------", host, query, userAgent, fromIP, response)
} else {
if os.Getenv("FULL_HTTP_REQUEST") != "" {
msg = fmt.Sprintf("%s\n```Query: %s\n%s\nFrom: %s\n\n---------- FULL REQUEST ----------\n%s\n----------------------------------", host, query, userAgent, fromIP, response)
} else {
msg = fmt.Sprintf("%s\n```Query: %s\n%s\nFrom: %s", host, query, userAgent, fromIP)
}
msg = fmt.Sprintf("%s\n```Query: %s\n%s\nFrom: %s", host, query, userAgent, fromIP)
}
}

go sendMsg(msg + "```")
if os.Getenv("DEBUG") == "true" {
logger("INFO", fromIP+" - "+host)
}
go sendMsg(msg + "```")
if os.Getenv("DEBUG") == "true" {
logger("INFO", fromIP+" - "+host)
}
}
}

conn.Write([]byte(" ")) // necessary as a 0 byte response triggers some clients to resend the request
conn.Close() // v. important lol
return true
return httpRespond(conn)
}
11 changes: 6 additions & 5 deletions libknary/notificationctrl.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,18 @@ func (a *blacklist) updateD(term string) bool {
if term == "" {
return false // would happen if there's no X-Forwarded-For header
}
item := standerdiseListItem(term)
a.mutex.Lock()
a.deny[item] = time.Now()
a.deny[term] = time.Now()
a.mutex.Unlock()
return true
}

// search for a denied domain/IP
func (a *blacklist) searchD(term string) bool {
item := standerdiseListItem(term)
a.mutex.Lock()
defer a.mutex.Unlock()

if _, ok := a.deny[item]; ok {
if _, ok := a.deny[term]; ok {
return true // found!
}
return false
Expand Down Expand Up @@ -116,7 +114,7 @@ func LoadBlacklist() (bool, error) {

for scanner.Scan() { // foreach denied item
if scanner.Text() != "" {
denied.updateD(scanner.Text())
denied.updateD(standerdiseListItem(scanner.Text()))
denyCount++
}
}
Expand All @@ -138,6 +136,7 @@ func inAllowlist(needles ...string) bool {
// strict matching. don't match subdomains
if needle == allowed[i].allow {
if os.Getenv("DEBUG") == "true" {
logger("INFO", "Found "+needle+" in allowlist (strict mode)")
Printy(needle+" matches allowlist", 3)
}
return true
Expand All @@ -146,6 +145,7 @@ func inAllowlist(needles ...string) bool {
// allow fuzzy matching
if strings.HasSuffix(needle, allowed[i].allow) {
if os.Getenv("DEBUG") == "true" {
logger("INFO", "Found "+needle+" in allowlist")
Printy(needle+" matches allowlist", 3)
}
return true
Expand All @@ -158,6 +158,7 @@ func inAllowlist(needles ...string) bool {

func inBlacklist(needles ...string) bool {
for _, needle := range needles {
needle := standerdiseListItem(needle)
if denied.searchD(needle) {
denied.updateD(needle) // found!

Expand Down
4 changes: 2 additions & 2 deletions libknary/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ func HeartBeat(version string, firstrun bool) (bool, error) {

// print allowed items (if any)
if allowCount > 0 {
beatMsg += strconv.Itoa(allowCount) + " allowed subdomains / IPs: \n"
beatMsg += strconv.Itoa(allowCount) + " allowed subdomains, User-Agents, IPs: \n"
if os.Getenv("ALLOWLIST_STRICT") == "true" {
beatMsg += "(Operating in strict mode) \n"
}
Expand All @@ -257,7 +257,7 @@ func HeartBeat(version string, firstrun bool) (bool, error) {

// print denied items (if any)
if denyCount > 0 {
beatMsg += strconv.Itoa(denyCount) + " denied subdomains / User-Agents / IPs: \n"
beatMsg += strconv.Itoa(denyCount) + " denied subdomains, User-Agents, IPs: \n"
beatMsg += "------------------------\n"
for subdomain := range denied.deny {
beatMsg += subdomain + "\n"
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
)

const (
VERSION = "3.4.6"
VERSION = "3.4.7"
GITHUB = "https://github.com/sudosammy/knary"
GITHUBVERSION = "https://raw.githubusercontent.com/sudosammy/knary/master/VERSION"
)
Expand Down

0 comments on commit a46e38a

Please sign in to comment.