-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathwordpress.bro
More file actions
308 lines (248 loc) · 7.63 KB
/
wordpress.bro
File metadata and controls
308 lines (248 loc) · 7.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
##
# Test policy to identify wordpress servers by the daily communication back
# to the mother ship...
#
# original source:
# https://raw.githubusercontent.com/set-element/misc-scripts/master/wordpress.bro
@load base/frameworks/software
module WP_PARSE;
export {
redef enum Log::ID += { LOG };
redef enum Software::Type += {
## Identifier for web applications in the software framework.
WEB_WORDPRESS_APP, # support app like MySQL
WEB_WORDPRESS_CORE, # core wordpress version
WEB_WORDPRESS_PLUGIN, # plugin info
};
redef enum Notice::Type += {
WP_Value,
};
# List of hosts that are assigned to the name 'api.wordpress.org'
global wp_api_hosts = [ 66.155.40.203, 66.155.40.249, 66.155.40.250, 66.155.40.202 ] &redef;
# HTTP header which holds the site name and agent
const ua_header = /USER-AGENT/ &redef;
# Used to snip out the desired values from the ascii goo thrown back and forth.
const wp_plugin_name = /s\:4\:\"Name\"/;
const wp_plugin_version = /s\:7:\"Version\"/;
# there are two types of this - a short lived and a long lived version
# short exists while the http_request and http_header are being procesed
# where the data is heald in wp_conns
#
# longer term is indexed by the site name and holds long term data
#
type wp_ent: record {
cid: conn_id;
uid: string &default="UID" &log;
name: string &default="NAME" &log;
wp_version: string &default="WPVERSION" &log;
php_version: string &default="PHPVERSION" &log;
sql_version: string &default="SQLVERSION" &log;
blog_count: string &default="0" &log;
multisite: string &default="0" &log;
users: string &default="0" &log;
data: string &default="";
plugin: string &default="NULL" &log;
plugin_ver: string &default="NULL" &log;
};
global t_wp: table[conn_id] of wp_ent;
}
function get_version(rawv: string) : Software::Version
{
local sv: Software::Version;
local ver = split_string(rawv, /\./);
if ( |ver| > 1 ) {
if ( |ver| > 3 )
sv$minor3 = to_count(ver[3]);
if ( |ver| > 2 )
sv$minor2 = to_count(ver[2]);
sv$minor = to_count(ver[1]);
sv$major = to_count(ver[0]);
}
return sv;
}
function software_log(w: wp_ent)
{
# This will get called in the event that we are reporting the wordpress core and when
# looking at plugins
local ts = network_time();
if ( w$plugin != "NULL" ) {
# This is a plugin report
local si: Software::Info;
si$ts = ts;
si$host = w$cid$orig_h;
si$host_p = w$cid$orig_p;
si$software_type = WEB_WORDPRESS_PLUGIN;
si$name = w$plugin;
si$version = get_version(w$plugin_ver);
si$unparsed_version = w$plugin_ver;
Software::found(w$cid, si);
}
else {
# kinda unclean ...
local si1: Software::Info;
si1$ts = ts;
si1$host = w$cid$orig_h;
si1$host_p = w$cid$orig_p;
si1$software_type = WEB_WORDPRESS_CORE;
si1$name = "Wordpress";
si1$version = get_version(w$wp_version);
si1$unparsed_version = w$wp_version;
Software::found(w$cid, si1);
local si2: Software::Info;
si2$ts = ts;
si2$host = w$cid$orig_h;
si2$host_p = w$cid$orig_p;
si2$software_type = WEB_WORDPRESS_APP;
si2$name = "WP_PHP";
si2$version = get_version(w$php_version);
si2$unparsed_version = w$php_version;
Software::found(w$cid, si2);
local si3: Software::Info;
si3$ts = ts;
si3$host = w$cid$orig_h;
si3$host_p = w$cid$orig_p;
si3$software_type = WEB_WORDPRESS_APP;
si3$name = "WP_MySQL";
si3$version = get_version(w$sql_version);
si3$unparsed_version = w$sql_version;
Software::found(w$cid, si3);
}
}
function clean_string(s: string) : string
{
# take a string that looks like /a:11:{s:4:"Name"/ and return /Name/
local t_s = split_string(s, /\"/);
return t_s[1];
}
event http_request(c: connection, method: string, original_URI: string, unescaped_URI: string, version: string) &priority=3
{
# There are two things we are lookig at:
# GET /core/version-check/1.6/?version=3.5.1&php=5.3.2&locale=en_US&mysql=5.1.52&local_package=&blogs=1&users=9&multisite_enabled=0 HTTP/1.0
# USER-AGENT WordPress/3.5.1; https://materialsproject.org/blog/
# and
# POST /plugins/update-check/1.0/ HTTP/1.0
if ( c$id$resp_h in wp_api_hosts ) {
if ( method == "GET" && /\/version-check\// in unescaped_URI )
{
# do some quick parsing
local twe: wp_ent;
local _uri = split_string(unescaped_URI, /\&|\?/);
if ( c$id !in t_wp ) {
#print fmt("register %s", c$id);
t_wp[c$id] = twe;
}
for (ui in _uri) {
local kv = split_string(_uri[ui], /=/);
if ( kv[0] == "version" ) {
twe$wp_version = kv[1];
#print fmt("VER: %s %s", kv[2], twe);
}
if ( kv[0] == "mysql" ) {
twe$sql_version = kv[1];
#print fmt("MSQ: %s %s", kv[2], twe);
}
if ( kv[0] == "php" ) {
twe$php_version = kv[1];
#print fmt("PHP: %s %s", kv[2], twe);
}
if ( kv[0] == "blogs" ) {
twe$blog_count = kv[1];
#print fmt("BLG: %s %s", kv[2], twe);
}
if ( kv[0] == "multisite_enabled" ) {
twe$multisite = kv[1];
#print fmt("MSE: %s %s", kv[2], twe);
}
if ( kv[0] == "users" ) {
twe$users = kv[1];
#print fmt("MSE: %s %s", kv[2], twe);
}
}
if ( twe$uid == "UID" )
twe$uid = c$uid;
twe$cid = c$id;
#Log::write(LOG, twe);
t_wp[c$id] = twe;
}
}
} # end http_request event
event http_header(c: connection, is_orig: bool, name: string, value: string) &priority=3
{
# The user-agent header value we are interested in has both the
# user agent (not useful) and the assigned site url (interesting!)
if ( (is_orig) && ( c$id$resp_h in wp_api_hosts ) )
{
if ( ua_header in name ) {
local domain = split_string(value, /\ /)[1];
local twe: wp_ent;
local si: Software::Info;
# Pull this stunt since there are a number of connections
# involved beyond the initial. The new twe struct is more
# in place to mark the data as interesting and will be indexed
# by the site name...
if ( c$id !in t_wp ) {
twe$name = domain;
twe$uid = c$uid;
twe$cid = c$id;
}
else {
twe = t_wp[c$id];
twe$name = domain;
Log::write(LOG, twe);
software_log(twe);
}
t_wp[c$id] = twe;
} # end header loop
}
} # end event
event http_entity_data(c: connection, is_orig: bool, length: count, data: string)
{
# If the connection is in the list of known collectors, track the
# data
if (is_orig && c$id in t_wp) {
local twe: wp_ent;
twe = t_wp[c$id];
# append new data on old
twe$data = fmt("%s%s", twe$data, data);
t_wp[c$id] = twe;
}
}
event http_end_entity(c: connection , is_orig: bool )
{
# There is probably a better way to go about doing this,
# but I will deal with that in the event that things get too
# complex or performance suffers.
if ( c$id in t_wp ){
local twe: wp_ent;
twe = t_wp[c$id];
local si: Software::Info;
local v_array = split_string( unescape_URI(twe$data), /;/);
delete t_wp[c$id];
local nret_value = "X";
local vret_value = "X";
for ( x in v_array ) {
local val = v_array[x];
if ( wp_plugin_name in val ) {
local nvalue = clean_string(v_array[x+1]);
nret_value = split_string1( nvalue, / /)[0];
twe$plugin = nret_value;
}
if ( wp_plugin_version in val ) {
local vvalue = clean_string(v_array[x+1]);
vret_value = split_string1( vvalue, / /)[0];
twe$plugin_ver = vret_value;
}
if ( (nret_value != "X") && (vret_value != "X")) {
#print fmt("%s %s", nret_value, vret_value);
Log::write(LOG, twe);
software_log(twe);
nret_value = "X";
vret_value = "X";
}
}
} # end if c$id in t_wp
}
event bro_init() &priority=5
{
Log::create_stream(WP_PARSE::LOG, [$columns=wp_ent]);
}