NVT Development
This page collects hints and guides for developing Network Vulnerability Tests.
- How to start
- Golden Rules
- How to write a product detection NVT
- Miscellaneous
- How to assign a CVSS to a NVT
- How to deprecate a NVT
How to start
The NVT development is not well documented.
But you will find other developers for questions and discussions
on the NVT Development Mailing List
The best start is to learn from existing NASL scripts.
There is also a template you can begin with:
template.nasl
License requirements: We welcome any contribution licensed under GNU GPLv2+.
These can easily go into the OpenVAS Feed to benefit everyone.
Other licenses might create legal conflicts and in doubt you should simply
ask the other NVT developers.
Golden Rules
This is a collection assembled over time from lessons learned.
-
Avoid writing your own product detection inside a vulnerability check.
Rationale: We separate product detection from actual vulnerability check where ever possible. You will find various detection scripts in the OpenVAS repository and should check whether there is already one you can use. Usually these scripts have a "detect* in their filename and you can search for other NVTs on how to use it.
In case there is no detection yet, consider writing a new detection script so it can be re-used in the future. In doubt how to do that or whether it makes sense in some special cases, just get in contact with the developers at openvas-plugins mailing list.
-
Use product detection result in proper way (example: gb_openssl_dos_vuln_lin_jun09.nasl).
You can do so by doing the following:
-
SCRIPT_OID: Defined as a variable because used in 2 places. This is a convenience method until OpenVAS-4 is deprecated and any NVT can rely on NASL function get_oid().
- You will need these includes:
include("version_func.inc"); include("host_details.inc"); - Add the dependency to the detection:
script_dependencies("gb_openssl_detect_lin.nasl"); - Add a key requirement to ensure the scripts doesn't run in unnecessary situations like:
script_require_keys("PostgreSQL/installed"); - Use get_app_version() to work with the version number:
ver = get_app_version(cpe:"cpe:/a:openssl:openssl", nvt:SCRIPT_OID);
Rationale: This enables product detection references inside vulnerability report as featured since OpenVAS-5.
-
-
Don't add any port scanner as dependency.
Rationale: Since the user should decide on her own which scanner to use, this could lead to situations where more then one port scanner is concurrently executed.
-
Don't use "toolchecks.nasl" as dependency if your NVT does not directly(!) use one of the tools listed in toolcheck.nasl.
Rationale: It's simply useless. Though it has no effect it creates a wrong impression when reading the code.
-
If you use "script_mandatory_keys("login/SSH/success");" then you also need to apply "script_dependencies("ssh_authorization.nasl");".
Rationale: The key will only be set when ssh_authorization is executed.
-
Use "script_mandatory_keys()" (also instead of "script_require_keys()") if the script does not do anything sensible if a key is not available.
For example: Use "script_mandatory_keys("MyApp/Linux/Version");" if your script starts like this with a very early exit():
include("version_func.inc"); myappVer = get_kb_item("MyApp/Linux/Version"); if(!myappVer){ exit(0); } ...Rationale: Using script_mandatory_keys will prevent that the NVT is executed at all; even if optimize_tests setting is disabled. This improves the scan performance.
-
Don't use UTF-8 encoding for your NVT. For the time being (as long as old scanner protocol OTP is supported), the encoding to be used for NVTs is ISO-8859-1.
Rationale: Is is actually not a big issue for openvas-scanner since it mostly just passes along the string, but can create trouble in modules processing the output of openvas-scanner, like openvas-manager or openvas-client. Since OTP 1.0 does not specify an encoding, they generally assume that everything they get from openvas-scanner is ISO-8859 encoded and will try to convert it.
This means that the encoding will be messed up if the NVT was already UTF-8 encoded which can result in anything from funny looking characters to incomplete reports.
How to write a product detection NVT
It is very important for the NVT Feed that we separate the work of the detection a certain product from actual vulnerability evaluation. The actual detection NVTs should result a CPE code for the product. If none is available yet, it needs to be registered with NIST. Also it is very important to explain the method how the product was detected (description) and where it was detected (result).
A good example for a authenticated product detection is gb_openssl_dectect_lin.nasl. Watch out for these elements:
-
SCRIPT_OID: Defined as a variable because used in 2 places. This is a convenience method until OpenVAS-4 is deprecated and any NVT can rely on NASL function get_oid().
-
script_tag(name:"detection", value:"..."): Always set the type of detection. This could be "executable version check" or "remote banner" and we can agree on further if needed.
-
script_name("..."): Use a simple direct name like ""OpenSSL Version Detection (Linux)".
-
script_description("..."): Explain how the product is detected. For example:
"Detection of installed version of OpenSSL. The script logs in via ssh, searches for executable 'openssl' and queries the found executables via command line option 'version'."
-
script_family("Product detection"): apply this family.
-
Necessary include's: include("cpe.inc"); include("host_details.inc");
-
Build a CPE and register the product:
cpe = build_cpe(value:sslVer[1], exp:"^([0-9.]+[a-z0-9]*)", base:"cpe:/a:openssl:openssl:"); if(!isnull(cpe)) register_product(cpe:cpe, location:executableFile, nvt:SCRIPT_OID);
-
log_message(): Always send a log message with as detailed and helpful information as possible. Here is a good example for the OpenSSL local detection:
log_message(data: build_detection_report(app:"OpenSSL", version: sslVer[1], install: executableFile, cpe: cpe, concluded:sslVer[max_index(sslVer)-1]), port:0);
-
Finally set a key to help scan scheduler like this example:
set_kb_item(name:"PostgreSQL/installed", value:TRUE);
Miscellaneous
-
Secret information in the Knowledge Base.
If you are handling secret information like passwords in the KB, you should prefix the name with "Secret/". Then these values are not written to disk into kb-files.
How to assign a CVSS to a NVT
Any new NVT must be provided with a CVSS Score and a CVSS Base Vector.
Common Vulnerability Scoring System (CVSS) is an industry standard for assessing the severity of computer system security vulnerabilities. CVSS is composed of three metric groups:
- Base Metrics
- Temporal Metric Group
- Environmental Metric Group
For NVT development purposes, we only use Base Metrics. Where linked to other security information (for example CVE), these are automatically updated over time. The information is captured in the following tags in NVT:
script_tag(name:"cvss_base", value:"x.x"); script_tag(name:"cvss_base_vector", value:"AV:N/AC:M/Au:N/C:P/I:C/A:C");
Follow these steps for assigning a CVSS:
-
If there is a CVE for which NVT is being developed, take the CVSS score already computed from NVD (National Vulnerability Database), http://web.nvd.nist.gov/view/vuln/search.
-
If there is a CVE for which NVT is being developed and if CVSS is not yet available at NVD, manually compute the CVSS score based on the guideline described below.
-
If there is non-CVE vulnerability reference for which NVT is being developed, manually compute the CVSS score based on the guidelines described below.
Scoring Guidelines
Base score is a score in the range of 0.0-10.0 at steps of 0.1. The attributes for computing CVSS Base Score are:
-
Access Vector
The metric reflects how the vulnerability is exploited. Possible values are:
- Local(L): local system, shell account
- Adjacent Network(A): Attack is from an adjacent network
- Network(N): Through network
-
Access Complexity
Measures the complexity of the attack required to exploit the vulnerability. Possible values are:
- High(H): If it is too complex to perform an attack. For example, not a default configuration race condition
- Medium(M): Some kind of privilege is required, non-default setting
- Low(L): easy to exploit
-
Authentication
Measures the number of times an attacker must authenticate to a target in order to exploit. Possible values are:
- Multiple(M): multiple authentication is required
- Single(S): one instance of authentication is required
- None(N): No authentication is required
-
Confidentiality
Measures the impact on confidentiality of a successfully exploited vulnerability. Possible values are:
- None(N)
- Partial(P)
- Complete(C)
-
Integrity
Measures the impact to integrity of a successfully exploited vulnerability. Possible valuse are:
- None(N)
- Partial(P)
- Complete(C)
-
Availability
Measures the impact to availability of a successfully exploited vulnerability. Possible values are:
- None(N)
- Partial(P)
- Complete(C)
Base Metrics: AV:[L,A,N]/AC:[H,M,L]/Au:[M,S,N]/C:[N,P,C]/I:[N,P,C]/A:[N,P,C]
The following tips can be applied while scoring:
Consider the direct impact a vulnerability will have on the target
If there are multiple vulnerabilities NVT is addressing, for scoring purposes for each of the above attributes, consider the vulnerability which will have high impact on the target (the one that yields high score)
If multiple options can be selected for each attribute, consider the one that will have high impact on the target.
In general, the following Confidentiality, Integrity and Availability values can be applied for different type of vulnerability.
Denial of Service:
- Confidentiality is None(N)
- Integrity is None(N)
- Availability is either Partial(P) or Complete(C)
Buffer overflow vulnerability:
- Confidentiality is Partial(P) or Complete(C)
- Integrity is Partial(P) or Complete(C)
- Availability is Partial(P) or Complete(C)
Format string vulnerability:
- Confidentiality is Partial(P) or Complete(C)
- Integrity is Partial(P) or Complete(C)
- Availability is Partial(P) or Complete(C)
Cross-site scripting vulnerability:
- Confidentiality is None(N)
- Integrity is Partial(P) or Complete(C)
- Availability is Partial(P) or Complete(C)
SQL Injection vulnerability:
- Confidentiality is Partial(P) or Complete(C)
- Integrity is Partial(P) or Complete(C)
- Availability is Partial(P) or Complete(C)
Directory traversal vulnerability:
- Confidentiality is Partial(P) or Complete(C)
- Integrity is None(N)
- Availability is None(N)
File inclusion vulnerability:
- Confidentiality is Partial(P) or Complete(C)
- Integrity is Partial(P) or Complete(C)
- Availability is Partial(P) or Complete(C)
Command execution vulnerability:
- Confidentiality is Partial(P) or Complete(C)
- Integrity is Partial(P) or Complete(C)
- Availability is Partial(P) or Complete(C)
Information disclosure vulnerability:
- Confidentiality is Partial(P) or Complete(C)
- Integrity is None(N)
- Availability is None(N)
Spoofing:
- Confidentiality is Partial(P) or Complete(C)
- Integrity is None(N)
- Availability is None(N)
Session Hijacking:
- Confidentiality is Partial(P) or Complete(C)
- Integrity is None(N)
- Availability is None(N)
Cross-site request forgery:
- Confidentiality is Partial(P) or Complete(C)
- Integrity is None(N)
- Availability is None(N)
Code execution:
- Confidentiality is Partial(P) or Complete(C)
- Integrity is Partial(P) or Complete(C)
- Availability is Partial(P) or Complete(C)
The decision on whether to choose Partial(P) or Complete(C) is based on how much impact it'll have on the target.
How to score
Once the values are chosen for each of the CVSS attributes, apply those values to CVSS Calculator program available at:
Common Vulnerability Scoring System Version 2 Calculator
References
How to deprecate a NVT
In same (rare) situations it might be necessary to deprecate a NVT so that it is not executed anymore even if selected in a Scan-Config.
Simply removing it from the Feed is a suboptimal solution because older Scan-Result will reference it and users can not view details anymore, not at least that it has been deprecated meanwhile.
Therefore this procedure is recommended for a deprecation:
-
Update the description to inform that is is being deprecated and explain the reason why it has been deprecated.
In case the NVT was replaced by another one, name this NVT and its OID. For example: "This NVT has been replaced by NVT "THE-NVT Development" (OID: THE-FULL-OID)."
-
Add the deprecation tag: script_tag(name:"deprecated", value:TRUE);
-
Add an early "exit(66);". Of course this must be after the description block. The exit code 66 marks the NVT as deprecated to the scanner.
English |
