[C#] C# 与 Nessus 交互,动态构建任务计划
C# 与 Nessus 交互,动态构建任务计划
什么是 Nessus?
它是一个流行的漏洞扫描程序,我们可以通过它来提高自己服务器的安全性。
本文并不是一篇关于 Nessus 的安装介绍。
本文演示如何通过 C# 编码以 HTTP 的 Resful 风格来执行 GET、PUT、POST 和 DELETE 等操作,来实现登录后动态的创建扫描任务和获取执行结果等一系列步骤,而不是通过人为机械的点击按钮来一步步进行操作。
创建会话 NessusSession
我先新建一个用于后续 HTTP 请求的的类 HttpRequestMethod.cs:
/// <summary> /// HTTP 请求方法 /// </summary> public class HttpRequestMethod { public const string Get = "GET"; public const string Post = "POST"; public const string Put = "PUT"; public const string Delete = "DELETE"; }
在服务器安装完毕 Nessus 后,输入域名()【8834 端口是默认 Nessus 设置的端口】,显示的是一个授权登录页,显然,想要进一步操作必须先通过认证。
为此,我编写了一个存储会话状态的类 NessusSession.cs
/// <summary> /// 会话 /// </summary> public class NessusSession : IDisposable { /// <summary> /// 端口 /// </summary> public int Port { get; set; } /// <summary> /// 主机 /// </summary> public string Host { get; set; } /// <summary> /// 令牌 /// </summary> public string Token { get; private set; } /// <summary> /// 认证标识 /// </summary> public bool IsAuthenticated { get; private set; } #region ctor public NessusSession() { ServicePointManager.ServerCertificateValidationCallback = (object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) => true; } public NessusSession(string host, int port = 8834) : this() { Host = host; Port = port; } #endregion ctor /// <summary> /// 认证 /// </summary> /// <param name="userName"></param> /// <param name="password"></param> /// <returns></returns> public bool Authenticate(string userName, string password) { var obj = new JObject { ["username"] = userName, ["password"] = password }; var result = MakeRequest(HttpRequestMethod.Post, "session", obj); if (result == null || result.token == null) { return false; } Token = result.token; return IsAuthenticated = true; } /// <summary> /// 请求 /// </summary> /// <param name="method"></param> /// <param name="uri"></param> /// <param name="data"></param> /// <returns></returns> public dynamic MakeRequest(string method, string uri, JObject data = null) { var url = $"https://{Host}:{Port}/{uri}"; var request = WebRequest.Create(url); request.Method = method; if (!Token.IsNullOrEmpty()) { request.Headers["X-Cookie"] = $"token={Token}"; } request.ContentType = "application/json"; if (data == null) { request.ContentLength = 0; } else { var bytes = Encoding.UTF8.GetBytes(data.ToString()); request.ContentLength = bytes.Length; using (var rs = request.GetRequestStream()) { rs.Write(bytes, 0, bytes.Length); } } var respStream = request.GetResponse().GetResponseStream(); if (respStream == null) { return null; } string response; using (var reader = new StreamReader(respStream)) { response = reader.ReadToEnd(); } return response.IsNullOrEmpty() ? null : response.ToJson(); } /// <summary> /// 注销 /// </summary> public void LogOut() { if (!IsAuthenticated) return; MakeRequest(HttpRequestMethod.Delete, "session"); IsAuthenticated = false; } public void Dispose() { LogOut(); } }
其中,Authenticate() 方法的主要目的是通过登录验证获取 token 并保存,MakeRequest() 方法是对我们后续进行 HTTP 请求的代码封装(在认证成功后每次请求会携带对应的 token 值进行后续操作,因为我们是通过 json 方式进行值传递,所以需要设置“application/json” ),LogOut() 方法是注销操作,采用 DELETE 的方式。
不知道大家有没有注意到 url 的地址头是 https,所以我在构造函数中设置了验证服务器证书的回调(ServicePointManager.ServerCertificateValidationCallback = (object obj, X509Certificate certificate,X509Chain chain, SslPolicyErrors errors) => true;)。
现在,需要编写一个测试类运行,观察编写的方法是否生效,即验证是否登录成功。
[TestMethod] public void NessusSessionTest() { using (var session = new NessusSession("www.nidie.com.cn")) { var result = session.Authenticate("admin", "you guess"); if (!result) { throw new Exception("认证失败"); } Console.WriteLine(session.Token); } }
从测试的结果来看,毫无疑问,事实是经得起考验的。
创建操作类 NessusManager
接下来,登录后我们需要进行的就是构建一系列扫描活动流程操作,为此我编写了一个类 NessusManager.cs,它包含了我接下来需要介绍的操作,类似 Facede 模式。
public class NessusManager : IDisposable { private readonly NessusSession _session; public NessusManager(NessusSession session) { _session = session; } /// <summary> /// 获取扫描策略 /// </summary> /// <returns></returns> public dynamic GetScanPolicies() { return _session.MakeRequest(HttpRequestMethod.Get, "editor/policy/templates"); } /// <summary> /// 创建扫描任务 /// </summary> /// <param name="policyId"></param> /// <param name="targets"></param> /// <param name="name"></param> /// <param name="description"></param> /// <returns></returns> public dynamic CreateScanJob(string policyId, string targets, string name, string description) { var data = new JObject { ["uuid"] = policyId, ["settings"] = new JObject { ["name"] = name, ["text_targets"] = targets, ["description"] = description } }; return _session.MakeRequest(HttpRequestMethod.Post, "scans", data); } /// <summary> /// 开始扫描 /// </summary> /// <param name="scanId"></param> /// <returns></returns> public dynamic StartScan(int scanId) { return _session.MakeRequest(HttpRequestMethod.Post, $"scans/{scanId}/launch"); } /// <summary> /// 获取扫描结果 /// </summary> /// <param name="scanId"></param> /// <returns></returns> public dynamic GetScanResult(int scanId) { return _session.MakeRequest(HttpRequestMethod.Get, $"scans/{scanId}"); } public void Dispose() { _session?.Dispose(); } }
下面来演示一下如何通过类 NessusManager 来完成一个基本的扫描流程:
[TestMethod] public void ManagerTest() { using (var session = new NessusSession("www.nidie.com.cn")) { var result = session.Authenticate("admin", "you guess"); if (!result) { throw new Exception("认证失败"); } using (var manager = new NessusManager(session)) { var policies = manager.GetScanPolicies(); var id = string.Empty; foreach (var template in policies.templates) { if (template.name != "basic") continue; id = template.uuid; break; } var job = manager.CreateScanJob(id, "117.48.203.231", "随便扫扫", "该用户很懒,什么也没有留下"); int scanId = job.scan.id; manager.StartScan(scanId); var scanResult = manager.GetScanResult(scanId); while (scanResult.info.status != "completed") { Console.WriteLine("扫描状态:" + scanResult.info.status); Thread.Sleep(5000); scanResult = manager.GetScanResult(scanId); } Console.WriteLine(scanResult); foreach (var vulnerability in scanResult.vulnerabilities) { Console.WriteLine(vulnerability); } } } }
代码分析:
先通过类 NessusSession 进行授权登录认证获取 token,然后把认证成功的 session 对象传给类 NessusManager。
通过 manager,我们可以使用 GetScanPolicies() 来获取多种扫描策略,目前代码中所使用的是“Basic Network Scan”模板进行扫描:
因为每一种模板都有标识 id,通过 manager.CreateScanJob(id, "117.48.203.231", "随便扫扫", "该用户很懒,什么也没有留下") 方法创建的就是指定模板的扫描任务,后面的三个参数对应的参数值如下图所示,其中 59 是创建完 job 后返回的 scanId,即代码中的 job.scan.id。其中下图的 Targets 参数表示的是被扫描者的 IP,可以多个,可以是互联网上别人的 IP。
scanId,我们可以通过 StartScan(scanId) 方法启动,后续通过 GetScanResult() 方法跟踪扫描进度以及结果,就像快递单查询一样。
本次演示的扫描结果:
测试名称: ManagerTest 测试结果: 已通过 结果 的标准输出: 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running 扫描状态:running { "comphosts": [], "hosts": [ { "totalchecksconsidered": 685, "numchecksconsidered": 685, "scanprogresstotal": 685, "scanprogresscurrent": 685, "host_index": 0, "score": 20, "severitycount": { "item": [ { "count": 20, "severitylevel": 0 }, { "count": 0, "severitylevel": 1 }, { "count": 0, "severitylevel": 2 }, { "count": 0, "severitylevel": 3 }, { "count": 0, "severitylevel": 4 } ] }, "progress": "685-685/97607-97607", "critical": 0, "high": 0, "medium": 0, "low": 0, "info": 20, "severity": 20, "host_id": 2, "hostname": "117.48.203.231" } ], "notes": null, "remediations": { "remediations": null, "num_hosts": 1, "num_cves": 0, "num_impacted_hosts": 0, "num_remediated_cves": 0 }, "vulnerabilities": [ { "count": 1, "plugin_name": "Windows NetBIOS / SMB Remote Host Information Disclosure", "vuln_index": 21, "severity": 0, "plugin_id": 10150, "severity_index": 0, "plugin_family": "Windows" }, { "count": 1, "plugin_name": "Server Message Block (SMB) Protocol Version 1 Enabled (uncredentialed check)", "vuln_index": 20, "severity": 0, "plugin_id": 96982, "severity_index": 1, "plugin_family": "Misc." }, { "count": 1, "plugin_name": "OS Identification", "vuln_index": 19, "severity": 0, "plugin_id": 11936, "severity_index": 2, "plugin_family": "General" }, { "count": 1, "plugin_name": "Nessus Scan Information", "vuln_index": 18, "severity": 0, "plugin_id": 19506, "severity_index": 3, "plugin_family": "Settings" }, { "count": 1, "plugin_name": "Microsoft Windows SMB Versions Supported (remote check)", "vuln_index": 17, "severity": 0, "plugin_id": 100871, "severity_index": 4, "plugin_family": "Windows" }, { "count": 1, "plugin_name": "Microsoft Windows SMB NativeLanManager Remote System Information Disclosure", "vuln_index": 15, "severity": 0, "plugin_id": 10785, "severity_index": 5, "plugin_family": "Windows" }, { "count": 1, "plugin_name": "Microsoft Windows NTLMSSP Authentication Request Remote Network Name Disclosure", "vuln_index": 14, "severity": 0, "plugin_id": 42410, "severity_index": 6, "plugin_family": "Windows" }, { "count": 1, "plugin_name": "Device Type", "vuln_index": 13, "severity": 0, "plugin_id": 54615, "severity_index": 7, "plugin_family": "General" }, { "count": 1, "plugin_name": "Common Platform Enumeration (CPE)", "vuln_index": 11, "severity": 0, "plugin_id": 45590, "severity_index": 8, "plugin_family": "General" }, { "count": 2, "plugin_name": "Microsoft Windows SMB Service Detection", "vuln_index": 16, "severity": 0, "plugin_id": 11011, "severity_index": 9, "plugin_family": "Windows" }, { "count": 9, "plugin_name": "DCE Services Enumeration", "vuln_index": 12, "severity": 0, "plugin_id": 10736, "severity_index": 10, "plugin_family": "Windows" } ], "filters": [ { "operators": [ "eq", "neq", "match", "nmatch" ], "control": { "readable_regex": "TEXT", "type": "entry", "regex": ".*" }, "name": "hostname", "readable_name": "Hostname" }, { "operators": [ "eq", "neq" ], "control": { "type": "dropdown", "list": [ "true", "false" ] }, "name": "in_the_news", "readable_name": "In The News" }, { "operators": [ "eq", "neq", "match", "nmatch" ], "control": { "readable_regex": "NUMBER", "type": "entry", "regex": "^[0-9]+$" }, "name": "osvdb", "readable_name": "OSVDB ID" }, { "operators": [ "match", "nmatch" ], "control": { "readable_regex": "TEXT", "type": "entry", "regex": ".*" }, "name": "description", "readable_name": "Plugin Description" }, { "operators": [ "eq", "neq" ], "control": { "type": "dropdown", "list": [ "AIX Local Security Checks", "Amazon Linux Local Security Checks", "Backdoors", "CGI abuses", "CGI abuses : XSS", "CISCO", "CentOS Local Security Checks", "DNS", "Databases", "Debian Local Security Checks", "Default Unix Accounts", "Denial of Service", "F5 Networks Local Security Checks", "FTP", "Fedora Local Security Checks", "Firewalls", "FreeBSD Local Security Checks", "Gain a shell remotely", "General", "Gentoo Local Security Checks", "HP-UX Local Security Checks", "Huawei Local Security Checks", "Junos Local Security Checks", "MacOS X Local Security Checks", "Mandriva Local Security Checks", "Misc.", "Mobile Devices", "Netware", "Oracle Linux Local Security Checks", "OracleVM Local Security Checks", "Palo Alto Local Security Checks", "Peer-To-Peer File Sharing", "Policy Compliance", "Port scanners", "RPC", "Red Hat Local Security Checks", "SCADA", "SMTP problems", "SNMP", "Scientific Linux Local Security Checks", "Service detection", "Settings", "Slackware Local Security Checks", "Solaris Local Security Checks", "SuSE Local Security Checks", "Ubuntu Local Security Checks", "VMware ESX Local Security Checks", "Virtuozzo Local Security Checks", "Web Servers", "Windows", "Windows : Microsoft Bulletins", "Windows : User management" ] }, "name": "plugin_family", "readable_name": "Plugin Family" }, { "operators": [ "eq", "neq", "match", "nmatch" ], "control": { "readable_regex": "NUMBER", "type": "entry", "regex": "^[0-9, ]+$" }, "name": "plugin_id", "readable_name": "Plugin ID" }, { "operators": [ "date-lt", "date-gt", "date-eq", "date-neq", "match", "nmatch" ], "control": { "type": "datefield" }, "name": "plugin_modification_date", "readable_name": "Plugin Modification Date" }, { "operators": [ "eq", "neq", "match", "nmatch" ], "control": { "readable_regex": "TEXT", "type": "entry", "regex": ".*" }, "name": "plugin_name", "readable_name": "Plugin Name" }, { "operators": [ "eq", "neq", "match", "nmatch" ], "control": { "readable_regex": "TEXT", "type": "entry", "regex": ".*" }, "name": "plugin_output", "readable_name": "Plugin Output" }, { "operators": [ "date-lt", "date-gt", "date-eq", "date-neq", "match", "nmatch" ], "control": { "type": "datefield" }, "name": "plugin_publication_date", "readable_name": "Plugin Publication Date" }, { "operators": [ "eq", "neq" ], "control": { "type": "dropdown", "list": [ "local", "remote" ] }, "name": "plugin_type", "readable_name": "Plugin Type" }, { "operators": [ "eq", "neq", "match", "nmatch" ], "control": { "readable_regex": "80", "type": "entry", "regex": "^[0-9]+$" }, "name": "port", "readable_name": "Port" }, { "operators": [ "eq", "neq" ], "control": { "type": "dropdown", "list": [ "tcp", "udp", "icmp" ] }, "name": "protocol", "readable_name": "Protocol" }, { "operators": [ "eq", "neq" ], "control": { "type": "dropdown", "list": [ "None", "Low", "Medium", "High", "Critical" ] }, "name": "risk_factor", "readable_name": "Risk Factor" }, { "operators": [ "eq", "neq", "match", "nmatch" ], "control": { "readable_regex": "TEXT", "type": "entry", "regex": ".*" }, "name": "see_also", "readable_name": "See Also" }, { "operators": [ "match", "nmatch" ], "control": { "readable_regex": "TEXT", "type": "entry", "regex": ".*" }, "name": "solution", "readable_name": "Solution" }, { "operators": [ "match", "nmatch" ], "control": { "readable_regex": "TEXT", "type": "entry", "regex": ".*" }, "name": "synopsis", "readable_name": "Synopsis" } ], "history": [ { "alt_targets_used": false, "scheduler": 0, "status": "completed", "type": "local", "uuid": "249a5bd3-7a0d-6a00-3852-558a535bb93a01fcdc6fab21f313", "last_modification_date": 1529451124, "creation_date": 1529450192, "owner_id": 2, "history_id": 63 } ], "compliance": [], "info": { "acls": [ { "permissions": 0, "owner": null, "display_name": null, "name": null, "id": null, "type": "default" }, { "permissions": 128, "owner": 1, "display_name": "admin", "name": "admin", "id": 2, "type": "user" } ], "edit_allowed": true, "alt_targets_used": null, "status": "completed", "scanner_start": 1529450192, "policy": "Basic Network Scan", "pci-can-upload": false, "hasaudittrail": true, "scan_start": 1529450192, "user_permissions": 128, "folder_id": null, "no_target": null, "targets": "117.48.203.231", "control": true, "timestamp": 1529451124, "object_id": 62, "scanner_name": "Local Scanner", "haskb": true, "uuid": "249a5bd3-7a0d-6a00-3852-558a535bb93a01fcdc6fab21f313", "scanner_end": 1529451121, "hostcount": 1, "scan_end": 1529451124, "scan_type": "local", "name": "随便扫扫" } } { "count": 1, "plugin_name": "Windows NetBIOS / SMB Remote Host Information Disclosure", "vuln_index": 21, "severity": 0, "plugin_id": 10150, "severity_index": 0, "plugin_family": "Windows" } { "count": 1, "plugin_name": "Server Message Block (SMB) Protocol Version 1 Enabled (uncredentialed check)", "vuln_index": 20, "severity": 0, "plugin_id": 96982, "severity_index": 1, "plugin_family": "Misc." } { "count": 1, "plugin_name": "OS Identification", "vuln_index": 19, "severity": 0, "plugin_id": 11936, "severity_index": 2, "plugin_family": "General" } { "count": 1, "plugin_name": "Nessus Scan Information", "vuln_index": 18, "severity": 0, "plugin_id": 19506, "severity_index": 3, "plugin_family": "Settings" } { "count": 1, "plugin_name": "Microsoft Windows SMB Versions Supported (remote check)", "vuln_index": 17, "severity": 0, "plugin_id": 100871, "severity_index": 4, "plugin_family": "Windows" } { "count": 1, "plugin_name": "Microsoft Windows SMB NativeLanManager Remote System Information Disclosure", "vuln_index": 15, "severity": 0, "plugin_id": 10785, "severity_index": 5, "plugin_family": "Windows" } { "count": 1, "plugin_name": "Microsoft Windows NTLMSSP Authentication Request Remote Network Name Disclosure", "vuln_index": 14, "severity": 0, "plugin_id": 42410, "severity_index": 6, "plugin_family": "Windows" } { "count": 1, "plugin_name": "Device Type", "vuln_index": 13, "severity": 0, "plugin_id": 54615, "severity_index": 7, "plugin_family": "General" } { "count": 1, "plugin_name": "Common Platform Enumeration (CPE)", "vuln_index": 11, "severity": 0, "plugin_id": 45590, "severity_index": 8, "plugin_family": "General" } { "count": 2, "plugin_name": "Microsoft Windows SMB Service Detection", "vuln_index": 16, "severity": 0, "plugin_id": 11011, "severity_index": 9, "plugin_family": "Windows" } { "count": 9, "plugin_name": "DCE Services Enumeration", "vuln_index": 12, "severity": 0, "plugin_id": 10736, "severity_index": 10, "plugin_family": "Windows" }
通过 scanResult.vulnerabilities 可以得到风险提示或建议等信息:
对应的 Web 端的截图: