Skip to content

Writing WhatWeb Plugins

bcoles edited this page Oct 25, 2010 · 23 revisions

Note to New Contributors

Please read this before writing a plugin.

  1. Check your desired plugins in ./plugins/
  2. Check them on the Plugin-ToDo page and pending page.
  3. If you don't find them, write them down on that page so that others can see what you'll be doing
  4. Start writing your plugins
  5. Update that page accordingly by removing what you've written
  6. Test your plugins well against a wide range of targets as many as possible
  7. Submit your new plugins via email or forking the project or via the Issues page.

Getting Started

To get started modify plugin-template.rb.txt in the my-plugins folder. Rename it to your desired one like my-first-plugin.rb which will be run by WhatWeb without your needing to put it into ./plugins/ folder.

Also read the tutorial on writing WhatWeb plugins at How to develop WhatWeb plugins.

Typical Plugin (Example)

A typical plugin looks like this:

	Plugin.define "Plone" do
	author "Andrew Horton"
	version "0.2"
	description "CMS http://plone.org"
	examples %w| www.norden.org www.trolltech.com www.plone.net www.smeal.psu.edu|

	matches [
	{:name=>"meta generator tag",
	:regexp=>/<meta name="generator" content="[^>]*http:\/\/plone.org" \/>/},

	{:name=>"plone css",
	:regexp=>/(@import url|text\/css)[^>]*portal_css\/.*plone.*css(\)|")/}, #"

	{:name=>"plone javascript",
	:regexp=>/src="[^"]*ploneScripts[0-9]+.js"/}, #"

	{:text=>'<div class="visualIcon contenttype-plone-site">'},

	{:name=>"div tag, visual-portal-wrapper",
	:certainty=>75,
	:text=>'<div id="visual-portal-wrapper">'},
	]

	def passive
		m=[]
		#X-Caching-Rule-Id: plone-content-types
		#X-Cache-Rule: plone-content-types
		m << {:name=>"X-Caching-Rule-Id: plone-content-types" } if @meta["x-caching-rule-id"] =~ /plone-content-types/i
		m << {:name=>"X-Cache-Rule: plone-content-types" } if @meta["x-cache-rule"] =~ /plone-content-types/i
		m
	end


	end

Matches

There are 3 levels to a plugin. Simple matches, passive and aggressive tests. You don’t need to know ruby to write plugins with simple matches. Passive and aggressive tests are written in ruby.

The matches [] array contains a set of ways to match a website to a system.

The methods are:

* :text			Matches text within the webpage
* :regexp		A regular expression. Append /i for case insensitive matches
* :ghdb			Like a google query
* :md5			MD5 hash of the HTML page
* :tagpattern		Pattern of HTML tags
* :name			This is the name of the match, and is optional.
* :url			The URL has to match this. Used for passive and agressive testing
* :certainty		Optional, defaults to 100. Values are maybe (25), probably (75) and certain (100).
* :version		As a string or number this is a version returned when other methods match
* :version		As a regular expression, this extracts the version information from the HTML. Also requires :version_regexp_offset=>1

If you port a GHDB match, use :ghdb. I usually rewrite the GHDB matches with regular expressions, especially if they require inurl:

Example:

# http://johnny.ihackstuff.com/ghdb?function=detail&id=1840

{ :ghdb=>'"Powered by Vsns Lemon" intitle:"Vsns Lemon"' }

Note the GHDB queries are case insensitive, as a Google query is.

Supported GHDB codes are:

* intitle:
* inurl:
* filetype:

Each plugin can access @body, @meta, @status, @base_uri, @md5, @ip and @tagpattern variables.

Passive tests add matches to the m array, each match is a hash containing the name of the match, probability and more. The entire hash is returned with Full output, Brief output returns just the match, :version and :string

To discover the regular expressions to match against, wget about 20-30 examples into the tests/ folder. Be aware that some software can have dramatic variations between versions.

Generating a signature with ease

Cookies

If the software uses unique cookies (such as Set-Cookie: PastVisitor=1; expires=Wed, 02-Jun-2010 04:05:29 GMT; path=/) then you can search for more instances of the software on ShodanHQ.

HTTP Headers

If the software uses unique HTTP headers (such as Server:BarracudaHTTP) then you can search for more instances of the software on ShodanHQ.

URL Pattern

If the software uses a unique URL pattern (such as inurl:web.config ext:config "ConnectionString") then you can search for more instances of the software on Google using inurl:

Unique Phrases

If the software uses a unique phrase (such as intitle:"Powered by ABC Software") then you can search for more instances of the software on Google using intext: or intitle:

Resources

  • Search ShodanHQ for example software with customer HTTP headers.

  • Search Google for unique phrases, titles or URL structures.

  • Check for demonstration or showcase websites hosted by the vendor.

Tips

More than one copy of WhatWeb

If you're editing a plugin and you have more than one copy of WhatWeb you'll need to change the name of the plugin in Plugin.define or else the plugin's output will be overwritten by the other plugin.

Regex matches

When you're matching HTML make sure you make the entire HTML element. For example:

For the href parameter:

/<a[\s]+href=['"]?http:\/\/this\/is\/a\/unique\/path\/to\/fingerprint\/['"]?[^>]*>/i

For the title parameter:

/<a[\s]+title=['"]?http:\/\/this\/is\/a\/unique\/title\/to\/fingerprint\/['"]?[^>]*>/i

Passive Matches

Passive matches in the def passive section require the same regex in both the @body regular expression and the @body.scan regular expression. For example:

	# Version Detection # Powered by text
	def passive
		m=[]
		@body =~ /powered by ABC software version ([\d\.]+)/
			m << { :version=>@body.scan(/powered by ABC software version ([\d\.]+)/) }
		end
		m
	end
	end

If the two regular expressions differ then the plugin will fail.

lrn2regexp

Learn it, Live it, Love it http://www.rubular.com/

Clone this wiki locally