projectdiscovery / nuclei

Nuclei is a fast, customizable vulnerability scanner powered by the global security community and built on a simple YAML-based DSL, enabling collaboration to tackle trending vulnerabilities on the internet. It helps you find vulnerabilities in your applications, APIs, networks, DNS, and cloud configurations.
https://docs.projectdiscovery.io/tools/nuclei
MIT License
20.45k stars 2.49k forks source link

Mapping functionality in the matchers #2933

Open dogancanbakir opened 1 year ago

dogancanbakir commented 1 year ago

Please describe your feature request and the use case of this feature::

This feature can be valuable when you've leads or hints instead of the exact value you want to extract in the response and want to pass it to the subsequent template in a workflow. Imagine a tool that publishes a new unique copyright text with every release and that you wanted to write an End-of-life(EOL) detection workflow for that tool. Let's say there is specific text in the copyright that allows you to do the mapping. Writing a version detection will be easy, as in this example:

    matchers-condition: or
    matchers:

      - type: regex
        regex:
          - '^Magento/.*$'

      - type: word
        name: magento-1.9
        words:
          - "Copyright (c) 2014 Magento Inc."

      - type: word
        name: magento-1.8
        words:
          - "Copyright (c) 2013 Magento Inc."

      - type: word
        name: magento-1.7
        words:
          - "Copyright (c) 2012 Magento Inc."

      - type: word
        name: magento-1.6
        words:
          - "Copyright (c) 2011 Magento Inc."

      - type: word
        name: magento-1.4.1-1.5
        words:
          - "Copyright (c) 2010 Magento Inc."

      - type: word
        name: magento-1.4.0
        words:
          - "Copyright (c) 2009 Irubin Consulting Inc."

      - type: word
        name: magento-1.0-1.3
        words:
          - "Copyright (c) 2008 Irubin Consulting Inc."

The problem is to pass the mapped named variable in the matchers as a single variable to the following template in a workflow to do EOL detection.

    matchers:
      - type: dsl
        dsl:
          - "compare_versions(version, '<2.4.4')"

I believe, something like this in the matcher would work (and, of course, we should be able to pass this to the subsequent template):

 - type: word
    version: 1.9
    words:
      - "Copyright (c) 2014 Magento Inc."

  - type: word
    version: 1.8
    words:
      - "Copyright (c) 2013 Magento Inc."`

Example PR: https://github.com/projectdiscovery/nuclei-templates/pull/6182

dogancanbakir commented 1 year ago

@princechaddha @ehsandeep FYI

JaneX8 commented 1 year ago

So, compare_versions and version are a nice solution but only in that particular situation. If you want to do any generic fingerprinting, meaning guessing a version approximately, it isn't helpful. Assuming you have multiple matchers resulting in one exact version, like both matchers could be version 1.9, then that solution works great:

 - type: word
    version: 1.9
    words:
      - "Copyright (c) 2014 Magento Inc."
      - "Copyright (c) 2014 Magento Gmbh."

But in the case one matcher results in an approximate amount of versions or a version range, this wouldn't work. For example I want to make a nuclei version of this NSE script: https://svn.nmap.org/nmap/scripts/http-php-version.nse.

You have one matcher, for example dsl matching the md5ed body against 4b2c92409cf0bcf465d199e93a15ac3f and multiple corresponding possible versions {"4.3.11", "4.4.0 - 4.4.9", "5.0.4 - 5.0.5", "5.1.0 - 5.1.2"}. Above solution would then only work when writing a lot of lines and matchers. Like:

 - type: word
    version: 4.3.11
    words:
      - "4b2c92409cf0bcf465d199e93a15ac3f"
 - type: word
    version: 4.4.0-4.4.9
    words:
      - "4b2c92409cf0bcf465d199e93a15ac3f"

This introduces a new problem. Using version ranges (see second example above). We could fix that by allowing version: to be an array but that's only allowing multiple versions and still not ranges. Like:

- type: word
    version: 
      - 4.3.11
      - 4.4.0
      - 4.4.1
      - 4.4.2
      - 4.4.3
      - 4.4.4
    words:
      - 4b2c92409cf0bcf465d199e93a15ac3f

But the problem with that is you still can't use version ranges and it would still take a lot of lines. I propose the solution of accepting multidimensional arrays in version: and potentially renaming it versions: instead, or possibleVersions:, or any other accurate descriptive name. A multidimensional array solution would look somewhat like this:

- type: word
    versions: 
      - 4.3.11
      - [4.0.0, 4.4.9]
      - [5.0.4, 5.0.5]
    words:
      - "4b2c92409cf0bcf465d199e93a15ac3"

Where [4.0.0, 4.4.9] is a range of [minVersion, maxVersion]. Assuming SEMVER formatting, MAJOR.MINOR.PATCH, although this would probably complicate the compare_versions function quite a bit.

Also the use of one value instead of (first level array) should probably still be allowed. Like version: 1.8. Unfortunately I never wrote in golang yet, so I hope that someone else could pick this up.