Skip to content

Commit

Permalink
Merge pull request #17337 from yvonneKim/master
Browse files Browse the repository at this point in the history
Beginning work on private key grep plugin
  • Loading branch information
andresriancho committed Oct 25, 2018
2 parents f5fb23b + 679e94a commit a799adb
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 4 deletions.
4 changes: 2 additions & 2 deletions w3af/plugins/crawl/open_api.py
Expand Up @@ -392,8 +392,8 @@ def get_options(self):
ol.add(o)

d = 'Path to Open API specification'
h = ('By default, the plugin looks for the API specification on the target,',
' but sometimes applications do not provide an API specification.',
h = ('By default, the plugin looks for the API specification on the target,'
' but sometimes applications do not provide an API specification.'
' Set this parameter to specify a local path to the API specification.'
' The file must have .json or .yaml extension.')
o = opt_factory('custom_spec_location', self._custom_spec_location, d, INPUT_FILE, help=h)
Expand Down
126 changes: 126 additions & 0 deletions w3af/plugins/grep/keys.py
@@ -0,0 +1,126 @@
"""
keys.py
Copyright 2006 Andres Riancho
This file is part of w3af, http://w3af.org/ .
w3af is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 2 of the License.
w3af is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with w3af; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""

import w3af.core.data.constants.severity as severity
from w3af.core.data.quick_match.multi_in import MultiIn
from w3af.core.controllers.plugins.grep_plugin import GrepPlugin
from w3af.core.data.kb.vuln import Vuln
from w3af.core.data.kb.info import Info


class keys(GrepPlugin):
"""
Grep every page for public and private keys.
:author: Yvonne Kim
"""
def __init__(self):
GrepPlugin.__init__(self)

self.PUBLIC = 'public'
self.PRIVATE = 'private'

PUBLIC = 'public'
PRIVATE = 'private'

KEY_FORMATS = (
# RSA (PKCS1)
('-----BEGIN RSA PRIVATE KEY-----', ('RSA-PRIVATE', PRIVATE)),
('-----BEGIN RSA PUBLIC KEY-----', ('RSA-PUBLIC', PUBLIC)),
('ssh-rsa', ('RSA-PUBLIC', PUBLIC)),

# DSA
('-----BEGIN DSA PRIVATE KEY-----', ('DSA-PRIVATE', PRIVATE)),
('-----BEGIN DSA PUBLIC KEY-----', ('DSA-PUBLIC', PUBLIC)),
('ssh-dss', ('DSA-PUBLIC', PUBLIC)),

# Elliptic Curve
('-----BEGIN EC PRIVATE KEY-----', ('EC-PRIVATE', PRIVATE)),
('-----BEGIN EC PUBLIC KEY-----', ('EC-PUBLIC', PUBLIC)),
('ecdsa-sha2-nistp256', ('EC-PUBLIC', PUBLIC)),

# SSH2
('---- BEGIN SSH2 PUBLIC KEY ----', ('SSH2-PRIVATE', PRIVATE)),
('---- BEGIN SSH2 PRIVATE KEY ----', ('SSH2-PUBLIC', PUBLIC)),

# ed25519 (OpenSSH)
('-----BEGIN OPENSSH PRIVATE KEY-----', ('ED25519-SSH-PRIVATE', PRIVATE)),
('-----BEGIN OPENSSH PUBLIC KEY-----', ('ED25519-SSH-PUBLIC', PUBLIC)),
('ssh-ed25519', ('ED25519-SSH-PUBLIC', PUBLIC)),

# PKCS8
('-----BEGIN PRIVATE KEY-----', ('PKCS8-PRIVATE', PRIVATE)),
('-----BEGIN PUBLIC KEY-----', ('PKCS8-PUBLIC', PUBLIC)),
('-----BEGIN ENCRYPTED PRIVATE KEY-----', ('PKCS8-ENCRYPTED-PRIVATE', PRIVATE)),
('-----BEGIN ENCRYPTED PUBLIC KEY-----', ('PKCS8-ENCRYPTED-PUBLIC', PUBLIC)),

# XML
('<RSAKeyPair>', ('XML-RSA', PRIVATE)),
('<RSAKeyValue>', ('.NET-XML-RSA', PUBLIC))
)

self._multi_in = MultiIn(KEY_FORMATS)


def grep(self, request, response):
"""
Plugin entry point, find the error pages and report them.
:param request: The HTTP request object.
:param response: The HTTP response object
:return: None
"""
if not response.is_text_or_html():
return

if not response.get_code() == 200:
return

for _, (key, keypair_type) in self._multi_in.query(response.body):
desc = u'The URL: "%s" discloses a key of type: "%s"'
desc %= (response.get_url(), key)

if keypair_type == self.PUBLIC:
item = Info(
'Public key disclosure', desc, response.id, self.get_name())

elif keypair_type == self.PRIVATE:
item = Vuln(
'Private key disclosure', desc, severity.HIGH, response.id, self.get_name())

item.set_url(response.get_url())
item.add_to_highlight(key)

self.kb_append(self, 'keys', item)


def get_long_desc(self):
"""
:return: A DETAILED description of the plugin functions and features.
"""

return """
This plugin scans responses for keys in a few of the most common formats.
Private keys are classified as vulnerabilities while public keys are stored
as information.
"""
116 changes: 116 additions & 0 deletions w3af/plugins/tests/grep/test_keys.py
@@ -0,0 +1,116 @@
"""
test_keys.py
Copyright 2011 Andres Riancho
This file is part of w3af, http://w3af.org/ .
w3af is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 2 of the License.
w3af is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with w3af; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
import os
import unittest

import w3af.core.data.kb.knowledge_base as kb

from w3af.plugins.grep.keys import keys
from w3af.core.data.dc.headers import Headers
from w3af.core.data.url.HTTPResponse import HTTPResponse
from w3af.core.data.request.fuzzable_request import FuzzableRequest
from w3af.core.data.parsers.doc.url import URL
from w3af.core.data.kb.vuln import Vuln
from w3af.core.data.kb.info import Info
from w3af.plugins.tests.helper import PluginTest


class TestKeys(PluginTest):

def setUp(self):
self.plugin = keys()
kb.kb.clear('keys', 'keys')

def tearDown(self):
self.plugin.end()

def test_private_key(self):
body = '-----BEGIN PRIVATE KEY-----'
url = URL('http://www.w3af.com/')
headers = Headers([('content-type', 'text/html')])
response = HTTPResponse(200, body, headers, url, url, _id=1)
request = FuzzableRequest(url, method='GET')
self.plugin.grep(request, response)

data = kb.kb.get('keys', 'keys')
self.assertEquals(len(data), 1)
self.assertEquals(type(data[0]), Vuln)

def test_public_key(self):
body = '-----BEGIN PUBLIC KEY-----'
url = URL('http://www.w3af.com/')
headers = Headers([('content-type', 'text/html')])
response = HTTPResponse(200, body, headers, url, url, _id=1)
request = FuzzableRequest(url, method='GET')
self.plugin.grep(request, response)

data = kb.kb.get('keys', 'keys')
self.assertEquals(len(data), 1)
self.assertEquals(type(data[0]), Info)

def test_xml_key(self):
body = '<RSAKeyValue>'
url = URL('http://www.w3af.com/')
headers = Headers([('content-type', 'text/html')])
response = HTTPResponse(200, body, headers, url, url, _id=1)
request = FuzzableRequest(url, method='GET')
self.plugin.grep(request, response)

data = kb.kb.get('keys', 'keys')
self.assertEquals(len(data), 1)

def test_public_ecdsa_key(self):
body = 'ecdsa-sha2-nistp256'
url = URL('http://www.w3af.com/')
headers = Headers([('content-type', 'text/html')])
response = HTTPResponse(200, body, headers, url, url, _id=1)
request = FuzzableRequest(url, method='GET')
self.plugin.grep(request, response)

data = kb.kb.get('keys', 'keys')
self.assertEquals(len(data), 1)
self.assertEquals(type(data[0]), Info)

def test_multi_match(self):
body = """
-----BEGIN OPENSSH PRIVATE KEY----- ssh-ed25519
------------------------------test <RSAKeyValue> <PrivateKey>
"""
url = URL('http://www.w3af.com/')
headers = Headers([('content-type', 'text/html')])
response = HTTPResponse(200, body, headers, url, url, _id=1)
request = FuzzableRequest(url, method='GET')
self.plugin.grep(request, response)

data = kb.kb.get('keys', 'keys')
self.assertEquals(len(data), 3)

def test_no_match(self):
body = '-----BEGIN-----ssh----- BEGIN PRIVATE PUBLIC KEY'
url = URL('http://www.w3af.com/')
headers = Headers([('content-type', 'text/html')])
response = HTTPResponse(200, body, headers, url, url, _id=1)
request = FuzzableRequest(url, method='GET')
self.plugin.grep(request, response)

data = kb.kb.get('keys', 'keys')
self.assertEquals(len(data), 0)
5 changes: 3 additions & 2 deletions w3af/plugins/tests/test_basic.py
Expand Up @@ -38,7 +38,8 @@

from w3af.core.data.options.option_types import (
BOOL, INT, FLOAT, STRING, URL, IPPORT, LIST,
REGEX, COMBO, INPUT_FILE, OUTPUT_FILE, PORT, IP)
REGEX, COMBO, INPUT_FILE, OUTPUT_FILE, PORT, IP,
QUERY_STRING, HEADER)

from w3af.plugins.tests.helper import PluginTest, PluginConfig

Expand Down Expand Up @@ -75,7 +76,7 @@ def test_plugin_options(self):

OPTION_TYPES = (
BOOL, INT, FLOAT, STRING, URL, IPPORT, LIST, REGEX, COMBO,
INPUT_FILE, OUTPUT_FILE, PORT, IP)
INPUT_FILE, OUTPUT_FILE, PORT, IP, QUERY_STRING, HEADER)

for plugin_type in self.plugins:
for plugin in self.plugins[plugin_type]:
Expand Down

0 comments on commit a799adb

Please sign in to comment.