85

C# 与 Nessus 交互,动态构建扫描任务计划

 5 years ago
source link: http://www.10tiao.com/html/396/201806/2650825523/2.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

什么是 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 后,输入地址(本文演示时使用的是 https://www.nidie.com.cn:8834/)【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() 方法跟踪扫描进度以及结果,就像快递单查询一样。

 

  本次演示的扫描结果:


见原文:https://www.cnblogs.com/liqingwen/archive/2018/06/20/9201834.html


  通过 scanResult.vulnerabilities 可以得到风险提示或建议等信息:

  对应的 Web 端的截图:

 原文链接:https://www.cnblogs.com/liqingwen/archive/2018/06/20/9201834.html

版权申明:本文来源于网友收集或网友提供,如果有侵权,请转告版主或者留言,本公众号立即删除。






About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK