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

目录

  • 什么是 Nessus?
  • 创建会话类 NessusSession
  • 登录测试
  • 创建操作类 NessusManager
  • 操作测试

什么是 Nessus?

  它是一个流行的漏洞扫描程序,我们可以通过它来提高自己服务器的安全性;定期对服务器进行漏洞和补丁扫描,使用已知漏洞的数据库评估正在运行在网络上不同平台的系统,这可以帮助我们更快速的识别风险以及进行合理规避。针对个人来讲,它是免费的。

  本文并不是一篇关于 Nessus 的安装介绍。

  本文演示的是如何通过 C# 编码以 HTTP 的 Resful 风格来执行 GET、PUT、POST 和 DELETE 等操作,来实现登录后动态的创建扫描任务和获取执行结果等一系列步骤,而不是通过人为机械的点击按钮来一步步进行操作。

创建会话类 NessusSession

  在服务器安装完毕 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 = ) : 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}";
} //set: json
request.ContentType = "application/json"; if (data == null)
{
request.ContentLength = ;
}
else
{
var bytes = Encoding.UTF8.GetBytes(data.ToString());
request.ContentLength = bytes.Length; using (var rs = request.GetRequestStream())
{
rs.Write(bytes, , bytes.Length);
}
} //request --> response
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();
}
}

NessusSession

  代码分析:

  其中,Authenticate(userName, password) 方法的主要目的是通过登录验证,请求时通过 Jobject 对象将参数进行包装传输,在成功后将获取的 token 值(又称身份令牌)进行保存,方便后续操作。

        /// <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;
}

  因为所有的方法调用都是以 HTTP 方式进行请求,所以封装了方法 MakeRequest(string method, string uri, JObject data = null) 。在登录成功后,每次请求都会携带对应的 token 值(request.Headers["X-Cookie"] = $"token={Token}"),我们在进行参数传递的时候都是以 json 的格式进行传输,所以需要设置 request.ContentType = "application/json",为了方便后续返回值的直接使用,我使用了 dynamic 类型作为返回值,这样也可以减少创建多个实体类。

        /// <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}";
} //set: json
request.ContentType = "application/json"; if (data == null)
{
request.ContentLength = ;
}
else
{
var bytes = Encoding.UTF8.GetBytes(data.ToString());
request.ContentLength = bytes.Length; using (var rs = request.GetRequestStream())
{
rs.Write(bytes, , bytes.Length);
}
} //request --> response
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();
}

  同时,我新建一个类 HttpRequestMethod.cs 来保存 HTTP 请求方式:

    /// <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";
}

  LogOut() 是注销操作,采取的是 DELETE 方式。因为我希望在关闭时直接释放,所以继承了 IDisposable 接口并实现。

        /// <summary>
/// 注销
/// </summary>
public void LogOut()
{
if (!IsAuthenticated) return; MakeRequest(HttpRequestMethod.Delete, "session");
IsAuthenticated = false;
} public void Dispose()
{
LogOut();
}

  还有一个比较特殊的地方,因为 url 的地址头是 https,所以我在构造函数中直接设置了验证服务器证书的回调为 true,来保证 SSL 证书的正常接受,即 ServicePointManager.ServerCertificateValidationCallback = (object obj, X509Certificate certificate,X509Chain chain, SslPolicyErrors errors) => true。

        public NessusSession()
{
ServicePointManager.ServerCertificateValidationCallback = (object obj, X509Certificate certificate,
X509Chain chain, SslPolicyErrors errors) => true;
} public NessusSession(string host, int port = ) : this()
{
Host = host;
Port = port;
}

登录测试

  现在,我已经实现了会话类 NessusSession,需要编写一个测试类运行,传入 IP 或域名,以及对应的用户名和密码,因为实现了 IDisposable 接口,所以可以直接使用 using 进行释放,观察编写的方法是否生效,即验证是否登录成功。因为登录失败根本无法进行后续的操作,所以在登录失败时我直接抛出“认证失败”的异常描述值。

        [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,它类似 Facede 模式,包含了一系列扫描活动流程操作,需要把之前的 session 传递进来,方便后续我们直接通过该类进行操作:

    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.cs

  代码分析:

  安装完毕 Nessus 之后,我们可以选择合适的扫描模板:

  通过 GetScanPolicies() 方法,我们就可以查看模板信息,这里的每一种模板都有对应的 UUID(又称 GUID):

        /// <summary>
/// 获取扫描策略
/// </summary>
/// <returns></returns>
public dynamic GetScanPolicies()
{
return _session.MakeRequest(HttpRequestMethod.Get, "editor/policy/templates");
}

  获取到我们选择的模板 id 后,我们可以通过 CreateScanJob() 方法把 id 和其它所需要的参数进行传入,即可创建扫描任务:

        /// <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);
}

  创建完毕之后,我们可以得到该任务 id(参数 scanId),通过 StartScan() 方法就可以直接启动对应的扫描任务。

        /// <summary>
