A Blog about Thijs de Vries

A blog

Php Import_request_variables Exploit Example

I previously ran across some code that used import_request_variables to set all post, get and cookie parameters to global variable. This saved time since you would not need to explicitly call:

1
2
3
if(isset($_GET['some_param']) and $_GET['some_param']){
  $some_var = $_GET['some_param'];
}

It was called using:

1
import_request_variables('GPC','_') ;

This code was called on every page of the site. The first parameter ‘GPC’ tells import_request_variables to import get, post and cookie parameters. The second parameter ‘_’ tells import_request_variables to add a underscore to the variable name. For example, passing $_GET[‘foo’] will create a variable called $_foo. If the second parameter is passed, there will be a E_NOTICE warning since this would allow any user to create or modify any variable. In this code, the underscore was provided as a prefix but this does not mean we are in the clear security wise. Before I show the example, lets draw up some example code for index.php.

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
<?
#the following code is example code 
#and should not be used as an example
#of the "right" way of doing things. 
#There may even be syntax errors.

#starts the session, connects to db, 
#and calls import_request_variables
function begin(){
  session_start();
  mysql_connect('localhost',
     'mysql_user',
     'mysql_password');
  import_request_variables('GPC', '_');
}

#checks username and password with db
#and sets $_SESSION['user_id'] if they authenticate successfully. 
function login($username, $password){
  $username = mysql_real_escape_string($username);
  $password = mysql_real_escape_string($password);

  #as stated above, this code is only an example,
  #for a real site you would want a hash/salt, not plaintext
  #password.
  $query = "SELECT * FROM users WHERE username = 
     '$username' and password = '$password' LIMIT 1";
  $result = mysql_query($query);
  $user = mysql_fetch_assoc($result);
  if($user){
    $_SESSION['user_id'] = $user['user_id'];
  }
}

#destroys session
function logout(){
  session_destroy();
}

#will return true if $_SESSION['user_id'] is set and belongs to a user
function isLoggedIn(){
  if(isset($_SESSION['user_id'])){
    $user_id = $_SESSION['user_id'];
    $query = "SELECT * FROM users WHERE user_id = '$user_id' LIMIT 1";
    $result = mysql_query($query);
    $user = mysql_fetch_assoc($query);
    if($user){
      return true;
    }else{
      return false;
    }
  }else{
    return false;
  }
}

begin();

#note that variables starting with 
#$_ where set above with import_request_variables
if(isset($_logout)){
  logout();
}
if(isset($_username) && isset($_password)){
  login($_username, $_password);
}

if(isLoggedIn()){
  ?>
  You are an authorized user! <a href='index.php?logout=true'>Logout</a>.
  <?
}else{
  ?>
  You are not authorized, use the form below to log in.
   <form action='index.php'>
      Username: <input type='text' name='username' /><br />
      Password: <input type='password' name='password' />
   </form>
  <?
}
?>

This is a simple page which will tell you if you are authorized, or give the login form if not. If the user successfully logs in, the $_SESSION[‘user_id’] is set. $_username and $_password are set by import_request_variables if the username and password get, post or cookie variables are set. Now on to the attack. Any variable starting with $_ can be set by passing a get, post or cookie with the appropriate parameters. If a user sent the request http://www.example.com/?SESSION[user_id]=1 would overwrite the $_SESSION[‘user_id’] variable to 1. Since database ids are often sequential, it would not be that hard to use a script to run through each number and find ids that belong to users. It would essentially allow a hacker to log in as any user. Make sure if using the import_request_variables function, make sure to choose a prefix that will not be used by any other variables.

Comments