Skip to Content
OpenVAPTAlpha
Pre-release. Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. Free for your own use, 100% at your own risk. This website collects no data. Read MoreRead Less
Pre-release. OpenVAPT has no Stable release yet. Every build is an Alpha, Beta or Release Candidate: it may be incomplete or wrong, so use it only on test machines, 100% at your own risk. See Release Channels
Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. These are AI systems, not people or registered engineers. Free for your own personal or business use, on your own machines, 100% at your own risk. Not for sale. Not for providing services. This website collects no data. Read the Acknowledgements
Pre-release. OpenVAPT has no Stable release yet. Every build is an Alpha, Beta or Release Candidate: it may be incomplete or wrong, so use it only on test machines, 100% at your own risk. See Release Channels
Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. These are AI systems, not people or registered engineers. Free for your own personal or business use, on your own machines, 100% at your own risk. Not for sale. Not for providing services. This website collects no data. Read the Acknowledgements
HighNET-DB-010Data ServicesNetwork

BIND Open Recursion

BIND enables recursion without restricting recursive clients.

Why It Matters
An open recursive resolver can be abused for reflection attacks and permits outsiders to consume resolver resources.
How It Detects
Reads named.conf and flags recursion yes when neither allow-recursion nor allow-query-cache restricts clients.
How to Fix
Disable recursion on authoritative-only servers, or set allow-recursion and allow-query-cache to explicit trusted networks.
How to Verify
Run named-checkconf and query recursion from an untrusted test network under an approved test plan.
CVSS v3.1 Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:H
Control Category
Data Services: CIS Controls v8 3, 4, 12; NIST CSF 2.0 PR.DS, PR.PS
Fix Effort
Low
CIS Controls v8 Safeguard
4.1
NIST CSF 2.0 Subcategory
PR.PS-01
CWE
CWE-406
OWASP Top 10
A05:2021
NIST SP 800-53 Rev. 5
SC-5, CM-6, SC-7
Benchmark Topic
Secure data service configuration (topic only, no benchmark text)
References
BIND 9 Administrator Reference Manual - Access Control
Learn the theory behind this check: P3 Network Exposure and Firewalls, P8 Data Services: Databases, File Shares, DNS and Mail.

Source Code

The complete file that defines this check. Run it with openvapt scan --only NET-DB-010, or read it offline with openvapt explain NET-DB-010.