/// 开始扫描
/// </summary>
/// <param name="scanId"></param>
/// <returns></returns>
public dynamic StartScan(int scanId)
{
return _session.MakeRequest(HttpRequestMethod.Post, $"scans/{scanId}/launch");
}

  通过定时调用 GetScanResult() 方法,我们可以以轮询的方式不断跟踪该计划的进度和状态,类似订单跟踪,参数依然是之前创建任务返回所得到的 id(scanId):

        /// <summary>
/// 获取扫描结果
/// </summary>
/// <param name="scanId"></param>
/// <returns></returns>
public dynamic GetScanResult(int scanId)
{
return _session.MakeRequest(HttpRequestMethod.Get, $"scans/{scanId}");
}

操作测试

  目前已经编写好两个主要参与的类,下面来演示一下如何通过类 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();
scanResult = manager.GetScanResult(scanId);
} Console.WriteLine(scanResult); foreach (var vulnerability in scanResult.vulnerabilities)
{
Console.WriteLine(vulnerability);
}
}
}
}

  在代码中,通过 manager 对象,我用 GetScanPolicies() 方法选取了名称为“basic”(“Basic Network Scan”)的模板进行创建,再调用 CreateScanJob() 方法创建扫描任务,接着调用 StartScan() 方法启动,轮询 GetScanResult() 方法返回的结果值输出,根据不同人的服务器运行速度以及网络环境等因素,整个时间可能比较漫长。

  因为每一种模板都有标识 id,通过 manager.CreateScanJob(id, "117.48.203.231", "随便扫扫", "该用户很懒,什么也没有留下") 方法创建的就是指定模板的扫描任务,后面的三个参数对应的参数值如下图所示,其中 59 是创建完 job 后返回的 scanId,即代码中的 job.scan.id。下图的 Targets 参数表示的是被扫描者的 IP,可以多个,可以是互联网上别人的 IP。

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

  对应的 Web 端的截图:

