Writing Vulnerability Checks

This is a tutorial on developing custom vulnerability checks in Nexpose. If you are new to vulnerability development in Nexpose, start here. This tutorial assumes that you have Nexpose installed. You can download Nexpose from the Rapid7 website.

For more information, see:
Vulnerability Check Examples and Converting a NASL check.

Nexpose includes a framework for creating complex vulnerability checks using a simple XML format. Nexpose vulnerability checks are split across two or more files which are parsed by Nexpose when the scan engine is started.

There are 2 types of XML files that make up a vulnerability check:

  • Vulnerability descriptor - A file ending in the .xml extension which contains information about a specific vulnerability (title, description, severity, CVE IDs, CVSS score, etc.).
  • Vulnerability check - A file ending in the .vck extension containing multiple tests, which are compiled at runtime and used by Nexpose to verify the existence (or non-existence) of the vulnerability described in the descriptor.

One vulnerability can have multiple different types of checks (vck's) associated with it.

Creating your first vulnerability check

In this article, we're reimplementing a simple vulnerability check from Nikto, because it's an open source tool that many people will be familiar with. The most recent check added to Nikto is for a WordPress version information leak. Let's do the same check in Nexpose so you can see the difference in approach.

The Nikto check is written as:

"006184","0","3","/wp-links-opml.php","GET","generator=\"WordPress/","","","","","This WordPress script reveals the installed version.","",""

In this check, 006184 is the Nikto vulnerability ID, /wp-links-opml.php is the URL path to request, and generator=\"WordPress/ is the string to look for in the response that would indicate the presence of this vulnerability (the quote is escaped in the Nikto file format).

Now let's create a check for the same vulnerability in Nexpose -- you'll find that the Nexpose format is more complex and takes a bit longer to write (although internally here at Rapid7 we have authoring tools to speed up the process).

Create a vulnerability descriptor (.xml) file

First we create the XML descriptor file. The file looks pretty complex, but it's actually fairly easy to explain. Create a file named cmty-http-wordpress-wplinks-opml-info-leak.xml, containing the following contents:

<?xml version='1.0' encoding='UTF-8'?>
<Vulnerability id="cmty-http-wordpress-wplinks-opml-info-leak" published="2007-05-26" added="2010-03-13" modified="2010-03-13" version="2.0">
  <name>WordPress version information leak via wp-links-opml.php</name>
  <Tags>
     <tag>WordPress</tag>
     <tag>Web</tag>
     <tag>Community</tag>
  </Tags>
  <cvss>(AV:N/AC:L/Au:N/C:P/I:N/A:N)</cvss>
  <AlternateIds>
    <id name="URL">http://blogsecurity.net/wordpress/tools/wp-scanner</id>  
  </AlternateIds>
  <Description>
     <p>The version of the WordPress blog software can be leaked by a request to
     a file named wp-links-opml.php. This page outputs link information in OPML
     format. OPML is the
     <a href="http://en.wikipedia.org/wiki/OPML">Outline Processor Markup Language</a>,
     used to exchange information between blog and RSS aggregators.</p>
  </Description>
  <Solutions>
    <Solution id="cmty-http-wordpress-disable-wplinks-opml" time="30m">
      <summary>Disable access to the wp-links-opml.php page</summary>
      <workaround>
        <p>Evaluate whether OPML needs to be enabled for your blog. If not, disable access to the wp-links-opml.php page by either
        deleting it or by using your web server's access control mechanism (for example .htaccess on Apache) to disable HTTP access
        to this file.</p>
    </workaround>
    </Solution>
  </Solutions>
</Vulnerability>

Let's explain the different parts of this file briefly.

  • The id attribute - Every vulnerability in Nexpose has a unique identifier. This ID distinguishes this vulnerability and is referred to by the corresponding vck files (described below). In this case, the vulnerability ID is "cmty-http-wordpress-wplinks-opml-info-leak". The "cmty" stands for "community". This prefix is used to keep this vulnerability from colliding with anything published by Rapid7. NOTE: The XML file must have the same base name as the ID. The vulnerability ID contains a unique series of up to 255 alphabetic characters, numbers, or hyphens (-).
  • The published attribute - Vulnerabilities may have a published date which describes the date when information about the vulnerability was first released. This attribute is optional, although it is highly recommended to include the attribute if the information for the vulnerability is available. In this case we have chosen 2007-05-26 as our published date because that's the earliest reference I could find for this particular information leak. This attribute may contain a valid date in the form of “YYYY-MM-DD”.
  • The added attribute - The descriptor may have an added date, which is used to note when it was first included in Nexpose. In this case, the added date is 2010-03-13, which is the date this tutorial was written.
  • The modified attribute - This describes when this XML file was last modified. NOTE: If you modify the XML file, you must modify the modified attribute otherwise Nexpose will not re-process this file. If you want Nexpose to re-import this data into its internal database, you must change the modified attribute every time you change this file.
  • The version attribute - This attribute refers to the version of the vulnerability descriptor file format. This should always be set to "2.0".
  • The <name> element - The name element is the title of the vulnerability, as displayed in the vulnerability detail listing in the Nexpose UI and reports.
  • The <cvss> element - This element stores the complete CVSSv2 vector which scores this vulnerability. See http://www.first.org/cvss/cvss-guide.html for more information about CVSS. NOTE: Nexpose automatically computes the CVSS base score from the vector.
  • The <Tags> element - Tagging is used to categorize the vulnerability for purposes of category searching or customization of scan templates. I've given this vulnerability two tags: "Web" and "WordPress".
  • The <AlternateIds> element - This element is used to group several reference identifiers to the vulnerability descriptor. These typically include references to Secunia advisories, CVE IDs, and URLs. Nexpose will automatically generate the correct hyperlink in the UI and reports for most types of IDs based on built-in logic. Alternate IDs can take several forms including:
    *<id name="BID">8725</id> for a Bugtraq ID
    • <id name="CVE">CVE-2002-1850</id> for a CVE ID
  • The <Description> element - This element is used to provide useful information on the behavior of the vulnerability which is displayed in the vulnerability details page in the UI. This element supports a limited subset of "HTML" markup, including <a>, <p>, <ul>, <ol>, and <li> so that it can be parsed into an intermediate documentation format, and subsequently be transformed into (X)HTML, PDF, RTF, and plaintext formats.
  • The <Solutions> element - This element describes "How can I patch or remediate this vulnerability?". The solutions for a vulnerability are grouped under the solutions element and are either referenced by id (for out-of-file solutions) or constructed in the file itself (inline solutions). The reason for solution IDs is so that Nexpose's Remediation Report can automatically figure out how to optimize the remediation steps (e.g. collapsing multiple vulnerabilities into a single common solution that remediates them all). I will skip over most of the logic on how solutions work. The time attribute refers to the estimated amount of time it would take to follow the steps listed in the solution. You can use "30m" to represent 30 minutes or "2h20m" to represent 2 hours and 20 minutes.

Create a vulnerability check (.vck) file

Now create a file named cmty-http-wordpress-wplinks-opml-info-leak.vck containing the following contents:

<VulnerabilityCheck id="cmty-http-wordpress-wplinks-opml-info-leak" scope="endpoint">
  <NetworkService type="HTTP|HTTPS"/>
  <HTTPCheck>
    <HTTPRequest method="GET">
      <URI>/wp-links-opml.php</URI>
    </HTTPRequest>
    <HTTPResponse code="200">
      <regex>generator="WordPress/(.*)"</regex>
    </HTTPResponse>
  </HTTPCheck>
</VulnerabilityCheck>

Let's explain the different parts of this file.

  • The id attribute refers to the same ID used in the xml file. This tells Nexpose which vulnerability this check is checking for.
  • The scope attribute should be set to "endpoint" for service-specific vulnerabilities (vulns affecting a particular port or service) or "node" for system-wide vulnerabilities (vulns affecting the entire system). Web vulns endpoint-specific by definition, while an exploit of the TCP/IP stack would be for the entire node.
  • The <NetworkService> element basically says "Fire this check against any port found to be running the HTTP or HTTPS protocol". Notice the use of the | (pipe) character to indicate HTTP or HTTPS.
  • This particular .vck uses the "<HTTPCheck>" element to indicate a basic HTTP "send a request, match the response" type of check. <HTTPCheck> may contain only one <HTTPRequest> child element and one <HTTPResponse> element. The <HTTPRequest> element may contain one or more <URI> elements. If multiple <URI> elements are present, each URI will be requested in turn, looking for a match to the <HTTPResponse> conditions.
  • The <HTTPResponse> element says "Look for a response with an HTTP status code of 200 whose body matches the given regular expression".
  • The <regex> basically uses Perl 5 syntax, with some small differences. This is matched against the HTTP response body. See http://nlp.stanford.edu/nlp/javadoc/gnu-regexp-docs/syntax.html for a complete syntax reference. If you want to do case-insensitive matching, specify <regex cflags="REG_ICASE">. Pattern matching occurs on a line-by-line basis -- if you want the regex to match across multiple lines, specify <regex cflags="REG_LINE_ANY_CRLF">.

Deploying your vulnerability check

To deploy this vulnerability check into Nexpose, simply copy your .xml and corresponding .vck file into the following directory:

<nexpose-install-dir>/plugins/java/1/CustomScanner/1/

and run the console command "load content". Watch for errors on the console (look in nsc.log) when Nexpose content is being reloaded. If you made any mistakes, Nexpose will log some error messages when it compiles the new vulnerabilities. If everything was successful, you should see something like the following message in the log:

2015-12-18T08:59:43 [INFO] Inserted 1 vulnerabilities.
...
2015-12-18T08:59:55 [INFO] Load Content command complete.

Browse to https://<nexpose>:3780/vulnerability/vuln-summary.jsp?vulnid=cmty-http-wordpress-wplinks -opml-info-leak. You should see the details of your new vulnerability. Review the information and correct any mistakes (don't forget to re-deploy the file to the Nexpose directory after you've made changes).

Run a scan with your new vulnerability

Your new vulnerability will automatically be included in most default scan templates. Simply run a scan against a server that has this vulnerability and see whether it shows up. Nexpose should show any new affected assets under the vulnerability.html page URL described above (don't forget to refresh the page). If the system is vulnerable but your check is not being detected, try running a Vulnerability Report Card report (with detail level set to High) for the affected asset(s) to see why Nexpose did not find the vulnerability.

Common Vulnerability Check Examples

Service banner checks

The <NetworkService> element checks for the presence of a certain service running on the network. The type attribute indicates the protocol. Multiple OR'd types are separated by a | (pipe) character. The example below uses "HTTP|HTTPS" to indicate HTTP or HTTPS.

The <Product> element indicates a fingerprinted product found running on that service. The <version> element checks for a range of product version numbers. NOTE: The <high> version number is exclusive -- in other words <high> should be equal to the next NON-vulnerable version of that product. In the example below, Apache 2.0.45 contains the patch.

http-apache-excessive-newline-dos.vck

<VulnerabilityCheck id="http-apache-excessive-newline-dos" scope="endpoint">
   <NetworkService type="HTTP|HTTPS">
      <Product name="Apache">
         <version>
            <range><low>2.0.0</low><high>2.0.45</high></range>
         </version>
      </Product>
   </NetworkService>
</VulnerabilityCheck>

ftp-proftpd-cwd-format-string.vck

Tests for the existence of a vulnerability using a FTP banner version test:

<VulnerabilityCheck id="ftp-proftpd-cwd-format-string" version="1.0" scope="endpoint">
   <NetworkService type="FTP">
      <Product name="ProFTPD">
         <version><range><high>1.2.0rc3</high></range></version>
      </Product>
   </NetworkService>
</VulnerabilityCheck>

ftp-servu-directory-traversal.vck

Connects to an FTP server with any available credentials, executes a directory change command, and checks the result code and text.

<VulnerabilityCheck id="ftp-servu-directory-traversal" version="1.0" scope="endpoint">
   <NetworkService type="FTP"/>
   <FTPCheck login="1">
      <FTPRequestResponse>
         <FTPRequest>CWD \\..%20.</FTPRequest>
         <FTPResponse code="250">
            <regex>Directory changed to /..</regex>
         </FTPResponse>
      </FTPRequestResponse>
   </FTPCheck>
</VulnerabilityCheck>

http-website-long-options-bof.vck

Example version check for two different product editions in one check file.

<VulnerabilityCheck id="http-website-long-options-bof" scope="endpoint">
   <NetworkService type="HTTP|HTTPS">
      <Product name="WebSite">
         <version><range><high>3.5.15</high></range></version>
      </Product>
      <Product name="WebSitePro">
         <version><range><high>3.5.15</high></range></version>
      </Product>
   </NetworkService>
</VulnerabilityCheck>

php-ecalloc-integer-overflow.vck

Sometimes you want to check for the version of a particular <Component> of a <Product>, for example PHP.

<VulnerabilityCheck id="php-ecalloc-integer-overflow" scope="endpoint">
   <NetworkService type="HTTP|HTTPS">
      <Product name="Apache">
         <Component name="PHP">
            <version><range><low>4.0.0</low><high>4.3.0</high></range></version>
            <version><range><low>5.0.0</low><high>5.2.0</high></range></version>
         </Component>
      </Product>
   </NetworkService>
</VulnerabilityCheck>

Authenticated Windows checks

In order for Nexpose to execute authenticated Windows checks, it must have access to either WMI or the Remote Registry service, which is usually discovered using a default account check (known default passwords) or administrative credentials specified in the site configuration of the Nexpose web interface.

ActiveXControlInstalled - Checks for the presence of an ActiveX control

The <ActiveXControlInstalled> element allows you to test for the presence of ActiveX control on a Windows system (by GUID). By default, this element does honor the kill-bit. In other words, if the ActiveX control is installed but the kill-bit is set, the check will report "not vulnerable". If you want to report the vulnerability even if the kill-bit is set, specify <ActiveXControlInstalled honorKillBit="0">

ibm-access-support-activex-bof.vck

<?xml version='1.0' encoding='UTF-8'?>
<VulnerabilityCheck id="ibm-access-support-activex-bof" scope="node" version="1.0">
   <ActiveXControlInstalled guid="74FFE28D-2378-11D5-990C-006094235084"/>
</VulnerabilityCheck>

WindowsRegistry - Checks for registry keys/values in the Windows registry

Windows registry check tests are tests which check for the existence of certain keys, values, or value data in a windows registry service found by Nexpose during a scan

w32-protoride-b-worm.vck

<VulnerabilityCheck id="w32-protoride-b-worm" scope="node">
   <WindowsRegistry>
      <registryKey name="HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run" mustNotBePresent="1">
         <registryValue name="Windows Taskbar Manager"><regex cflags="REG_ICASE">.*</regex></registryValue>
      </registryKey>
   </WindowsRegistry>
</VulnerabilityCheck>

The <WindowsRegistry> element is the top-level element of a windows registry test. This element must contain at least one <registryKey> sub-element. No other elements are allowed.

The <registryKey> element is the top-level element describing a registry key and value to test for. It has two attributes: name and mustNotBePresent. The name is the name of the registry key to check for. The mustNotBePresent attribute is worded a backwards. If mustNotBePresent="1", it means "Fire this check if the key is present, otherwise return not vulnerable".

The <registryValue> element specifies the expected data assigned to a specific registry value. It contains three attributes: name, type and default.

  • The name attribute specifies the name of the registry value (under the parent key).
  • The type attribute specifies the expected type of the registry value, which must be one of: REG_SZ, REG_EXPAND_SZ, REG_BINARY, REG_DWORD, REG_DWORD_LITTLE_ENDIAN,, REG_DWORD_BIG_ENDIAN, REG_LINK, REG_MULTI_SZ, REG_RESOURCE_LIST, REG_FULL_RESOURCE_DESCRIPTOR, REG_RESOURCE_REQUIREMENTS_LIST, REG_QWORD, REG_QWORD_LITTLE_ENDIAN. See http://msdn.microsoft.com/en-us/library/ms724884%28VS.85%29.aspx for more information on different value types.
  • The default attribute is used when you want to check for the registry "default value" under a key without specifying the name attribute.

If you want to test for existence of registry keys and registry values, use the <registryKeyExists> and <registryValueExists> elements.

trojan-gimmiv.vck

<VulnerabilityCheck id="trojan-gimmiv" scope="node">
  <or>
    <!-- created by the dropper component (Gimmiv.A) -->
    <WindowsRegistry>
      <registryKeyExists name="HKLM\SYSTEM\CurrentControlSet\Services\sysmgr\Parameters">
        <registryValueExists name="ServiceDll"/>
        <registryValueExists name="ServiceMain"/>
      </registryKeyExists>
      <registryKeyExists name="HKLM\SYSTEM\CurrentControlSet\Services\sysmgr">
        <registryValueExists name="DisplayName"/>
        <registryValueExists name="ImagePath"/>
      </registryKeyExists>
    </WindowsRegistry>
    <WindowsFileExists>
      <!-- created by the dropper component (Gimmiv.A) -->
      <fileExists name="%CSIDL_SYSTEM%\wbem\sysmgr.dll"/>
      <!-- created by the worm component (Gimmiv.B) -->
      <fileExists name="%CSIDL_SYSTEM%\wbem\winbaseInst.exe"/>
      <fileExists name="%CSIDL_SYSTEM%\wbem\winbase.dll"/>
      <fileExists name="%CSIDL_SYSTEM%\wbem\basesvc.dll"/>
      <fileExists name="%CSIDL_SYSTEM%\wbem\syicon.dll"/>
    </WindowsFileExists>
  </or>
</VulnerabilityCheck>

WindowsFileExists - Check for existence of files

The <fileExists> element is used to determine whether or not a file is available on a system. The trojan-gimmiv.vck example above shows an example of how this check works. The name attribute is the name of the file. Certain well-known Windows paths can be specified using percent substitution. For example, it is not always known where the Windows directory is. Usually, this is ‘C:\WINDOWS’, however it may vary depending on the configuration of the device. During runtime (scanning), Nexpose will discover the windows root for the device under test and replace the ‘%windir%’ string in the path for the fileVersion test with the actual windows root directory on the device. The following substitution strings are supported:

  • %SystemDrive%
  • %windir%
  • %CSIDL_SYSTEM%
  • %CSIDL_SYSTEMX86%
  • %ProgramFiles%
  • %ProgramFiles(x86)%
  • %CommonProgramFiles%
  • %CSIDL_PROGRAM_FILES_COMMONX86%
  • %SQLPath%
  • %ExchangePath%
  • %OfficePath%

WindowsFileVersion - Check for version of a Windows .EXE or .DLL

<WindowsFileVersion> tests allow testing of versions retrieved from files on the file system of the endpoint under test during a scan. If a file versioning service is found during a scan, Nexpose will use them to execute these tests.

A file version check test would resemble the following:

<WindowsFileVersion>
  <fileVersion name="%windir%\system32\scesrv.dll" mustExist="1">
    <version><range><high inclusive="0">5.0.2195.3649</high></range></version>
  </fileVersion>
</WindowsFileVersion>

Here is a more complex check which contains tests for a specific file version only on Windows XP SP2 (a combination of system and windows file version check tests).

<VulnerabilityCheck id="acme-windows-file-check-1" scope="node" version="1.0">
  <System>
    <OS minCertainty="1.0" name="Windows XP Professional" vendor="Microsoft">
      <version>
        <value>SP2</value>
      </version>
    </OS>
  </System>
  <WindowsFileVersion>
    <fileVersion name="%windir%\system32\shsvcs.dll" mustExist="1">
      <version><range><high inclusive="0">6.0.2900.3051</high></range></version>
    </fileVersion>
  </WindowsFileVersion>
</VulnerabilityCheck>

Here is an example check for a particular version of Internet Explorer which tests multiple registry values for that specific product.

<VulnerabilityCheck id="acme-windows-registry-check-1" scope="node" version="1.0">
  <InstalledSoftware>
    <Product minCertainty="1.0" name="Internet Explorer" vendor="Microsoft">
      <version>
        <value>5.01 SP4</value>
      </version>
    </Product>
  </InstalledSoftware>
  <WindowsRegistry>
    <registryKey name="HKLM\SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility\{D9998BD0-7957-11D2-8FED-00606730D3AA}">
      <registryValue name="Compatibility Flags" type="REG_DWORD">
        <value>1024</value>
      </registryValue>
    </registryKey>
    <registryKey name="HKLM\SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility\{BE4191FB-59EF-4825-AEFC-109727951E42}">
      <registryValue name="Compatibility Flags" type="REG_DWORD">
        <value>1024</value>
      </registryValue>
    </registryKey>
  </WindowsRegistry>
</VulnerabilityCheck>

InstalledSoftware

Checks for version of installed software.

<InstalledSoftware>
    <Product minCertainty="1.0" name="Exchange 2000 Server" vendor="Microsoft">
      <version>
        <value>SP3</value>
      </version>
    </Product>
  </InstalledSoftware>

Default account checks

n Nexpose, all username/password checks work basically the same way. You have a <NetworkService> element with the type of service, followed by a <DefaultAccount> element. The <uid> and <password> elements should be self-explanatory. The optional <realm> element can be used to specify either a domain name (for CIFS), a database or schema (for database authentication), or an authentication realm (for HTTP).

The following type attribute values can be used for <NetworkService> with a <DefaultAccount> check:

  • "IBM AS400 PortMapper"
  • "pcAnywhere-control"
  • "CIFS" (optional <realm> specifies Windows domain name)
  • "DB2" (<realm> specifies database name, e.g. SAMPLE)
  • "FTP"
  • "HTTP"
  • "MySQL" (<realm> specifies database name, e.g. mysql)
  • "Novell Netware"
  • "Oracle" (<realm> specifies database name)
  • "Postgres" (<realm> specifies database name, e.g. template1)
  • "Remote Execution" (rexec i.e. port 512/tcp)
  • "SNMP" (specify only the <password> element which will be the SNMP community name, e.g. private)
  • "SSH"
  • "TDS" (Microsoft SQL Server, <realm> specifies database name, e.g. master)
  • "Sybase" (<realm> specifies database name, e.g. master)
  • "Telnet"

cifs-default-password-administrator-password.vck

Checks for Administrator/password on CIFS.

<VulnerabilityCheck id="CIFS-GENERIC-0002" scope="node">
   <NetworkService type="CIFS"/>
   <DefaultAccount>
      <uid>Administrator</uid>
      <password>password</password>
   </DefaultAccount>
</VulnerabilityCheck>

ssh-default-account-root-password-password.vck

Checks for root/password on SSH.

<VulnerabilityCheck id="ssh-default-account-root-password-password" scope="endpoint">
   <NetworkService type="SSH"/>
   <DefaultAccount>
      <uid>root</uid>
      <password>password</password>
   </DefaultAccount>
</VulnerabilityCheck>

telnet-default-account-root-password-password.vck

Checks for root/password on Telnet.

<VulnerabilityCheck id="telnet-default-account-root-password-password" scope="endpoint">
   <NetworkService type="Telnet"/>
   <DefaultAccount>
      <uid>root</uid>
      <password>password</password>
      <realm></realm>
   </DefaultAccount>
</VulnerabilityCheck>

mysql-default-account-admin-nopassword.vck

Checks for admin with no password on MySQL.

<VulnerabilityCheck id="mysql-default-account-admin-nopassword" scope="endpoint">
   <NetworkService type="MySQL"/>
   <DefaultAccount>
      <uid>admin</uid>
      <password></password>
      <realm>mysql</realm>
   </DefaultAccount>
</VulnerabilityCheck>

Operating System fingerprint checks

<System>
    <OS minCertainty="1.0" name="Windows NT Server" vendor="Microsoft">
      <version>
        <value>4.0 SP5</value>
      </version>
      <version>
        <value>4.0 SP6a</value>
      </version>
      <version>
        <value>4.0 SP4</value>
      </version>
    </OS>
  </System>

Denial-of-service checks

Sometimes you want to do an unsafe check, for example a denial-of-service. In this case, set the safe attribute of <VulnerabilityCheck> to "0". This will cause this check to be skipped unless the user explicitly enables unsafe checks in their scan template.

<VulnerabilityCheck id="http-jrun-long-url-bof" safe="0" scope="endpoint">
   <NetworkService type="HTTP|HTTPS">
      <Product name="JRun"/>
   </NetworkService>
   <HTTPCheck retries="3">
      <HTTPRequest method="GET">
         <URI>/<garbage length="5200">R7</garbage>.jsp</URI>
      </HTTPRequest>
      <HTTPResponse><thrownException class="java.io.IOException"/></HTTPResponse>
   </HTTPCheck>
   <TCPStatusCheck wait="2500" status="closed"/>
</VulnerabilityCheck>

Boolean expressions

Sometimes you may want to group a set of checks with <and> or <or> (or either in combination) to form complex boolean tests. For example:

VulnerabilityCheck id=”example-http-sensitive-data” scope=”endpoint”>
<NetworkService type=”HTTP|HTTPS”/>
<HTTPCheck>
   <HTTPRequest method=”GET”><URI>/</URI>
   <HTTPResponse code=”200”>
      <or>
         <regex>secret password</regex>
         <regex>SSN: [0-9]{3}-[0-9]{2}-[0-9]{4}</regex>
      </or>
   </HTTPResponse>
</HTTPCheck>
</VulnerabilityCheck>

Writing Vulnerability Checks