internal/checks/network/services.go · Lines 1-198View on GitHub at This Commit
1package network
2
3import (
4 "bufio"
5 "context"
6 "strings"
7
8 "github.com/openvapt/openvapt/internal/check"
9 "github.com/openvapt/openvapt/internal/checks/collect"
10 "github.com/openvapt/openvapt/internal/sys"
11)
12
13func readAny(ctx context.Context, s sys.System, paths ...string) (string, string, bool, bool) {
14 var found []string
15 var text strings.Builder
16 complete := true
17 for _, p := range paths {
18 r, err := s.ReadFile(ctx, p)
19 if err == nil {
20 found = append(found, p)
21 text.Write(r.Data)
22 text.WriteByte('\n')
23 complete = complete && r.Complete
24 }
25 }
26 if len(found) == 0 {
27 return "", "", false, false
28 }
29 return strings.Join(found, ", "), text.String(), complete, true
30}
31func simpleFileCheck(id, title string, sev check.Severity, cat check.Category, summary, rationale, detection, fix, verify, cvss string, paths []string, ref check.Ref, bad func(string) []string) {
32 check.Register(&check.Check{
33 ID: id, Title: title, Domain: check.DomainNetwork, Severity: sev, Category: cat, Effort: check.EffortLow, CVSS: cvss, Summary: summary, Rationale: rationale, Detection: detection, Fix: fix, Verify: verify, Refs: []check.Ref{ref}, Standards: dataStandards(id), Run: func(ctx context.Context, s sys.System) []check.Result {
34 p, text, complete, ok := readAny(ctx, s, paths...)
35 if !ok {
36 return []check.Result{check.SkipR(strings.Join(paths, ", "), "Configuration was not found")}
37 }
38 ev := bad(text)
39 if len(ev) > 0 {
40 return []check.Result{check.FailR(p, summary, ev...)}
41 }
42 if !complete {
43 return []check.Result{check.IncompleteR(p, "Configuration input was truncated")}
44 }
45 return []check.Result{check.PassR(p, "No unsafe setting was found")}
46 }})
47}
48
49func init() {
50 simpleFileCheck("NET-DB-008", "Unsafe NFS Export Options", check.High, check.CatData, "NFS exports are world-accessible or use no_root_squash or insecure.", "These export options can grant untrusted clients filesystem access or preserve remote root privileges.", "Reads /etc/exports and active exports.d fragments, ignoring comments, and flags *, no_root_squash, and insecure.", "Restrict every export to explicit client CIDRs, use root_squash, remove insecure, and reload exports.", "Run exportfs -v during approved administration and confirm only intended networks and safe options appear.", "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H", []string{"/etc/exports"}, check.Ref{Title: "Linux exports(5)", URL: "https://man7.org/linux/man-pages/man5/exports.5.html"}, func(t string) []string { return matchingLines(t, "*", "no_root_squash", "insecure") })
51 simpleFileCheck("NET-DB-009", "Unsafe Samba Guest or SMB1 Configuration", check.High, check.CatData, "Samba permits guest access, maps unknown users to guest, or allows SMB1.", "Guest shares weaken accountability, while SMB1 lacks modern protocol protections and has a long history of severe flaws.", "Reads smb.conf and flags guest ok = yes, permissive map to guest modes, or a server min protocol below SMB2.", "Set guest ok = no, map to guest = Never, and server min protocol = SMB2_02 or newer. Review each share ACL.", "Run testparm -s during approved administration and confirm the effective values.", "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H", []string{"/etc/samba/smb.conf"}, check.Ref{Title: "Samba smb.conf Manual", URL: "https://www.samba.org/samba/docs/current/man-html/smb.conf.5.html"}, func(t string) []string {
52 l := strings.ToLower(t)
53 var e []string
54 if strings.Contains(l, "guest ok = yes") || strings.Contains(l, "guest ok=yes") {
55 e = append(e, "guest ok enabled")
56 }
57 if strings.Contains(l, "map to guest = bad") || strings.Contains(l, "map to guest=bad") {
58 e = append(e, "unknown users map to guest")
59 }
60 if strings.Contains(l, "server min protocol = nt1") || strings.Contains(l, "server min protocol=nt1") {
61 e = append(e, "SMB1 NT1 enabled")
62 }
63 return e
64 })
65 simpleFileCheck("NET-DB-010", "BIND Open Recursion", check.High, check.CatData, "BIND enables recursion without restricting recursive clients.", "An open recursive resolver can be abused for reflection attacks and permits outsiders to consume resolver resources.", "Reads named.conf and flags recursion yes when neither allow-recursion nor allow-query-cache restricts clients.", "Disable recursion on authoritative-only servers, or set allow-recursion and allow-query-cache to explicit trusted networks.", "Run named-checkconf and query recursion from an untrusted test network under an approved test plan.", "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:H", []string{"/etc/bind/named.conf.options", "/etc/named.conf"}, check.Ref{Title: "BIND 9 Administrator Reference Manual - Access Control", URL: "https://bind9.readthedocs.io/en/latest/reference.html#access-control"}, func(t string) []string {
66 l := strings.ToLower(t)
67 if strings.Contains(l, "recursion yes") && !strings.Contains(l, "allow-recursion") && !strings.Contains(l, "allow-query-cache") {
68 return []string{"recursion yes without a client ACL"}
69 }
70 return nil
71 })
72 simpleFileCheck("NET-DB-011", "BIND Zone Transfers Allowed to Any", check.High, check.CatData, "BIND permits zone transfers to any client.", "Unrestricted transfers disclose the complete DNS namespace and provide attackers with a detailed target inventory.", "Reads named.conf and flags allow-transfer clauses containing any.", "Set allow-transfer to none or explicit secondary server addresses, preferably protected with TSIG.", "Run named-checkconf and verify AXFR is refused from an unauthorized test host.", "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", []string{"/etc/bind/named.conf.options", "/etc/named.conf"}, check.Ref{Title: "BIND 9 Administrator Reference Manual - allow-transfer", URL: "https://bind9.readthedocs.io/en/latest/reference.html#namedconf-statement-allow-transfer"}, func(t string) []string {
73 l := strings.ToLower(t)
74 if strings.Contains(l, "allow-transfer") && strings.Contains(l, "any;") {
75 return []string{"allow-transfer includes any"}
76 }
77 return nil
78 })
79 registerMailChecks()
80 registerExposureChecks()
81}
82
83func registerMailChecks() {
84 simpleFileCheck("NET-DB-012", "Postfix Open Relay Controls", check.High, check.CatData, "Postfix relay restrictions are missing or mynetworks trusts overly broad ranges.", "An open relay enables spam and phishing, damages domain reputation, and can expose the host to blocklisting.", "Parses main.cf for smtpd_relay_restrictions with reject_unauth_destination and flags 0.0.0.0/0 or ::/0 in mynetworks.", "Set smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination and narrow mynetworks.", "Run postconf -n during approved administration and perform only an authorized relay test.", "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L", []string{"/etc/postfix/main.cf"}, check.Ref{Title: "Postfix SMTP Relay and Access Control", URL: "https://www.postfix.org/SMTPD_ACCESS_README.html"}, func(t string) []string {
85 m := collect.ParseKV(t)
86 relay := strings.ToLower(strings.Join(m["smtpd_relay_restrictions"], " "))
87 nets := strings.ToLower(strings.Join(m["mynetworks"], " "))
88 var e []string
89 if !strings.Contains(relay, "reject_unauth_destination") {
90 e = append(e, "reject_unauth_destination missing")
91 }
92 if strings.Contains(nets, "0.0.0.0/0") || strings.Contains(nets, "::/0") {
93 e = append(e, "mynetworks trusts all addresses")
94 }
95 return e
96 })
97 simpleFileCheck("NET-DB-013", "Mail Cleartext Authentication Allowed", check.Medium, check.CatCrypto, "Postfix or Dovecot permits plaintext authentication without requiring TLS.", "Credentials sent before transport encryption can be observed by any party able to intercept the connection.", "Reads Postfix smtpd_tls_auth_only and Dovecot disable_plaintext_auth, and flags no values.", "Set smtpd_tls_auth_only = yes and disable_plaintext_auth = yes. Use implicit TLS on ports 465, 993, and 995. STARTTLS assessment is out of scope.", "Confirm the effective mail configuration and that unauthenticated plaintext sessions do not advertise AUTH.", "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N", []string{"/etc/postfix/main.cf", "/etc/dovecot/conf.d/10-auth.conf"}, check.Ref{Title: "Dovecot Authentication Settings", URL: "https://doc.dovecot.org/main/core/config/auth/settings.html"}, func(t string) []string {
98 m := collect.ParseKV(t)
99 var e []string
100 for _, k := range []string{"smtpd_tls_auth_only", "disable_plaintext_auth"} {
101 for _, v := range m[k] {
102 if strings.EqualFold(strings.TrimSpace(v), "no") {
103 e = append(e, k+" is no")
104 }
105 }
106 }
107 return e
108 })
109}
110
111// controlPorts are control planes and remote desktops: reaching one of them
112// from the network means administrative control of the host or cluster
113// (vendor documentation and the IANA registry). SNMP is not here: it is a
114// cleartext protocol and belongs to NET-SVC-001 only, so one socket never
115// yields more than one finding per check family.
116var controlPorts = map[int]string{2375: "Docker API without TLS", 6443: "Kubernetes API", 10250: "Kubelet API", 2379: "etcd client API", 5900: "VNC", 3389: "RDP"}
117
118// managementPorts are management panels, monitoring exporters and service
119// discovery APIs: sensitive telemetry and configuration, usually behind their
120// own authentication, so exposure is Medium rather than Critical.
121var managementPorts = map[int]string{2376: "Docker API (TLS)", 8500: "Consul HTTP API", 9100: "Prometheus Node Exporter", 15672: "RabbitMQ Management", 10000: "Webmin", 2083: "cPanel", 2087: "WHM"}
122
123func registerExposureChecks() {
124 check.Register(&check.Check{
125 ID: "NET-SVC-002",
126 Title: "Control Plane or Remote Desktop Ports Network-Reachable",
127 Domain: check.DomainNetwork,
128 Severity: check.Critical,
129 Category: check.CatNetwork,
130 Effort: check.EffortMedium,
131 CVSS: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
132 Summary: "Docker API, Kubernetes API, kubelet, etcd, VNC or RDP listen on network-reachable addresses.",
133 Rationale: "These services hand out administrative control of the host or the cluster. The unauthenticated Docker API and etcd give code execution or every cluster secret to whoever connects; RDP and VNC are the most attacked remote access services.",
134 Detection: "Parses local TCP and UDP socket tables and matches listeners bound to a non-loopback address against the default ports of these control planes and remote desktops. Severity follows the service identity: management panels and exporters are rated separately by NET-SVC-004. The binding (all interfaces, private or public address) is recorded as evidence. No packets are sent.",
135 Fix: "Bind control planes to loopback or a dedicated management address, require mutual TLS or strong authentication, and filter access to approved source networks or a VPN.",
136 Verify: "Review ss -tulpn and the host firewall from a trusted administrative session.",
137 Refs: []check.Ref{{Title: "Docker Protect the Daemon Socket", URL: "https://docs.docker.com/engine/security/protect-access/"}, {Title: "Kubernetes Ports and Protocols", URL: "https://kubernetes.io/docs/reference/networking/ports-and-protocols/"}, {Title: "IANA Service Name and Transport Protocol Port Number Registry", URL: "https://www.iana.org/assignments/service-names-port-numbers"}},
138 Standards: check.Standards{CWE: []string{"CWE-1327", "CWE-923"}, OWASP: []string{"A05:2021"}, NIST: []string{"AC-17", "CM-7", "SC-7"}},
139 Run: portListRun(controlPorts, "Control plane or remote desktop ports are network-reachable", "No control plane or remote desktop port is network-reachable"),
140 })
141 check.Register(&check.Check{
142 ID: "NET-SVC-004",
143 Title: "Management Panel or Monitoring Ports Network-Reachable",
144 Domain: check.DomainNetwork,
145 Severity: check.Medium,
146 Category: check.CatNetwork,
147 Effort: check.EffortLow,
148 CVSS: "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N",
149 Summary: "Webmin, cPanel, WHM, RabbitMQ Management, Consul, the Docker API with TLS or the Prometheus node exporter listen on network-reachable addresses.",
150 Rationale: "Management panels and exporters publish host metrics, service configuration and often a login form. They are a reconnaissance gift and a password guessing target, but they sit behind their own authentication or TLS, so the risk is lower than an open control plane.",
151 Detection: "Parses local TCP and UDP socket tables and matches listeners bound to a non-loopback address against the default ports of these management panels, exporters and discovery APIs. The binding (all interfaces, private or public address) is recorded as evidence. No packets are sent.",
152 Fix: "Bind the panel or exporter to 127.0.0.1 or a management address, put it behind a reverse proxy with authentication, or restrict the port to monitoring hosts in the firewall.",
153 Verify: "ss -tulpn shows these ports on 127.0.0.1, ::1 or a management address only.",
154 Refs: []check.Ref{{Title: "Prometheus, Security model", URL: "https://prometheus.io/docs/operating/security/"}, {Title: "IANA Service Name and Transport Protocol Port Number Registry", URL: "https://www.iana.org/assignments/service-names-port-numbers"}},
155 Standards: check.Standards{CWE: []string{"CWE-1188", "CWE-1327"}, OWASP: []string{"A05:2021"}, NIST: []string{"AC-17", "CM-7", "SC-7"}},
156 Run: portListRun(managementPorts, "Management panel or monitoring ports are network-reachable", "No management panel or monitoring port is network-reachable"),
157 })
158}
159
160// portListRun builds the Run function of a check that reports every
161// network-reachable listener whose port is in ports.
162func portListRun(ports map[int]string, failMsg, passMsg string) func(context.Context, sys.System) []check.Result {
163 return func(_ context.Context, s sys.System) []check.Result {
164 ls, err := collect.Listeners(s)
165 if err != nil {
166 return []check.Result{check.IncompleteR("/proc/net", "Socket tables were unreadable or partial")}
167 }
168 var ev []string
169 for _, l := range ls {
170 if n, ok := ports[l.Port]; ok && l.Public() {
171 ev = append(ev, l.String()+" "+n+", bound to "+exposure(l))
172 }
173 }
174 if len(ev) > 0 {
175 return []check.Result{check.FailR("sockets", failMsg, ev...)}
176 }
177 return []check.Result{check.PassR("sockets", passMsg)}
178 }
179}
180
181func matchingLines(t string, needles ...string) []string {
182 var out []string
183 s := bufio.NewScanner(strings.NewReader(t))
184 for s.Scan() {
185 line := strings.TrimSpace(s.Text())
186 if line == "" || strings.HasPrefix(line, "#") {
187 continue
188 }
189 l := strings.ToLower(line)
190 for _, n := range needles {
191 if strings.Contains(l, strings.ToLower(n)) {
192 out = append(out, line)
193 break
194 }
195 }
196 }
197 return out
198}

Also available as plain Markdown for notes, scripts and AI tools. Same facts, same source, no styling.

Cite This Page

If this page helped your work, please cite it. The version and web address are filled in; the access date is today's date in your browser. Every other style, and when credit is required, is on How to Cite OpenVAPT.

APA 7
OpenVAPT. (2026). NET-DB-010 BIND Open Recursion (Version 0.1.0-alpha.6). OpenVAPT Check Catalogue. Retrieved October 16, 2026, from https://openvapt.com/checks/NET-DB-010.html

In text: (OpenVAPT, 2026). Accessed: the date shown is today's date from your own browser (the site's build date without JavaScript); change it to the day you read the page.

BibTeX
@misc{openvapt_net_db_010,
  author       = {{OpenVAPT}},
  title        = {{NET-DB-010 BIND Open Recursion}},
  year         = {2026},
  howpublished = {\url{https://openvapt.com/checks/NET-DB-010.html}},
  note         = {OpenVAPT Check Catalogue, version 0.1.0-alpha.6. Accessed 16 October 2026}
}

Accessed: the date shown is today's date from your own browser (the site's build date without JavaScript); change it to the day you read the page.

Citing OpenVAPT does not mean OpenVAPT endorses, approves or has reviewed your work.