Lucene search
+L

os_commanding

🗓️ 10 Jun 2013 23:02:10Reported by andresrianchoType 
w3af
 w3af
🔗 w3af.org👁 16 Views

This plugin detects OS commanding vulnerabilities using time delays and writing known files to HTML output

Code
"""
os_commanding.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

"""
from __future__ import with_statement

import w3af.core.data.constants.severity as severity
import w3af.core.data.kb.config as cf

from w3af.core.controllers.plugins.audit_plugin import AuditPlugin
from w3af.core.controllers.delay_detection.exact_delay_controller import ExactDelayController
from w3af.core.controllers.delay_detection.exact_delay import ExactDelay
from w3af.core.data.fuzzer.fuzzer import create_mutants
from w3af.core.data.quick_match.multi_in import MultiIn
from w3af.core.data.constants.file_patterns import FILE_PATTERNS
from w3af.core.data.kb.vuln import Vuln


class os_commanding(AuditPlugin):
    """
    Find OS Commanding vulnerabilities.
    :author: Andres Riancho ([email protected])
    """

    FILE_PATTERNS = FILE_PATTERNS 
    _multi_in = MultiIn(FILE_PATTERNS)

    def __init__(self):
        AuditPlugin.__init__(self)

        #
        #   Some internal variables
        #
        self._special_chars = ['', '&&', '|', ';', '\n', '\r\n']
        self._file_compiled_regex = []

    def audit(self, freq, orig_response, debugging_id):
        """
        Tests an URL for OS Commanding vulnerabilities.

        :param freq: A FuzzableRequest
        :param orig_response: The HTTP response associated with the fuzzable request
        :param debugging_id: A unique identifier for this call to audit()
        """
        # We are implementing two different ways of detecting OS Commanding
        # vulnerabilities:
        #       - Time delays
        #       - Writing a known file to the HTML output
        # The basic idea is to be able to detect ANY vulnerability, so we use
        # ALL of the known techniques
        #
        # Please note that I'm running the echo ones first in order to get them
        # into the KB before the ones with time delays so that the os_commanding
        # exploit can (with a higher degree of confidence) exploit the
        # vulnerability
        #
        # This also speeds-up the detection process a little bit in the cases
        # where there IS a vulnerability present and can be found with both
        # methods.
        self._with_echo(freq, orig_response, debugging_id)
        self._with_time_delay(freq, debugging_id)

    def _with_echo(self, freq, orig_response, debugging_id):
        """
        Tests an URL for OS Commanding vulnerabilities using cat/type to write
        the content of a known file (i.e. /etc/passwd) to the HTML.

        :param freq: A FuzzableRequest
        """
        # Prepare the strings to create the mutants
        command_list = self._get_echo_commands()
        only_command_strings = [v.get_command() for v in command_list]

        # Create the mutants, notice that we use append=False (default) and
        # True to have better coverage.
        mutants = create_mutants(freq,
                                 only_command_strings,
                                 orig_resp=orig_response)
        mutants.extend(create_mutants(freq,
                                      only_command_strings,
                                      orig_resp=orig_response,
                                      append=True))

        self._send_mutants_in_threads(self._uri_opener.send_mutant,
                                      mutants,
                                      self._analyze_echo,
                                      debugging_id=debugging_id)

    def _analyze_echo(self, mutant, response):
        """
        Analyze results of the _send_mutant method that was sent in the
        _with_echo method.
        """
        #
        #   I will only report the vulnerability once.
        #
        if self._has_bug(mutant):
            return

        for file_pattern_match in self._multi_in.query(response.get_body()):

            if file_pattern_match in mutant.get_original_response_body():
                continue

            # Search for the correct command and separator
            sent_os, sent_separator = self._get_os_separator(mutant)

            desc = 'OS Commanding was found at: %s' % mutant.found_at()
            # Create the vuln obj
            v = Vuln.from_mutant('OS commanding vulnerability', desc,
                                 severity.HIGH, response.id,
                                 self.get_name(), mutant)

            v['os'] = sent_os
            v['separator'] = sent_separator
            v.add_to_highlight(file_pattern_match)

            self.kb_append_uniq(self, 'os_commanding', v)
            break

    def _get_os_separator(self, mutant):
        """
        :param mutant: The mutant that is being analyzed.
        :return: A tuple with the OS and the command separator
        that was used to generate the mutant.
        """
        os = separator = None

        # Retrieve the data I need to create the vuln and the info objects
        command_list = self._get_echo_commands()

        # TODO: Are you sure that this works as expected ?!
        for comm in command_list:
            if comm.get_command() in mutant.get_token_value():
                os = comm.get_OS()
                separator = comm.get_separator()

        return os, separator

    def _with_time_delay(self, freq, debugging_id):
        """
        Tests an URL for OS Commanding vulnerabilities using time delays.

        :param freq: A FuzzableRequest
        """
        self._send_mutants_in_threads(func=self._find_delay_in_mutant,
                                      iterable=self._generate_delay_tests(freq, debugging_id),
                                      callback=lambda x, y: None)

    def _generate_delay_tests(self, freq, debugging_id):
        fake_mutants = create_mutants(freq, ['', ])
        fake_mutants.extend(create_mutants(freq, ['', ], append=True))

        for mutant in fake_mutants:
            #
            # Don't try to find an OS commanding using a time delay method
            # if we already found it via echo
            #
            if self._has_bug(mutant):
                return

            for delay_obj in self._get_wait_commands():
                yield mutant, delay_obj, debugging_id

    def _find_delay_in_mutant(self, (mutant, delay_obj, debugging_id)):
        """
        Try to delay the response and save a vulnerability if successful

        :param mutant: The mutant to modify and test
        :param delay_obj: The delay to use
        :param debugging_id: The debugging ID for logging
        """
        if self._has_bug(mutant):
            return

        ed = ExactDelayController(mutant, delay_obj, self._uri_opener)
        ed.set_debugging_id(debugging_id)
        success, responses = ed.delay_is_controlled()

        if not success:
            return

        desc = 'OS Commanding was found at: %s' % mutant.found_at()

        v = Vuln.from_mutant('OS commanding vulnerability', desc,
                             severity.HIGH, [r.id for r in responses],
                             self.get_name(), mutant)

        v['os'] = delay_obj.get_OS()
        v['separator'] = delay_obj.get_separator()

        self.kb_append_uniq(self, 'os_commanding', v)

    def _get_echo_commands(self):
        """
        :return: This method returns a list of commands to try to execute in
                 order to print the content of a known file.
        """
        commands = []
        for special_char in self._special_chars:
            # Unix
            cmd_string = special_char + "/bin/cat /etc/passwd"
            commands.append(Command(cmd_string, 'unix', special_char))
            # Windows
            cmd_string = special_char + "type %SYSTEMROOT%\\win.ini"
            commands.append(Command(cmd_string, 'windows', special_char))

        # Execution quotes
        commands.append(Command("`/bin/cat /etc/passwd`", 'unix', '`'))
        # FoxPro uses run to run os commands. I found one of this vulns !!
        commands.append(
            Command("run type %SYSTEMROOT%\\win.ini", 'windows', 'run'))

        # Now I filter the commands based on the target_os:
        target_os = cf.cf.get('target_os').lower()
        commands = [c for c in commands if target_os in (c.get_OS(), 'unknown')]

        return commands

    def _get_wait_commands(self):
        """
        :return: This method returns a list of commands to try to execute in
                 order to introduce a time delay.
        """
        commands = []
        for special_char in self._special_chars:
            # Windows
            cmd_fmt = special_char + 'ping -n %s localhost'
            delay_cmd = PingDelay(cmd_fmt, 'windows', special_char)
            commands.append(delay_cmd)

            # Unix
            cmd_fmt = special_char + 'ping -c %s localhost'
            delay_cmd = PingDelay(cmd_fmt, 'unix', special_char)
            commands.append(delay_cmd)

            # This is needed for solaris 10
            cmd_fmt = special_char + '/usr/sbin/ping -s localhost %s'
            delay_cmd = PingDelay(cmd_fmt, 'unix', special_char)
            commands.append(delay_cmd)

        # Using execution quotes
        commands.append(PingDelay('`ping -n %s localhost`', 'windows', '`'))
        commands.append(PingDelay('`ping -c %s localhost`', 'unix', '`'))

        # FoxPro uses the "run" macro to exec os commands. I found one of this
        # vulns !!
        commands.append(
            PingDelay('run ping -n %s localhost', 'windows', 'run '))

        # Now I filter the commands based on the target_os:
        target_os = cf.cf.get('target_os').lower()
        commands = [c for c in commands if target_os in (c.get_OS(), 'unknown')]

        return commands

    def get_long_desc(self):
        """
        :return: A DETAILED description of the plugin functions and features.
        """
        return """
        This plugin will find OS commanding vulnerabilities. The detection is
        performed using two different techniques:
            - Time delays
            - Writing a known file to the HTML output

        With time delays, the plugin sends specially crafted requests that,
        if the vulnerability is present, will delay the response for 5 seconds
        (ping -c 5 localhost).

        When using the second technique, the plugin sends specially crafted
        requests that, if the vulnerability is present, will print the content
        of a known file (i.e. /etc/passwd) to the HTML output

        This plugin has a rather long list of command separators, like ";" and
        "`" to try to match all programming languages, platforms and
        installations.
        """


class Command(object):
    """
    Defines a command that is going to be sent to the remote web app.
    """
    def __init__(self, comm, os, sep):
        self._comm = comm
        self._os = os
        self._sep = sep

    def get_OS(self):
        """
        :return: The OS
        """
        return self._os

    def get_command(self):
        """
        :return: The Command to be executed
        """
        return self._comm

    def get_separator(self):
        """
        :return: The separator, could be one of ; && | etc.
        """
        return self._sep

    def __repr__(self):
        fmt = '<Command (OS: %s, Separator: "%s", Command: "%s")>'
        return fmt % (self._os, self._sep, self._comm)


class PingDelay(Command, ExactDelay):
    def __init__(self, delay_fmt, os, sep):
        Command.__init__(self, delay_fmt, os, sep)
        ExactDelay.__init__(self, delay_fmt)
        self._delay_delta = 1

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation