-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathvulnerable.module
executable file
·474 lines (412 loc) · 18.6 KB
/
vulnerable.module
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
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
<?php
/**
* @file vulnerable.module
*
* This is a module you should only install on a completely isolated test site
* to understand how vulnerabilities work and how to fix them.
*
* +++++ DO NOT INSTALL THIS ON A PUBLICLY ACCESSIBLE SITE +++++
*
*/
/**
* Implements hook_menu().
*/
function vulnerable_menu() {
$items = array();
$items['vulnerable'] = array(
'title' => 'Vulnerable module overview',
'access arguments' => array('access content'),
'page callback' => 'vulnerable_overview',
'type' => MENU_NORMAL_ITEM,
);
$items['vulnerable/insufficient-authentication'] = array(
'title' => 'Insufficient Authentication',
'access arguments' => array('access content'),
'page callback' => 'vulnerable_insufficient_authentication',
'page arguments' => array(2),
'type' => MENU_CALLBACK,
);
$items['vulnerable/log-in-sql-injection'] = array(
'title' => '',
'access arguments' => array('access content'),
'page callback' => 'vulnerable_log_in_injection',
'page arguments' => array(2, 3),
'type' => MENU_CALLBACK,
);
$items['vulnerable/show-me-the-data'] = array(
'title' => 'Here is some data about our users',
'access arguments' => array('access content'),
'page callback' => 'vulnerable_show_me_the_data',
'page arguments' => array(2),
'type' => MENU_CALLBACK,
);
$items['vulnerable/csrf-disable'] = array(
'title' => 'Disable users in a csrf, xss, sql injection kind of way',
'access arguments' => array('access content'),
'page callback' => 'vulnerable_account_disable',
'page arguments' => array(2),
'type' => MENU_CALLBACK,
);
$items['vulnerable/user-form-data'] = array(
'title' => 'Show user supplied data as form options.',
'access arguments' => array('access content'),
'page callback' => 'drupal_get_form',
'page arguments' => array('vulnerable_user_fapi_form'),
'type' => MENU_CALLBACK,
);
$items['vulnerable/node-list'] = array(
'title' => 'Show nodes in a list.',
'access arguments' => array('access content'),
'page callback' => 'vulnerable_node_list',
'type' => MENU_CALLBACK,
);
$items['vulnerable/cookie-monster'] = array(
'title' => 'NOM NOM NOM - Eat those cookies.',
'access arguments' => array('access content'),
'page callback' => 'vulnerable_steal_those_cookies',
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Page callback to give an overview of the module's features.
*/
function vulnerable_overview() {
$items[] = l('Insufficient Authentication', 'vulnerable/insufficient-authentication') . ' ' . t('Allows users to login to any account simply by supplying a UID to the URL');
$items[] = l('Login SQL Injection', 'vulnerable/log-in-sql-injection') . ' ' . t('Another example of sql injection and using GET instead of POST');
$items[] = l('Here is some data about our users', 'vulnerable/show-me-the-data') . ' ' . t('An example of authorization bypass, sql injection, and xss.');
$items[] = l('Disable users in a csrf, xss, sql injection kind of way', 'vulnerable/csrf-disable') . ' ' . t('Allows any user to disable an account with some CSRF and sql injection.');
$items[] = l('Show user supplied data as form options.', 'vulnerable/user-form-data') . ' ' . t('Outputs some data from different form elements to show which are vulnerable and which are not.');
$items[] = l('Eat those cookies (NOM NOM NOM).', 'vulnerable/cookie-monster') . ' ' . t('A cookie logger functionality which requires an XSS to exploit.');
$items[] = l('Show all nodes to everyone', 'vulnerable/node-list') . ' ' . t('A list of nodes that disobeys node access rules and also allows XSS from node titles/bodies.');
return theme('item_list', array('items' => $items));
}
/**
* An example of insufficient authentication.
* Exploit this by visiting example.com/vulnerable/insufficient-authentication/1
* and you will become the uid 1 user without any password.
* If an account has been blocked they could also use this to login since it doesn't check whether they are active.
*/
function vulnerable_insufficient_authentication($uid) {
global $user;
// This should, at the minimum, also be checking a username and password.
$account = user_load($uid);
if (!empty($account->name)) {
// In Drupal 6 it was sufficient to just set the global $user to an account and then they were logged in.
$user = $account;
// In Drupal 7 this mistake is a little harder to make, you would have to call user_login_finalize to complete.
user_login_finalize($user);
// In Drupal 7 to elevate permissions/change accounts would require further mistakes (probably closing original session and opening a new one).
// Separately, if someone gets javascript into their name this would be an XSS vulnerability.
drupal_set_message('You are now logged in as ' . $user->name);
}
return t('Hey, this is pretty vulnerable.');
}
/**
* Another example of sql injection and using GET instead of POST
* To exploit sql injection: example.com/vulnerable/log-in-sql-injection/a/a') OR (uid = 1 AND 1 = 1) OR (1 = 'a
* If the first login doesn't work, be sure to visit the url twice (maybe this is a problem if you are using Pressflow)
* The problem with the GET is that your password is in plain text in places like the Apache logs and the browser history
* If an account has been blocked they could also use this to login since it doesn't check whether they are active.
*/
function vulnerable_log_in_injection($name, $password) {
// More SQL injection.
// The password hashing system makes the original functionality nearly impossible, but this example does show how
// leaving "dead code" in your site/module can lead to a vulnerability.
// Debugging messages left in code. Happens surprisingly often. Super helpful for hackers.
drupal_set_message("SELECT uid FROM {users} WHERE ( name = '$name' AND pass = '$password')");
$uid = db_query("SELECT uid FROM {users} WHERE (name = '$name' AND pass = '$password')")->fetchField();
if (!empty($uid)) {
global $user;
$user = user_load($uid);
user_login_finalize($user);
}
return t('Hey, this is pretty vulnerable.');
}
if (!function_exists('dsm')) {
function dsm($v) {
drupal_set_message(var_export($v, true));
}
}
/**
* An example of authorization bypass, sql injection, and xss.
*
* To exploit authorization bypass, enter a character in a username that is "blocked" and would therefore only be visible to admins.
* To exploit xss (opera only): example.com/vulnerable/show-me-the-data/<IMG SRC=javascript:alert('XSS')>
* To exploit sql injection: example.com/vulnerable/show-me-the-data/' UNION SELECT uid, pass, init FROM users where 1=1 OR 1 ='
*/
function vulnerable_show_me_the_data($user_search) {
// Using PASS_THROUGH to allow html through. Also lets through xss for some browsers :(
drupal_set_title('Searching for <em>' . $user_search .'</em>', PASS_THROUGH);
if (empty($user_search)) {
$output = 'Please add some characters from a username onto the end of this URL to search the users.';
}
else {
// Here is a SQL injection - could be used to get data and metadata from the database.
// Should use %d, %s style placeholders for the variable.
$results = db_query("SELECT uid, name, mail FROM {users} WHERE name LIKE '%%$user_search%%'");
// Here is some XSS, should use the t() function with @ or %
$output = 'Information about users with ' . $user_search . ' in their name<br>.';
foreach ($results as $result) {
// More XSS - should use t() and % or @
$output .= "UID: $result->uid Name: $result->name Mail: $result->mail <br>";
}
}
return $output;
}
/**
* Access bypass.
* To exploit access bypass: Just unpublish a node, install a node access module to hide stuff, and visit vulnerable/node-list
* To exploit XSS: create a node with XSS in the title or body.
* To exploit sql injection: /vulnerable/node-list?order_field=nid;update users set mail = '[email protected]' where uid = 1;
*/
function vulnerable_node_list() {
if ($nid = arg(2)) {
$node = node_load($nid);
if ($node) {
drupal_set_message($node->title);
}
}
$query = db_select('node', 'n')
->fields('n', array('nid', 'title'));
$order = isset($_GET['order']) ? $_GET['order'] : 'DESC';
$order_field = isset($_GET['order_field']) ? $_GET['order_field'] : 'n.nid';
$query->orderBy($order_field, $order);
$results = $query->execute();
foreach ($results as $result) {
$node = node_load($result->nid);
$body = $node->body[LANGUAGE_NONE][0]['value'];
$items[] = l($result->nid, 'node/' . $result->nid) . ' :: ' . $result->title . ' :: ' . $body;
}
return theme('item_list', array('items' => $items));
}
/**
* An example of a CSRF, SQL injection, xss, authorization bypass
*
* To exploit CSRF: create an image like <img src="http://example.com/vulnerable/csrf-disable/1"> and have an admin look at that page
* SQL Injection: visit example.com/vulnerable/csrf-disable/2 OR 1 = 1
* To exploit XSS: example.com/vulnerable/csrf-disable/5<IMG SRC=javascript:alert('XSS')>
* Authorization bypass: This URL is only protected by "access content" permissions so it is probably available to any anonymous user allow them to disable all users on the site
*/
function vulnerable_account_disable($uid) {
db_query("UPDATE {users} SET status = 0 WHERE uid = $uid");
drupal_set_message('Access disabled for uid ' . $uid);
return t('Hey, this is pretty vulnerable.');
}
/**
* Page callback for a cookie stealer. Logs requests with a cookie in the URL.
* Requires XSS to exploit.
*/
function vulnerable_steal_those_cookies() {
if (!empty($_GET['c'])) {
watchdog('vulnerable', $_GET['c'], array(), WATCHDOG_ERROR);
return '<a href="http://icanhascheezburger.com/2007/01/15/day-i-got-cookie/"><img alt="day i got cookie" src="http://icanhascheezburger.files.wordpress.com/2007/01/2000067174645735152_rs.jpg" /></a><br />more <a href="http://icanhascheezburger.com">animals</a>';
}
else {
return '<a href="http://icanhascheezburger.com/2007/12/07/and-you-eated-it/"><img src="http://icanhascheezburger.wordpress.com/files/2007/12/funny-pictures-maded-me-a-cookie.jpg" alt="funny pictures" /></a><br />more <a href="http://icanhascheezburger.com">animals</a>';
}
}
/**
* Implements hook_init().
* Warn people they really shouldn't be running this module.
*/
function vulnerable_init() {
if (!drupal_is_cli()) {
drupal_set_message(t('This site is running the "Vulnerable" module which should <strong>never</strong> be enabled on a <em>public</em> website. If this is a public website, you should probably <a href="!uri">disable it.</a>', array('!uri' => url('admin/modules'))), 'error');
}
}
/**
* Attempts to help test, demonstrate and debug database api weaknesses.
*
* NOTE: these examples assume you are on PHP before 5.5.21 / 5.6.5 o r have
* reversed the patch in https://www.drupal.org/node/2388255
*
* To use, do something like `drush -v ev "vulnerable_dbtng_weaknesses();"`
*
* It's well known that db_query() is weak to sqli with concatenating user-
* supplied content. However, what about DBTNG?
*
* After running this, you can expect to see 13 records in the vulnerable_demo
* database table. It should be fairly straightforward to imagine a scenario
* where a developer might allow user supplied strings to enter into these
* fields. The goal of this function is twofold:
* 1. It shows developers and reviewers which dbtng parameters they should
* give particular attention when writing code or doing code reviews.
* 2. It might let us think of more ways to harden the database API.
*
* Please also consider the frequency with which methods are used:
* https://gist.github.com/greggles/df1faa38ab27798e8e38
*
* @see vulnerable_node_list().
*
*/
function vulnerable_dbtng_weaknesses() {
// db_insert->fields(['dangerous', 'dangerous']);
$query = db_insert('variable')
->fields(["name", "value) values (3, 4);insert into vulnerable_demo values ('fields key1');"])
->values([
"name" => "vulnerable_sqli_test2",
"value" => serialize('yep'),
]);
$query->execute();
variable_del(3);
// db_insert->fields([dangerous => safe])
$query = db_insert('variable')
->fields(array(
"value" => serialize('yep'),
"name) values (1, 2);insert into vulnerable_demo values ('fields key2');" => "vulnerable_sqli_test",
));
$query->execute();
variable_del(2);
// db_insert->values([safe => safe]); * when combined with safe ->fields
// i.e. the "degenerate" form provides security if you must put user
// supplied data into the keys of the values.
// $query = db_insert('variable')
// ->fields(["name", "value"])
// ->values([
// "value" => serialize('yep'),
// "name) values (1, 2);insert into vulnerable_demo values ('fields key3');" => "vulnerable_sqli_test2",
// ]);
// $results = $query->execute();
// db_update->fields([dangerous => safe])
$query = db_update('variable')
->fields(array(
"value = 'some value' where 1 = 2;insert into vulnerable_demo values ('fields key update');" => "some value' where 1= 2;insert into vulnerable_demo values ('fields value');",
))
->condition('name', 'llamas in pajamas');
$query->execute();
// db_delete->condition(safe, safe, dangerous)
// This is the same as select and probably everywhere, just confirming it is.
$query = db_delete('variable')
->condition("name", "vulnerable_sqli_test", "= 1);insert into vulnerable_demo values ('delete condition 3rd');");
$query->execute();
// db_select(safe, safe)
$query = db_select("node", "n")
->fields("n", array("uid"));
$query->execute();
// ->fields(safe, safe)
$query = db_select("node", "n")
->fields("n", array("uid"));
$query->execute();
// ->condition(safe, safe, dangerous)
$query = db_select('node', 'n')
->fields('n', array('nid', 'title'))
->condition("nid", "1", "= 1); insert into vulnerable_demo values ('condition operator');");
$query->execute();
// ->where(dangerous)
$query = db_select('node', 'n')
->fields('n', array('nid', 'title'))
->where("nid = 1);insert into vulnerable_demo values ('where');");
$query->execute();
// ->addTag(safe)
$query = db_select("node", "n");
$query->addField("n", "uid", "n_uid");
$query->addTag("foo");
$query->execute();
// addField(safe, safe, safe)
$query = db_select("node", "n");
$query->addField("n", "uid", "n_uid");
$query->execute();
// ->range(safe, safe)
$query = db_select('node', 'n')
->fields('n', array('nid', 'title'))
->range("10;insert into vulnerable_demo values ('range1');", "20;insert into vulnerable_demo values ('range1');");
$query->execute();
// ->join(safe, safe, dangerous, safe array of args)
// ->innerJoin(safe, safe, dangerous, safe array of args)
// ->leftJoin(safe, safe, dangerous, safe array of args)
// ->rightJoin(safe, safe, dangerous, safe array of args)
$query = db_select("node", "n")
->fields("n", array("uid"));
$query->join("users", "u", "n.uid = u.uid AND n.uid = :uid;insert into vulnerable_demo values ('joincondition');", [':uid' => "1"]);
$query->execute();
// ->addJoin(dangerous, safe, safe, dangerous, safe array of parameters)
$query = db_select("node", "n");
$query->addField("n", "uid", "n_uid");
$query->addJoin("INNER JOIN users u on n.uid = u.uid;insert into vulnerable_demo values ('addJoin type');", "users", "u", "n.uid = u.uid AND n.uid = :uid;insert into vulnerable_demo values ('inner join condition');", [':uid' => ';']);
$query->execute();
// ->addExpression(dangerous, safe)
$query = db_select('node', 'n')
->fields('n', array('uid'));
$query->addExpression("count(uid) from node n group by n.uid;insert into vulnerable_demo values ('expression first param');", "uid_node_count");
$query->execute();
// ->isNotNull(safe)
$query = db_select("node", "n");
$query->addField("n", "uid", "n_uid");
$query->isNotNull("n.uid");
$query->range(1,2);
$query->execute();
// ->havingCondition(safe, safe, dangerous);
$query = db_select('node', 'n')
->fields('n', array('uid'));
$query->addExpression('count(uid)', 'uid_node_count');
$query->groupBy("n.uid");
$query->havingCondition("uid_node_count", "2", ">= 2);insert into vulnerable_demo values ('havingCondition condition');");
$query->execute();
// ->having(dangerous, safe parameter array)
$query = db_select('node', 'n')
->fields('n', array('uid'));
$query->addExpression('count(uid)', 'uid_node_count');
$query->groupBy("n.uid");
$query->having("COUNT(uid) >= 2);insert into vulnerable_demo values ('having');");
$query->execute();
// ->groupBy(dangerous);
$query = db_select('node', 'n')
->fields('n', array('uid'));
$query->addExpression('count(uid)', 'uid_node_count');
$query->groupBy("n.uid;insert into vulnerable_demo values ('groupby');");
$query->execute();
// See https://www.drupal.org/node/829464 - used to be both were dangerous.
// ->orderBy(dangerous, safe);
$query = db_select('node', 'n')
->fields('n', array('nid', 'title'));
$query->orderBy("nid; insert into vulnerable_demo values ('order_field');", 'DESC');
$query->execute();
// ->addMetaData(safe, safe)
$mal_object = new StdClass();
$mal_object->inject = ";insert into vulnerable_demo values ('addMetadataObject');";
$query = db_select("node", "n");
$query->addField("n", "uid", "n_uid");
$query->addMetaData(";insert into vulnerable_demo values ('addMetadata key');", $mal_object);
$query->execute();
}
/**
* A form that shows some XSS opportunities.
*/
function vulnerable_user_fapi_form($form, &$form_state) {
$form = array();
// For convenience a little XSS data. In reality this could come from a title, url, term,etc.
$user_data = "<script>alert('xss')</script>";
$user_data2 = "\"><script>alert('xss')</script><a href=\"";
$form['vulnerable_markup'] = array(
'#value' => '<a href="' . $user_data2 . '">' . $user_data . '</a>',
);
// Does not present a weakness - data is sanitized.
$form['vulnerable_select'] = array(
'#type' => 'select',
'#title' => 'A select',
'#options' => array($user_data => $user_data),
);
// These three are weaknesses:
$form['vulnerable_checkbox'] = array(
'#type' => 'checkbox',
'#title' => 'Unsafe checkbox' . $user_data,
);
$form['vulnerable_checkboxes'] = array(
'#type' => 'checkboxes',
'#title' => 'Some checkboxes',
'#options' => array(
'safe' => t('Safe'),
$user_data => $user_data,
),
);
$form['vulnerable_radios'] = array(
'#type' => 'radios',
'#title' => 'Some checkboxes',
'#options' => array(
'safe' => t('Safe'),
$user_data => $user_data,
),
);
return $form;
}