I have an existing application which used to use the deprecated mysql_* functions to perform database queries. I have since changed most database access (and all that has user input) to PDO, so I believe I am relatively safe from injection attacks. However, I was wondering how one would perform an injection attack on the previous code just so I can demonstrate how unsafe it is should the need arise.
I have a link in the format:
http://localhost/api/view.php?id=
Which is then passed, unsanitized, into the select
function below:
$db->select('invitations','Replied, Response, Registered',null,"Id = '".$id."'");
$res = $db->getResult();
I then do a var_dump()
to see the result.
I have tried something like:
http://localhost/api/view.php?id=<id>'
=>array(1) { [0]=> string(185) "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''<id>''' at line 1" }
http://localhost/api/view.php?id=<id>" or 1=1"
=>array(0) { }
But it doesn't seem like those types of queries would be successful. Ideally, I would want to be able to do something severe. I was hoping for something like '); DROP TABLE users;--
, but I cannot seem to make anything of the sort occur.
Here is the full select
function:
public function select($table, $rows = '*', $join = null, $where = null, $order = null, $limit = null){
// Create query from the variables passed to the function
$q = 'SELECT '.$rows.' FROM '.$table;
if($join != null){
$q .= ' JOIN '.$join;
}
if($where != null){
if (is_array($where)) {
$filter = $where;
$where = " 0 = 0 ";
$qs = "";
for ($i=0;$i<count($filter);$i++){
switch($filter[$i]['data']['type']){
case 'string' : $qs .= " AND ".$filter[$i]['field']." LIKE '%".$filter[$i]['data']['value']."%'"; Break;
case 'list' :
if (strstr($filter[$i]['data']['value'],',')){
$fi = explode(',',$filter[$i]['data']['value']);
for ($q=0;$q<count($fi);$q++){
$fi[$q] = "'".$fi[$q]."'";
}
$filter[$i]['data']['value'] = implode(',',$fi);
$qs .= " AND ".$filter[$i]['field']." IN (".$filter[$i]['data']['value'].")";
}else{
$qs .= " AND ".$filter[$i]['field']." = '".$filter[$i]['data']['value']."'";
}
Break;
case 'boolean' : $qs .= " AND ".$filter[$i]['field']." = ".($filter[$i]['data']['value']); Break;
case 'numeric' :
switch ($filter[$i]['data']['comparison']) {
case 'eq' : $qs .= " AND ".$filter[$i]['field']." = ".$filter[$i]['data']['value']; Break;
case 'lt' : $qs .= " AND ".$filter[$i]['field']." < ".$filter[$i]['data']['value']; Break;
case 'gt' : $qs .= " AND ".$filter[$i]['field']." > ".$filter[$i]['data']['value']; Break;
}
Break;
case 'date' :
switch ($filter[$i]['data']['comparison']) {
case 'eq' : $qs .= " AND ".$filter[$i]['field']." = '".date('Y-m-d',strtotime($filter[$i]['data']['value']))."'"; Break;
case 'lt' : $qs .= " AND ".$filter[$i]['field']." < '".date('Y-m-d',strtotime($filter[$i]['data']['value']))."'"; Break;
case 'gt' : $qs .= " AND ".$filter[$i]['field']." > '".date('Y-m-d',strtotime($filter[$i]['data']['value']))."'"; Break;
}
Break;
}
}
$where .= $qs;
}
$q .= ' WHERE '.$where;
}
if($order != null){
$q .= ' ORDER BY '.$order;
}
if($limit != null){
$q .= ' LIMIT '.$limit;
}
// Check to see if the table exists
if($this->tableExists($table)){
// The table exists, run the query
$query = @mysql_query($q);
if($query){
// If the query returns >= 1 assign the number of rows to numResults
$this->numResults = mysql_num_rows($query);
// Loop through the query results by the number of rows returned
for($i = 0; $i < $this->numResults; $i++){
$r = mysql_fetch_array($query);
$key = array_keys($r);
for($x = 0; $x < count($key); $x++){
// Sanitizes keys so only alphavalues are allowed
if(!is_int($key[$x])){
if(mysql_num_rows($query) > 1){
$this->result[$i][$key[$x]] = $r[$key[$x]];
}else if(mysql_num_rows($query) < 1){
$this->result = null;
}else{
$this->result[$key[$x]] = $r[$key[$x]];
}
}
}
}
return true; // Query was successful
}else{
array_push($this->result,mysql_error());
return false; // No rows where returned
}
}else{
return false; // Table does not exist
}
}
How would I be able to perform an SQL injection?
EDIT:
The value is received via browser using $_GET['id']
. There is, in fact, a second value which is not used in an SQL query but a switch statement. So, the full request looks like this:
http://localhost/api/view.php?rsvp=<status>&id=<id>
That may help.
EDIT 2:
It may have been important to mention that the id
s in question are in fact GUID
s, not numeric values. I ran all queries in the comments so far with a proper ID. Here are some results:
http://localhost/api/view.php?id=62FD23D8-B6C0-03F1-D45A-C9AC33C91774%27;%20drop%20table%20users;--
=>array(1) { [0]=> string(166) "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'drop table users;--'' at line 1" }
http://localhost/api/view.php?id=62FD23D8-B6C0-03F1-D45A-C9AC33C91774%27;select%20*%20from%20invitations;%27
=>array(1) { [0]=> string(179) "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select * from invitations;''' at line 1" }
These following lines are the entirety of the code that makes use of $id
:
// ...
if(isset($_GET['id'])) {
$id = $_GET['id'];
$db->select('invitations','Replied, Response, Registered',null,"Id = '".$id."'");
$res = $db->getResult();
// ...
}