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
- How to write a policy NVT
- Miscellaneous
- How to assign a CVSS to a NVT
- How to deprecate a NVT
- How to handle references to CVEs that are marked REJECTED
- How to handle NVTs with "no solution" for the user
- How to assign a QoD to a NVT
- How to assign a solution type to 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:
- 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");
Rationale: This enables product detection references inside vulnerability report as featured since OpenVAS-5.
- You will need these includes:
-
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. -
Place a newly developed NVT into the subdirectory named as the current year (e.g. 2013/). Important exceptions are:
- NVTs that are used as dependencies for others. This covers for example all detection NVTs.
- Any include files.
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_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);
-
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);
How to write a policy NVT
A policy NVT does not search for a documented general vulnerability. It rather tests a target system for properties that a user defines.
In order to allow the user to configure the severity level, a policy is split into 4 parts. For the following example you will find a reference implementation with NAME=file_checksums:
- Policy/policy_NAME.nasl: The main routine that offers and manages the user preferences. Furthermore it will execute the policy checks and store the results in the internal knowledge base. This NVT will not report anything except "error" in case it fails fundamentally.
- Policy/policy_NAME_violation.nasl: Creates a log-result for all policy violations.
- Policy/policy_NAME_ok.nasl: Creates a log-result for all policies that were met.
- Policy/policy_NAME_error.nasl: Creates a log-result for all policies where an error occurred while trying to check. For example if a file could not be accessed due to permission problems.
The routines "_violation", "_ok" and "_error" report log level. The user can define appropriate Overrides in the Manager and assign severities as felt appropriate. Those Overrides are typically configured to be applied generally for all tasks and/or IPs.
Developers should implement their policies as close to the reference example as possible, especially in terms of naming scheme and code structure.
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.
In case multiple CVEs are referenced, pick the one with the Maximum CVSS Base and take the corresponding CVSS Base Vector.
It is not allowed to apply any other CVSS in case at least one CVE is referenced that offers a CVSS. In case author likes to apply another CVSS, a second NVT should be added that has no CVE reference and a CVSS of its own. However, the better approach is to submit a rationale to NIST if you think the CVSS is wrong for the respective vulnerability.
-
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 values 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
- CVSS Home page
- CVSS Guide
- CVSS v2 Calculator at NVD of NIST
- OpenVAS CVSS v2 Calculator at Greenbone SecInfo
How to deprecate a NVT
In some (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.
How to handle references to CVEs that are marked REJECTED
It may happen that a CVE is marked REJECTED over time but already used as a reference in a NVT.
In this case the following procedure should be applied:
-
In case other (not rejected) CVEs are present as references: Remove the CVE reference.
-
In case it is the only CVE reference: Remove the CVE reference. Then evaluate whether the NVT creates a helpful log information for the user or collects helpful information for other NVTs.
If so, then make the NVT a log-class NVT. Else deprecate the NVT.
How to handle NVTs with "no solution" for the user
It happens that for a vulnerability there is no solution for the user yet. This usually is the case when the respective weakness was only recently discovered. Of course a special solution can be a workaround or even disabling a service, but no known patch or vendor update is available to actually close the attack vector.
In the case no solution is available at time of writing the respective tag should contain this sentence (dd is the day, mmmmmmmm the month as a word, yyyy is the year; 'th' is 'st', 'nd' or 'rd' where appropriate):
No solution or patch is available as of ddth mmmmmmmm, yyyy. Information regarding this issue will be updated once solution details are available.
Next, the solution_type must be set to "NoneAvailable".
After some time, solutions get available. But for some vulnerabilities solutions are never worked out for a particular software release.
For updating the "no solution" information the following guide should be applied (if getting aware of a solution, this can and should be applied anytime, independent of the described schedule):
After one month after creation of the NVT, the solution availability must be re-checked and if still no solution is available, the "No solution available as of ..." should be updated with the current date.
After 6 month after creation of the NVT, the same must be done like after one month.
If no solution becomes available for over 12 month after release of the NVT, the phrase "No solution ..." in the tag "solution" must be replaced by:
No solution or patch was made available for at least one year since disclosure of this vulnerability. Likely none will be provided anymore. General solution options are to upgrade to a newer release, disable respective features, remove the product or replace the product by another one.
Also, the solution_type tag should be set to "WillNotFix".
And in case there is a work around:
No solution or patch was made available for at least one year since disclosure of this vulnerability. Likely none will be provided anymore. General solution options are to upgrade to a newer release, disable respective features, remove the product or replace the product by another one.
A workaround is to ...
How to assign a QoD to a NVT
The QoD is a value between 0% and 100% describing the reliability of the executed vulnerability detection or product detection.
One of the main reasons to introduce this concept was to handle the challenge of potential vulnerabilities properly. The goal was to keep such in the results database but only visible on demand.
While the QoD range allows to express the quality pretty refined, in fact most of the test routines use a standard methodology. Therefore the QoD Types were introduced of which each is associated with a QoD value. The current list of types might be extended over time.
Overview on QoD values and types
QoD QoD Type(s) Description 100% exploit The detection happened via an exploit and therefore is fully verified. 99% remote_vul Remote active checks (code execution, traversal attack, sql injection etc.) where the response clearly shows the presence of the vulnerability. 98% remote_app Remote active checks (code execution, traversal attack, sql injection etc.) where the response clearly shows the presence of the vulnerable application. 97% package Authenticated package-based checks for Linux(oid) systems. 97% registry Authenticated registry-based checks for Windows systems. 95% remote_active Remote active checks (code execution, traversal attack, sql injection etc.) where the response shows the likely presence of the vulnerable application or of the vulnerability. "Likely" means that only rare circumstances are possible where the detection would be wrong. 80% remote_banner Remote banner check of applications that offer patch level in version. Many proprietary products do so. 80% executable_version Authenticated executable version checks for Linux(oid) or Windows systems where applications offer patch level in version. 75% This value was assigned to any pre-qod results during migration to OpenVAS-8 and is also assigned for results from NVTs that do not own a qod. However, some NVTs eventually might own this value for some reason. 70% remote_analysis Remote checks that do some analysis but which are not always fully reliable. 50% remote_probe Remote checks where intermediate systems such as firewalls might pretend correct detection so that it is actually not clear whether the application itself answered. This can happen for example for non-TLS connections. 30% remote_banner_unreliable Remote Banner checks of applications that don't offer patch level in version identification. For example, this is the case for many Open Source products due to backport patches. 30% executable_version_unreliable Authenticated executable version checks for Linux(oid) systems where applications don't offer patch level in version identification. 1% general_note General note on potential vulnerability without finding any present application. Transition phase for NVTs and results
The value of 70% is the default minimum used for the default filtering to display the results in the reports.
The QoD is introduced with OpenVAS-8. Any results created with prior versions are assigned the value of 75% during migration.
The transition of the NVTs is a long-term activity. For NVTs that have no QoD assigned yet, scan results will be assigned with 75%.
This setting of 75% ensures that by default the results are visible as before. However, eventually new results might occur with a QoD of 75%. So, this value can not automatically be interpreted as "old pre-qod result".
How to assign a solution type to a NVT
This describes the type of solution available for the vulnerability in a formal way. Use one of the following keywords.
Workaround: Information is available about a configuration or specific deployment scenario that can be used to avoid exposure to the vulnerability. There may be none, one, or more workarounds available. This is typically the "first line of defense" against a new vulnerability before a mitigation or vendor fix has been issued or even discovered.
Mitigation: Information is available about a configuration or deployment scenario that helps to reduce the risk of the vulnerability but that does not resolve the vulnerability on the affected product. Mitigations may include using devices or access controls external to the affected product. Mitigations may or may not be issued by the original author of the affected product, and they may or may not be officially sanctioned by the document producer.
VendorFix: Information is available about an official fix that is issued by the original author of the affected product. Unless otherwise noted, it is assumed that this fix fully resolves the vulnerability.
NoneAvailable: Currently there is no fix available. Information should contain details about why there is no fix.
WillNotFix: There is no fix for the vulnerability and there never will be one. This is often the case when a product has been orphaned, end-of-lifed, or otherwise deprecated. Information should contain details about why there will be no fix issued.
English |
