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
HighHOST-SSH-001Identity and AuthenticationServer

SSH Allows Direct Root Login

Checks whether the SSH daemon accepts logins as root with a password.

Why It Matters
Root is the one account name every attacker knows. Allowing it over SSH turns any password guess into full control of the server, and removes the audit trail of which person used root.
How It Detects
Reads the effective sshd configuration (sshd -T, or the config files as a fallback) and inspects PermitRootLogin. The value 'yes' fails. When the keyword is absent from the files (sshd -T always prints it), the installed OpenSSH version decides: 7.0 and later default to prohibit-password and pass with that noted; older versions defaulted to yes and fail; an unknown version is Incomplete, because the effective value needs sshd -T as root. 'prohibit-password', 'without-password', 'forced-commands-only' and 'no' pass. A risky value set inside a Match block in the config files fails as a conditional finding, with the Match condition in the evidence.
How to Fix
In /etc/ssh/sshd_config set: PermitRootLogin no (or prohibit-password if key-based root login is truly required). Log in as a named user and use sudo. Then run: sshd -t && systemctl reload sshd
How to Verify
sshd -T | grep -i permitrootlogin should print 'permitrootlogin no'.
CVSS v3.1 Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
Control Category
Identity and Authentication: CIS Controls v8 5, 6; NIST CSF 2.0 PR.AA
Fix Effort
Low
CIS Controls v8 Safeguard
5.4
NIST CSF 2.0 Subcategory
PR.AA-05
CWE
CWE-250
NIST SP 800-53 Rev. 5
AC-6(2), IA-2
Benchmark Topic
SSH server configuration: root login (topic only, no benchmark text)
References
OpenBSD, sshd_config(5) manual page
NIST SP 800-123, Guide to General Server Security
Learn the theory behind this check: P2 SSH Security, P15 Access Control: Accounts, Sudo and Logins, P23 Test Design for Security Checks, A2 Remediation and Verification.

Source Code

The complete file that defines this check. Run it with openvapt scan --only HOST-SSH-001, or read it offline with openvapt explain HOST-SSH-001.

internal/checks/host/ssh_root_login.go · Lines 1-67View on GitHub at This Commit
1package host
2
3import (
4 "context"
5
6 "github.com/openvapt/openvapt/internal/check"
7 "github.com/openvapt/openvapt/internal/checks/collect"
8 "github.com/openvapt/openvapt/internal/sys"
9)
10
11// rootLoginUnset decides an absent PermitRootLogin from the installed
12// OpenSSH version: the default changed from yes to prohibit-password in
13// OpenSSH 7.0 (2015), so a modern server with the keyword unset is not
14// exposed, and reporting High there would be a false positive.
15func rootLoginUnset(ctx context.Context, s sys.System, cfg map[string]string) check.Result {
16 major, minor, raw, ok := collect.OpenSSHVersion(ctx, s)
17 if !ok {
18 return check.IncompleteR("sshd", "PermitRootLogin is not set and the OpenSSH version is unknown; the effective value needs sshd -T (run as root)", "source: "+sourceOr(cfg[collect.SSHDSourceKey], "unknown"))
19 }
20 ver := "openssh-server " + raw
21 if major >= 7 {
22 return check.PassR("sshd", "PermitRootLogin is not set; OpenSSH 7.0 and later default to prohibit-password", "permitrootlogin (default) prohibit-password", ver)
23 }
24 _ = minor
25 return check.FailR("sshd", "PermitRootLogin is not set and this OpenSSH release defaults to yes", "permitrootlogin (default) yes", ver)
26}
27
28func sourceOr(v, d string) string {
29 if v == "" {
30 return d
31 }
32 return v
33}
34
35func init() {
36 check.Register(&check.Check{
37 ID: "HOST-SSH-001",
38 Title: "SSH Allows Direct Root Login",
39 Domain: check.DomainHost,
40 Severity: check.High,
41 CVSS: "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
42 Summary: "Checks whether the SSH daemon accepts logins as root with a password.",
43 Rationale: "Root is the one account name every attacker knows. Allowing it over SSH turns any password guess into full control of the server, and removes the audit trail of which person used root.",
44 Detection: "Reads the effective sshd configuration (sshd -T, or the config files as a fallback) and inspects PermitRootLogin. The value 'yes' fails. When the keyword is absent from the files (sshd -T always prints it), the installed OpenSSH version decides: 7.0 and later default to prohibit-password and pass with that noted; older versions defaulted to yes and fail; an unknown version is Incomplete, because the effective value needs sshd -T as root. 'prohibit-password', 'without-password', 'forced-commands-only' and 'no' pass. A risky value set inside a Match block in the config files fails as a conditional finding, with the Match condition in the evidence.",
45 Fix: "In /etc/ssh/sshd_config set: PermitRootLogin no (or prohibit-password if key-based root login is truly required). Log in as a named user and use sudo. Then run: sshd -t && systemctl reload sshd",
46 Verify: "sshd -T | grep -i permitrootlogin should print 'permitrootlogin no'.",
47 Refs: []check.Ref{refSSHDManual, refNIST800123},
48 Standards: check.Standards{CWE: []string{"CWE-250"}, NIST: []string{"AC-6(2)", "IA-2"}, CISTopic: "SSH server configuration: root login"},
49 Run: func(ctx context.Context, s sys.System) []check.Result {
50 cfg, ok := collect.SSHD(ctx, s)
51 if !ok {
52 return []check.Result{check.SkipR("sshd", "SSH daemon configuration not found")}
53 }
54 v, set := cfg["permitrootlogin"]
55 if v == "yes" {
56 return []check.Result{check.FailR("sshd", "Root may log in over SSH with a password", "permitrootlogin "+v)}
57 }
58 if !set {
59 return []check.Result{rootLoginUnset(ctx, s, cfg)}
60 }
61 if r, bad := sshMatchFail(cfg, "Root may log in over SSH with a password", "permitrootlogin"); bad {
62 return []check.Result{r}
63 }
64 return []check.Result{check.PassR("sshd", "Direct root password login is disabled", "permitrootlogin "+v)}
65 },
66 })
67}

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). HOST-SSH-001 SSH Allows Direct Root Login (Version 0.1.0-alpha.6). OpenVAPT Check Catalogue. Retrieved October 16, 2026, from https://openvapt.com/checks/HOST-SSH-001.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_host_ssh_001,
  author       = {{OpenVAPT}},
  title        = {{HOST-SSH-001 SSH Allows Direct Root Login}},
  year         = {2026},
  howpublished = {\url{https://openvapt.com/checks/HOST-SSH-001.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.