FTP Server Installed or Enabled
Checks whether an FTP server (vsftpd, ProFTPD or Pure-FTPd) is installed or set to start at boot.
- Why It Matters
- Plain FTP sends passwords and files unencrypted and needs a wide range of ports open. SFTP over SSH, which the SSH server already provides, does the same job encrypted. An FTP server is usually a leftover from a web hosting panel. If this machine is one of the hosts that must serve legacy FTP clients that legitimately run it, record the reason in the suppression file so the finding stays visible but accepted.
- How It Detects
- Reads installed packages from /var/lib/dpkg/status (only entries marked 'install ok installed'; /var/lib/dpkg/info/*.list is a fallback used only when the status file cannot be read) and the allow-listed rpm -qa, and looks for vsftpd, proftpd-core, proftpd-basic, proftpd, pure-ftpd, pure-ftpd-common. Reads the *.wants links under /etc/systemd/system (and asks the allow-listed systemctl is-enabled) for vsftpd.service, proftpd.service, pure-ftpd.service. Either an installed package or an enabled unit fails. Incomplete when no package database and no systemd unit directory could be read.
- How to Fix
- Move users to SFTP (the OpenSSH internal-sftp subsystem), then apt purge vsftpd proftpd-core pure-ftpd or dnf remove vsftpd proftpd and systemctl disable --now vsftpd . If FTP must stay, enforce TLS (FTPS) and restrict it to named users. Before removing a package, check that no application on this machine or the network still uses the service, and keep console access when the service is remote-access related.
- How to Verify
dpkg -l vsftpd proftpd-core proftpd-basic proftpd pure-ftpd pure-ftpd-common 2>/dev/null | grep '^ii' ; systemctl is-enabled vsftpd.service proftpd.service pure-ftpd.service 2>/dev/null- CVSS v3.1 Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N- Control Category
- Secure Configuration: CIS Controls v8 4; NIST CSF 2.0 PR.PS
- Fix Effort
- Low
- CIS Controls v8 Safeguard
- 4.8
- NIST CSF 2.0 Subcategory
- PR.PS-01, PR.IR-01
- CWE
- CWE-319, CWE-1188
- NIST SP 800-53 Rev. 5
- CM-7, CM-7(1), SC-8
- Benchmark Topic
- FTP server is not installed (CIS Controls v8 4.8) (topic only, no benchmark text)
- References
- MITRE ATT&CK T1040, Network Sniffing
NIST SP 800-123, Guide to General Server Security
Source Code
The complete file that defines this check. Run it with openvapt scan --only HOST-SVC-005, or read it offline with openvapt explain HOST-SVC-005.
internal/checks/host/legacy_services.go · Lines 1-214View on GitHub at This Commit
| 1 | package host |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "fmt" |
| 6 | "strings" |
| 7 | |
| 8 | "github.com/openvapt/openvapt/internal/check" |
| 9 | "github.com/openvapt/openvapt/internal/sys" |
| 10 | ) |
| 11 | |
| 12 | // HOST-SVC inventory: legacy and rarely needed services that are installed |
| 13 | // or enabled at boot. HOST-SVC-001 asks systemd what is active right now; |
| 14 | // these checks look at what is installed (dpkg status, dpkg info, rpm) and |
| 15 | // what is wanted at boot (*.wants links, systemctl is-enabled), because a |
| 16 | // service that is installed but stopped comes back with the next package |
| 17 | // upgrade or reboot and is one command away from running. |
| 18 | |
| 19 | // legacyRule describes one family of services. One rule becomes one check. |
| 20 | type legacyRule struct { |
| 21 | id, title string |
| 22 | sev check.Severity |
| 23 | cvss string |
| 24 | cwe []string |
| 25 | pkgs, units []string |
| 26 | summary, rationale, fix string |
| 27 | cisTopic string |
| 28 | refs []check.Ref |
| 29 | cleartext bool // the protocol itself sends credentials or data unencrypted |
| 30 | whenNeeded string |
| 31 | } |
| 32 | |
| 33 | var ( |
| 34 | cvssLegacyLow = "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N" |
| 35 | cvssLegacyMedium = "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N" |
| 36 | refTelnetRisk = check.Ref{Title: "MITRE ATT&CK T1040, Network Sniffing", URL: "https://attack.mitre.org/techniques/T1040/"} |
| 37 | refNFSSec = check.Ref{Title: "NFS security, Linux kernel documentation (exports and rpcbind)", URL: "https://docs.kernel.org/filesystems/nfs/index.html"} |
| 38 | ) |
| 39 | |
| 40 | var legacyRules = []legacyRule{ |
| 41 | { |
| 42 | id: "HOST-SVC-002", title: "Telnet Server Installed or Enabled", sev: check.Medium, cvss: cvssLegacyMedium, |
| 43 | cwe: []string{"CWE-319", "CWE-1188"}, |
| 44 | pkgs: []string{"telnetd", "inetutils-telnetd", "telnetd-ssl", "telnet-server"}, |
| 45 | units: []string{"telnet.socket", "telnetd.service", "inetutils-telnetd.service"}, |
| 46 | summary: "Checks whether a Telnet server is installed or set to start at boot.", |
| 47 | rationale: "Telnet sends user names, passwords and every keystroke in the clear. Anyone on the network path reads them. SSH replaced it decades ago; a Telnet server on a modern server is almost always forgotten rather than needed.", |
| 48 | fix: "apt purge telnetd inetutils-telnetd or dnf remove telnet-server , and systemctl disable --now telnet.socket . Use SSH.", |
| 49 | cisTopic: "Telnet server is not installed (CIS Controls v8 4.8)", refs: []check.Ref{refTelnetRisk, refNIST800123}, cleartext: true, |
| 50 | }, |
| 51 | { |
| 52 | id: "HOST-SVC-003", title: "rsh, rlogin, rexec or finger Server Installed or Enabled", sev: check.Medium, cvss: cvssLegacyMedium, |
| 53 | cwe: []string{"CWE-319", "CWE-1188"}, |
| 54 | pkgs: []string{"rsh-server", "rsh-redone-server", "fingerd", "finger-server", "efingerd", "cfingerd"}, |
| 55 | units: []string{"rsh.socket", "rlogin.socket", "rexec.socket", "finger.socket", "rsh-redone-server.service", "fingerd.service"}, |
| 56 | summary: "Checks whether the Berkeley r-services (rsh, rlogin, rexec) or a finger server is installed or enabled.", |
| 57 | rationale: "The r-services trust the client's claim of who it is and send everything unencrypted; .rhosts files (HOST-USR-007) exist for them. finger lists account names and login times to anyone who asks, which is reconnaissance for password guessing.", |
| 58 | fix: "apt purge rsh-server rsh-redone-server fingerd or dnf remove rsh-server finger-server , and systemctl disable --now rsh.socket rlogin.socket rexec.socket finger.socket .", |
| 59 | cisTopic: "rsh and finger servers are not installed (CIS Controls v8 4.8)", refs: []check.Ref{refTelnetRisk, refNIST800123}, cleartext: true, |
| 60 | }, |
| 61 | { |
| 62 | id: "HOST-SVC-004", title: "TFTP Server Installed or Enabled", sev: check.Medium, cvss: cvssLegacyMedium, |
| 63 | cwe: []string{"CWE-306", "CWE-1188"}, |
| 64 | pkgs: []string{"tftpd-hpa", "atftpd", "tftp-server", "tftpd"}, |
| 65 | units: []string{"tftpd-hpa.service", "atftpd.service", "tftp.socket", "tftp.service"}, |
| 66 | summary: "Checks whether a TFTP server is installed or set to start at boot.", |
| 67 | rationale: "TFTP has no authentication and no encryption: any client that can reach it can read, and often write, every file in its directory. It is needed only on network boot (PXE) servers and for some network equipment, and is a frequent source of leaked configuration files.", |
| 68 | fix: "apt purge tftpd-hpa atftpd or dnf remove tftp-server , and systemctl disable --now tftpd-hpa.service tftp.socket . Where PXE is required, serve only that directory, read-only, from a dedicated host.", |
| 69 | cisTopic: "TFTP server is not installed (CIS Controls v8 4.8)", refs: []check.Ref{refNIST800123}, cleartext: true, |
| 70 | whenNeeded: "network boot (PXE) servers", |
| 71 | }, |
| 72 | { |
| 73 | id: "HOST-SVC-005", title: "FTP Server Installed or Enabled", sev: check.Medium, cvss: cvssLegacyMedium, |
| 74 | cwe: []string{"CWE-319", "CWE-1188"}, |
| 75 | pkgs: []string{"vsftpd", "proftpd-core", "proftpd-basic", "proftpd", "pure-ftpd", "pure-ftpd-common"}, |
| 76 | units: []string{"vsftpd.service", "proftpd.service", "pure-ftpd.service"}, |
| 77 | summary: "Checks whether an FTP server (vsftpd, ProFTPD or Pure-FTPd) is installed or set to start at boot.", |
| 78 | rationale: "Plain FTP sends passwords and files unencrypted and needs a wide range of ports open. SFTP over SSH, which the SSH server already provides, does the same job encrypted. An FTP server is usually a leftover from a web hosting panel.", |
| 79 | fix: "Move users to SFTP (the OpenSSH internal-sftp subsystem), then apt purge vsftpd proftpd-core pure-ftpd or dnf remove vsftpd proftpd and systemctl disable --now vsftpd . If FTP must stay, enforce TLS (FTPS) and restrict it to named users.", |
| 80 | cisTopic: "FTP server is not installed (CIS Controls v8 4.8)", refs: []check.Ref{refTelnetRisk, refNIST800123}, cleartext: true, |
| 81 | whenNeeded: "hosts that must serve legacy FTP clients", |
| 82 | }, |
| 83 | { |
| 84 | id: "HOST-SVC-006", title: "NIS Server or Client Installed or Enabled", sev: check.Medium, cvss: cvssLegacyMedium, |
| 85 | cwe: []string{"CWE-319", "CWE-522"}, |
| 86 | pkgs: []string{"nis", "ypserv", "ypbind", "yp-tools"}, |
| 87 | units: []string{"ypserv.service", "ypbind.service", "nis.service", "yppasswdd.service"}, |
| 88 | summary: "Checks whether the Network Information Service (NIS, ypserv or ypbind) is installed or enabled.", |
| 89 | rationale: "NIS distributes account and password hash databases over the network without encryption or strong authentication; anyone who can reach the server can request the whole password map. HOST-USR-006 looks for its + entries in the account files; this check looks for the software itself.", |
| 90 | fix: "Replace NIS with a modern directory (SSSD with LDAPS or FreeIPA), then apt purge nis or dnf remove ypserv ypbind and systemctl disable --now ypserv ypbind .", |
| 91 | cisTopic: "NIS server and client are not installed (CIS Controls v8 4.8)", refs: []check.Ref{refTelnetRisk, refNIST800123}, cleartext: true, |
| 92 | }, |
| 93 | { |
| 94 | id: "HOST-SVC-007", title: "Avahi or CUPS Installed or Enabled", sev: check.Low, cvss: cvssLegacyLow, |
| 95 | cwe: []string{"CWE-1188"}, |
| 96 | pkgs: []string{"avahi-daemon", "cups", "cups-daemon"}, |
| 97 | units: []string{"avahi-daemon.service", "avahi-daemon.socket", "cups.service", "cups.socket", "cups-browsed.service"}, |
| 98 | summary: "Checks whether zero-configuration discovery (Avahi) or the printing system (CUPS) is installed or enabled.", |
| 99 | rationale: "Avahi announces the machine and its services to the local network, and CUPS listens for print jobs and runs a web interface. Both are desktop features with a history of vulnerabilities and no purpose on a server.", |
| 100 | fix: "apt purge avahi-daemon cups cups-daemon or dnf remove avahi cups , and systemctl disable --now avahi-daemon.socket avahi-daemon.service cups.socket cups.service .", |
| 101 | cisTopic: "Avahi and CUPS are not installed (CIS Controls v8 4.8)", refs: []check.Ref{refNIST800123}, |
| 102 | whenNeeded: "desktops and print servers", |
| 103 | }, |
| 104 | { |
| 105 | id: "HOST-SVC-008", title: "NFS Server or rpcbind Installed or Enabled", sev: check.Low, cvss: cvssLegacyLow, |
| 106 | cwe: []string{"CWE-1188", "CWE-306"}, |
| 107 | pkgs: []string{"nfs-kernel-server", "rpcbind"}, |
| 108 | units: []string{"nfs-server.service", "rpcbind.service", "rpcbind.socket", "nfs-kernel-server.service"}, |
| 109 | summary: "Checks whether the NFS server or the RPC port mapper (rpcbind) is installed or enabled.", |
| 110 | rationale: "NFS exports whole directory trees with trust based on client IP address and numeric user ids, and rpcbind tells any caller which RPC services are running. Unless this machine is a file server, both are attack surface with no benefit. The rpcbind package is often pulled in as a dependency and left running.", |
| 111 | fix: "apt purge nfs-kernel-server rpcbind or dnf remove nfs-utils rpcbind where NFS is not served, and systemctl disable --now nfs-server rpcbind.socket rpcbind.service . Where NFS is required, export read-only with root_squash to named hosts and firewall port 2049 and 111.", |
| 112 | cisTopic: "NFS and rpcbind are not installed unless required (CIS Controls v8 4.8)", refs: []check.Ref{refNFSSec, refNIST800123}, |
| 113 | whenNeeded: "file servers", |
| 114 | }, |
| 115 | { |
| 116 | id: "HOST-SVC-009", title: "DHCP or DNS Server Installed or Enabled", sev: check.Low, cvss: cvssLegacyLow, |
| 117 | cwe: []string{"CWE-1188"}, |
| 118 | pkgs: []string{"isc-dhcp-server", "dhcp-server", "kea-dhcp4-server", "kea-dhcp6-server", "bind9", "bind", "bind9-dnsutils-server"}, |
| 119 | units: []string{"isc-dhcp-server.service", "dhcpd.service", "kea-dhcp4-server.service", "named.service", "bind9.service"}, |
| 120 | summary: "Checks whether a DHCP server or an authoritative or recursive DNS server (BIND) is installed or enabled on a machine that is not a network server.", |
| 121 | rationale: "A rogue DHCP server hands out addresses and gateways to the whole network segment, and BIND is a large, privileged network service with a long vulnerability history. Both belong on dedicated infrastructure hosts. On a web or application server they are either forgotten or an attacker's foothold.", |
| 122 | fix: "apt purge isc-dhcp-server bind9 or dnf remove dhcp-server bind , and systemctl disable --now isc-dhcp-server named . If this machine is meant to be a DNS or DHCP server, record that in the suppression file and review NET-SVC findings for its configuration.", |
| 123 | cisTopic: "DHCP and DNS servers are not installed unless required (CIS Controls v8 4.8)", refs: []check.Ref{refNIST800123}, |
| 124 | whenNeeded: "dedicated network infrastructure hosts", |
| 125 | }, |
| 126 | { |
| 127 | id: "HOST-SVC-010", title: "Samba File Server Installed or Enabled", sev: check.Low, cvss: cvssLegacyLow, |
| 128 | cwe: []string{"CWE-1188"}, |
| 129 | pkgs: []string{"samba"}, |
| 130 | units: []string{"smbd.service", "nmbd.service", "smb.service", "nmb.service", "samba.service"}, |
| 131 | summary: "Checks whether the Samba SMB file server is installed or set to start at boot.", |
| 132 | rationale: "SMB file sharing exposes files, printers and authentication to the network and has been the target of worms and several critical remote code execution flaws. It is needed only on file servers for Windows-style clients.", |
| 133 | fix: "apt purge samba or dnf remove samba , and systemctl disable --now smbd nmbd (smb nmb on the RHEL family). Where it is required, disable SMB1, require signing and restrict shares to named users.", |
| 134 | cisTopic: "Samba is not installed unless required (CIS Controls v8 4.8)", refs: []check.Ref{refNIST800123}, |
| 135 | whenNeeded: "file servers", |
| 136 | }, |
| 137 | { |
| 138 | id: "HOST-SVC-011", title: "SNMP Agent or Squid Proxy Installed or Enabled", sev: check.Low, cvss: cvssLegacyLow, |
| 139 | cwe: []string{"CWE-1188", "CWE-319"}, |
| 140 | pkgs: []string{"snmpd", "net-snmp", "squid", "squid-openssl"}, |
| 141 | units: []string{"snmpd.service", "squid.service"}, |
| 142 | summary: "Checks whether an SNMP agent or the Squid caching proxy is installed or enabled.", |
| 143 | rationale: "SNMP v1 and v2c authenticate with a community string sent in clear text (public and private are the usual defaults) and reveal routing tables, interfaces, processes and installed software. Squid turns the machine into a relay for other traffic and is often left open. Both need a deliberate configuration to be anything but a liability.", |
| 144 | fix: "apt purge snmpd squid or dnf remove net-snmp squid , and systemctl disable --now snmpd squid . If monitoring needs SNMP, use version 3 with authPriv and bind the agent to the management network.", |
| 145 | cisTopic: "SNMP and proxy servers are not installed unless required (CIS Controls v8 4.8)", refs: []check.Ref{refNIST800123}, |
| 146 | whenNeeded: "monitored hosts using SNMPv3 and dedicated proxy servers", |
| 147 | }, |
| 148 | { |
| 149 | id: "HOST-SVC-012", title: "inetd Super-Server Installed or Enabled", sev: check.Low, cvss: cvssLegacyLow, |
| 150 | cwe: []string{"CWE-1188"}, |
| 151 | pkgs: []string{"xinetd", "openbsd-inetd", "inetutils-inetd"}, |
| 152 | units: []string{"xinetd.service", "inetd.service", "openbsd-inetd.service", "inetutils-inetd.service"}, |
| 153 | summary: "Checks whether xinetd or another inetd super-server is installed or enabled.", |
| 154 | rationale: "inetd-style super-servers exist to launch small network services (echo, chargen, telnet, tftp) on demand from a shared configuration. On a systemd machine every service they would start has a native unit; the super-server adds a second, rarely reviewed, place where network services are switched on.", |
| 155 | fix: "apt purge xinetd openbsd-inetd or dnf remove xinetd , and systemctl disable --now xinetd . Review /etc/inetd.conf and /etc/xinetd.d first so nothing needed disappears.", |
| 156 | cisTopic: "xinetd and inetd are not installed (CIS Controls v8 4.8)", refs: []check.Ref{refNIST800123}, |
| 157 | }, |
| 158 | } |
| 159 | |
| 160 | func init() { |
| 161 | for _, r := range legacyRules { |
| 162 | registerLegacy(r) |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | func registerLegacy(r legacyRule) { |
| 167 | detect := "Reads installed packages from /var/lib/dpkg/status (only entries marked 'install ok installed'; /var/lib/dpkg/info/*.list is a fallback used only when the status file cannot be read) and the allow-listed rpm -qa, and looks for " + strings.Join(r.pkgs, ", ") + |
| 168 | ". Reads the *.wants links under /etc/systemd/system (and asks the allow-listed systemctl is-enabled) for " + strings.Join(r.units, ", ") + |
| 169 | ". Either an installed package or an enabled unit fails. Incomplete when no package database and no systemd unit directory could be read." |
| 170 | cwe := r.cwe |
| 171 | nist := []string{"CM-7", "CM-7(1)"} |
| 172 | if r.cleartext { |
| 173 | nist = append(nist, "SC-8") |
| 174 | } |
| 175 | rationale := r.rationale |
| 176 | if r.whenNeeded != "" { |
| 177 | rationale += " If this machine is one of the " + r.whenNeeded + " that legitimately run it, record the reason in the suppression file so the finding stays visible but accepted." |
| 178 | } |
| 179 | check.Register(&check.Check{ |
| 180 | ID: r.id, Title: r.title, Domain: check.DomainHost, |
| 181 | Severity: r.sev, Category: check.CatConfig, Effort: check.EffortLow, |
| 182 | CVSS: r.cvss, |
| 183 | Summary: r.summary, |
| 184 | Rationale: rationale, |
| 185 | Detection: detect, |
| 186 | Fix: r.fix + " Before removing a package, check that no application on this machine or the network still uses the service, and keep console access when the service is remote-access related.", |
| 187 | Verify: "dpkg -l " + strings.Join(r.pkgs, " ") + " 2>/dev/null | grep '^ii' ; systemctl is-enabled " + strings.Join(r.units, " ") + " 2>/dev/null", |
| 188 | Refs: r.refs, |
| 189 | Standards: check.Standards{CWE: cwe, NIST: nist, CISTopic: r.cisTopic}, |
| 190 | Run: func(ctx context.Context, s sys.System) []check.Result { |
| 191 | pkgs, pkgOK := installedPackages(ctx, s) |
| 192 | unitsOK := exists(s, "/etc/systemd/system") |
| 193 | if !pkgOK && !unitsOK { |
| 194 | return []check.Result{check.IncompleteR("services", "No package database (dpkg or rpm) and no systemd unit directory could be read")} |
| 195 | } |
| 196 | var ev []string |
| 197 | for _, p := range packagesPresent(pkgs, r.pkgs...) { |
| 198 | ev = append(ev, "package "+p+" is installed") |
| 199 | } |
| 200 | for _, u := range enabledUnits(ctx, s, r.units...) { |
| 201 | ev = append(ev, "unit "+u+" is enabled at boot") |
| 202 | } |
| 203 | if len(ev) > 0 { |
| 204 | return []check.Result{check.FailR("services", fmt.Sprintf("%d legacy service component(s) present", len(ev)), ev...)} |
| 205 | } |
| 206 | msg := "Not installed and not enabled" |
| 207 | if !pkgOK { |
| 208 | msg = "Not enabled at boot (no package database was readable, so installation could not be confirmed)" |
| 209 | return []check.Result{check.IncompleteR("services", msg)} |
| 210 | } |
| 211 | return []check.Result{check.PassR("services", msg)} |
| 212 | }, |
| 213 | }) |
| 214 | } |
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.
OpenVAPT. (2026). HOST-SVC-005 FTP Server Installed or Enabled (Version 0.1.0-alpha.6). OpenVAPT Check Catalogue. Retrieved October 16, 2026, from https://openvapt.com/checks/HOST-SVC-005.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.
@misc{openvapt_host_svc_005,
author = {{OpenVAPT}},
title = {{HOST-SVC-005 FTP Server Installed or Enabled}},
year = {2026},
howpublished = {\url{https://openvapt.com/checks/HOST-SVC-005.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.