@@ -24,17 +24,22 @@ def initialize(info = {})
24
24
[
25
25
'Thomas McCarthy "smilingraccoon" <smilingraccoon[at]gmail.com>' ,
26
26
'Brandon McCann "zeknox" <bmccann[at]accuvant.com>' ,
27
- 'FireFart' , # Original PoC
27
+ 'FireFart' # Original PoC
28
28
] ,
29
29
'License' => MSF_LICENSE ,
30
30
'References' =>
31
31
[
32
32
[ 'URL' , 'http://www.securityfocus.com/archive/1/525045/30/30/threaded' ] ,
33
33
[ 'URL' , 'http://www.ethicalhack3r.co.uk/security/introduction-to-the-wordpress-xml-rpc-api/' ] ,
34
- [ 'URL' , 'https://github.com/FireFart/WordpressPingbackPortScanner' ] ,
35
- ] ,
34
+ [ 'URL' , 'https://github.com/FireFart/WordpressPingbackPortScanner' ]
35
+ ]
36
36
) )
37
37
38
+ register_options (
39
+ [
40
+ OptString . new ( 'TARGETURI' , [ true , 'The path to wordpress installation (e.g. /wordpress/)' , '/' ] )
41
+ ] , self . class )
42
+
38
43
register_advanced_options (
39
44
[
40
45
OptInt . new ( 'NUM_REDIRECTS' , [ true , "Number of HTTP redirects to follow" , 10 ] )
@@ -55,16 +60,21 @@ def get_xml_rpc_url(ip)
55
60
vprint_status ( "Enumerating XML-RPC URI for #{ ip } ..." )
56
61
57
62
begin
63
+
64
+ uri = target_uri . path
65
+ uri << '/' if uri [ -1 , 1 ] != '/'
66
+
58
67
res = send_request_cgi (
59
68
{
60
69
'method' => 'HEAD' ,
70
+ 'uri' => "#{ uri } "
61
71
} )
62
72
# Check if X-Pingback exists and return value
63
- unless res . nil?
64
- unless res [ 'X-Pingback' ] . nil?
73
+ if res
74
+ if res [ 'X-Pingback' ]
65
75
return res [ 'X-Pingback' ]
66
76
else
67
- print_error ( "X-Pingback header not found, quiting " )
77
+ vprint_status ( "X-Pingback header not found at #{ ip } " )
68
78
return nil
69
79
end
70
80
else
@@ -92,29 +102,29 @@ def generate_pingback_xml (target, valid_blog_post)
92
102
93
103
def get_blog_posts ( xml_rpc , ip )
94
104
# find all blog posts within IP and determine if pingback is enabled
95
- vprint_status ( "Enumerating Blog posts..." )
96
- blog_posts = { }
105
+ vprint_status ( "Enumerating Blog posts on #{ ip } ..." )
106
+ blog_posts = ""
107
+
108
+ uri = target_uri . path
109
+ uri << '/' if uri [ -1 , 1 ] != '/'
97
110
98
111
# make http request to feed url
99
112
begin
113
+ vprint_status ( "Resolving #{ ip } #{ uri } ?feed=rss2 to locate wordpress feed..." )
100
114
res = send_request_cgi ( {
101
- 'uri' => '/ ?feed=rss2' ,
102
- 'method' => 'GET' ,
115
+ 'uri' => " #{ uri } ?feed=rss2" ,
116
+ 'method' => 'GET'
103
117
} )
104
118
105
- resolve = true
106
119
count = datastore [ 'NUM_REDIRECTS' ]
107
- while ( res . code == 301 || res . code == 302 ) and count != 0
108
- if resolve
109
- print_status ( "Resolving #{ ip } /?feed=rss2 to locate wordpress feed..." )
110
- resolve = false
111
- else
112
- vprint_status ( "Web server returned a #{ res . code } ...following to #{ res . headers [ 'location' ] } " )
113
- end
114
- uri = res . headers [ 'location' ] . sub ( /.*?#{ ip } / , "" )
120
+
121
+ # Follow redirects
122
+ while ( res . code == 301 || res . code == 302 ) and res . headers [ 'Location' ] and count != 0
123
+ vprint_status ( "Web server returned a #{ res . code } ...following to #{ res . headers [ 'Location' ] } " )
124
+ uri = res . headers [ 'Location' ] . sub ( /.*?#{ ip } / , "" )
115
125
res = send_request_cgi ( {
116
126
'uri' => "#{ uri } " ,
117
- 'method' => 'GET' ,
127
+ 'method' => 'GET'
118
128
} )
119
129
120
130
if res . code == 200
@@ -129,31 +139,37 @@ def get_blog_posts(xml_rpc, ip)
129
139
end
130
140
131
141
# parse out links and place in array
142
+ if res . nil? or res . code != 200
143
+ return blog_posts
144
+ end
145
+
132
146
links = res . to_s . scan ( /<link>([^<]+)<\/ link>/i )
133
147
134
- if res . code != 200 or links . nil? or links . empty?
148
+ if links . nil? or links . empty?
135
149
return blog_posts
136
150
end
137
151
152
+
138
153
links . each do |link |
139
154
blog_post = link [ 0 ]
140
- pingback_request = get_pingback_request ( xml_rpc , 'http://127.0.0.1' , blog_post )
141
-
142
- pingback_disabled_match = pingback_request . body . match ( /<value><int>33<\/ int><\/ value>/i )
143
- if pingback_request . code == 200 and pingback_disabled_match . nil?
144
- print_good ( "Pingback enabled: #{ link . join } " )
145
- blog_posts = link . join
146
- return blog_posts
147
- else
148
- vprint_status ( "Pingback disabled: #{ link . join } " )
155
+ pingback_response = get_pingback_request ( xml_rpc , 'http://127.0.0.1' , blog_post )
156
+ if pingback_response
157
+ pingback_disabled_match = pingback_response . body . match ( /<value><int>33<\/ int><\/ value>/i )
158
+ if pingback_response . code == 200 and pingback_disabled_match . nil?
159
+ print_good ( "Pingback enabled: #{ link . join } " )
160
+ blog_posts = link . join
161
+ return blog_posts
162
+ else
163
+ vprint_status ( "Pingback disabled: #{ link . join } " )
164
+ end
149
165
end
150
166
end
151
167
return blog_posts
152
168
end
153
169
154
170
# method to send xml-rpc requests
155
171
def get_pingback_request ( xml_rpc , target , blog_post )
156
- uri = xml_rpc . sub ( /.*?#{ ip } / , "" )
172
+ uri = xml_rpc . sub ( /.*?#{ target } / , "" )
157
173
# create xml pingback request
158
174
pingback_xml = generate_pingback_xml ( target , blog_post )
159
175
@@ -162,7 +178,7 @@ def get_pingback_request(xml_rpc, target, blog_post)
162
178
res = send_request_cgi ( {
163
179
'uri' => "#{ uri } " ,
164
180
'method' => 'POST' ,
165
- 'data' => "#{ pingback_xml } " ,
181
+ 'data' => "#{ pingback_xml } "
166
182
} )
167
183
rescue ::Rex ::ConnectionRefused , ::Rex ::HostUnreachable , ::Rex ::ConnectionTimeout
168
184
print_error ( "Unable to connect to #{ uri } " )
@@ -177,9 +193,12 @@ def get_pingback_request(xml_rpc, target, blog_post)
177
193
# Save data to vuln table
178
194
def store_vuln ( ip , blog )
179
195
report_vuln (
180
- :host => ip ,
181
- :name => self . name ,
182
- :info => "Module #{ self . fullname } found pingback at #{ blog } "
196
+ :host => ip ,
197
+ :proto => 'tcp' ,
198
+ :port => datastore [ 'RPORT' ] ,
199
+ :name => self . name ,
200
+ :info => "Module #{ self . fullname } found pingback at #{ blog } " ,
201
+ :sname => datastore [ 'SSL' ] ? "https" : "http"
183
202
)
184
203
end
185
204
0 commit comments