[C#] C# 与 Nessus 交互,动态构建扫描任务计划的更多相关文章

  1. 基础才是重中之重~Emit动态构建方法(参数和返回值)

    回到目录 对于Emit我们知道它的可以动态构建程序集,类型,方法,属性等,或者说只要手动使用C#创建的东西使用Emit也都可以动态创建它们,Emit由于它的特别之处,所以在很多领域得到了广泛的应用,像 ...

  2. Lind.DDD.ExpressionExtensions动态构建表达式树,实现对数据集的权限控制

    回到目录 Lind.DDD框架里提出了对数据集的控制,某些权限的用户为某些表添加某些数据集的权限,具体实现是在一张表中存储用户ID,表名,检索字段,检索值和检索操作符,然后用户登陆后,通过自己权限来构 ...

  3. LINQ to SQL 运行时动态构建查询条件

    在进行数据查询时,经常碰到需要动态构建查询条件.使用LINQ实现这个需求可能会比以前拼接SQL语句更麻烦一些.本文介绍了3种运行时动态构建查询条件的方法.本文中的例子最终实现的都是同一个功能,从Nor ...

  4. Mybatis之动态构建SQL语句

    今天一个新同事问我,我知道如何利用XML的方式来构建动态SQL,可是Mybatis是否能够利用注解完成动态SQL的构建呢?!!答案是肯定的,MyBatis 提供了注解,@InsertProvider, ...

  5. C# Lambda 表达式学习之(三):动态构建类似于 c => c.Age == null || c.Age > 18 的表达式

    可能你还感兴趣: 1. C# Lambda 表达式学习之(一):得到一个类的字段(Field)或属性(Property)名,强类型得到 2. C# Lambda 表达式学习之(二):LambdaExp ...

  6. C# Lambda 表达式学习之(四):动态构建类似于 c => c.Age == 2 || c.Age == 5 || c => c.Age == 17 等等一个或多个 OrElse 的表达式

    可能你还感兴趣: 1. C# Lambda 表达式学习之(一):得到一个类的字段(Field)或属性(Property)名,强类型得到 2. C# Lambda 表达式学习之(二):LambdaExp ...

  7. Spring data JPA中使用Specifications动态构建查询

    有时我们在查询某个实体的时候,给定的条件是不固定的,这是我们就需要动态 构建相应的查询语句,在JPA2.0中我们可以通过Criteria接口查询,JPA criteria查询.相比JPQL,其优势是类 ...

  8. python动态构建类(类似声明)

    对于类实例的动态构建,那是非常的简单.可要在代码中动态的构建类,然后该类还能够被使用,那得多么的强大呀. 在Python中,内建的__builtin__提供了一个type的方法,用该方法可以动态的构建 ...

  9. HTML5+中动态构建列表并填充数据

    部分代码参考demo----<历史上的今天>. 感谢作者的分享,愿好人一生平安,虽然只有两个页面,但是通过这个示例让我学会了5+中如何动态构建列表并填充数据,非常实用. html部分: & ...

随机推荐

  1. ALS交替最小二乘法总结

    摘要: 1.算法概述 2.算法推导 3.算法特性及优缺点 4.注意事项 5.实现和具体例子 6.适用场合 内容: 1.算法概述 ALS是alternating least squares的缩写 , 意 ...

  2. 【Android】自己动手做个扫雷游戏

    1. 游戏规则 扫雷是玩法极其简单的小游戏,点击玩家认为不存在雷的区域,标记出全部地雷所在的区域,即可获得胜利.当点击不包含雷的块的时候,可能它底下存在一个数,也可能是一个空白块.当点击中有数字的块时 ...

  3. AI2(App Inventor 2)离线版服务器(2019.04.28更新)

    我们的目标:搭建一个本地多用户的App Inventor 2 服务器   演示: http://ai2.fsyz.net  [旧 win]     http://ai2n.fsyz.net [新 Ce ...

  4. python接口自动化(八)--发送post请求的接口(详解)

    简介 上篇介绍完发送get请求的接口,大家必然联想到发送post请求的接口也不会太难,被聪明的你又猜到了.答案是对的,虽然发送post请求的参考例子很简单,但是实际遇到的情况却是很复杂的,因为所有系统 ...

  5. Java集合详解8:Java集合类细节精讲

    今天我们来探索一下Java集合类中的一些技术细节.主要是对一些比较容易被遗漏和误解的知识点做一些讲解和补充.可能不全面,还请谅解. 本文参考:http://cmsblogs.com/?cat=5 具体 ...

  6. Java异常处理最佳实践及陷阱防范

    前言 不管在我们的工作还是生活中,总会出现各种“错误”,各种突发的“异常”.无论我们做了多少准备,多少测试,这些异常总会在某个时间点出现,如果处理不当或是不及时,往往还会导致其他新的问题出现.所以我们 ...

  7. clang如何获得程序控制流图

    在低版本的clag中,可以直接clang -cc1 -analyze -cfg-dump 1.c来获得程序控制流图,但较高版本后就不行了 另外clang -cc1默认仅限当前目录,所以会出现fata ...

  8. 【转载】通俗易懂,什么是.NET?什么是.NET Framework?什么是.NET Core?

    本文转载自:http://www.cnblogs.com/1996V/p/9037603.html [尊重作者原创,转载说明出处!感谢作者“小曾看世界”分享! ] 什么是.NET?什么是.NET Fr ...

  9. BFS(广度优先搜索)

    简介 BFS的过程是首先访问起始结点v,接着访问顶点v的所有未被访问的邻接结点,然后对每个继续进行上述步骤,直到所有结点都被访问过为止,当然,在访问过程中,需要使用一个队列,然后类似二叉树的层次遍历来 ...

  10. 设计模式之行为类模式PK

    行为类模式包括: 责任链模式 命令模式 解释器模式 迭代器模式 中介者模式 备忘录模式 观察者模式 状态模式 策略模式 模板方法模式 访问者模式 行为型模式涉及到算法和对象间职责的分配 行为类模式关注 ...