Reputation: 384
EDIT: after reading all the input from the other users, i decided, to use what @chris suggested call_user_func_array() one more reason not to use eval() its slower than call_user_func_array(), but so far, nobody was able to exploit it my way, if you find a way, please post it as answer or comment :). So everybody can learn from it. Merry XMAS to all!
---EDIT END---
Ok i needed to make a dynamic code:
I get user input like $_POST['a'], $_POST['b']; // Depends on each query how many user input.
$sql = "SELECT 1, 2, 3 FROM x WHERE b = ? AND a = ? LIMIT 10"; // SQL STATEMENT
$input = array($_POST['a'], $_POST['b']);
$output = 3; // Number of variables need for 1, 2, 3
$data = readDB2($sql, $input, $output);
var_dump($data);
this input, gets passed to mysqli->prepared statements
cause the number of variables is dynamic ($input and $output);
i used the php function eval(); Now my question can this be exploited, in my code?
Just look in my function readDB2 to see how i used the eval() function (used it 3x times).
public function readDB2($sql, $input, $output1) {
$stmt = $this->mysqli->prepare($sql);
if(!empty($input) && is_array($input)) {
$sp = "";
$data = "";
$inputn = count($input) - 1;
for($i = 0; $i <= $inputn; $i++) {
if($i !== $inputn) {
$data .= '$input[' . $i . "],";
} else {
$data .= '$input[' . $i . "]";
}
$sp .= "s";
}
$bind = '$stmt->bind_param(\''. $sp . '\',' . $data . ');';
eval("return $bind");
}
if (!$stmt) {throw new Exception($this->mysqli->error);}
$stmt->execute();
if (!$stmt) {throw new Exception($this->mysqli->error);}
$stmt->store_result();
$checker = $stmt->num_rows;
if($checker !== 0) {
if(!empty($output1)) {
$out = "";
for($i = 1; $i <= $output1; $i++) {
if($i !== $output1) {
$out .= '$out' . $i . ",";
} else {
$out .= '$out' . $i;
}
}
$res = '$stmt->bind_result(' . $out . ');';
eval("return $res");
$vars = "array(" . $out . ");";
while ($stmt->fetch()) {
$results[] = eval("return $vars");
}
}
} else {
$results = "NO RESULTS";
}
$stmt->fetch();
$stmt->close();
$this->results = array('num_rows' => $checker, $results);
return $this->results;
}
EDIT FOR meagar
$bind = '$stmt->bind_param(\''. $sp . '\',' . $data . ');';
==
$bind = '$stmt->bind_param('ss', $input[0], $input[1]););
OR and so on
$bind = '$stmt->bind_param('sss', $input[0], $input[1], $input[2]););
EDIT FOR Incognito:
$input = array($_POST['pwnd']);
$data = readDB2($sql, $input, $output) {
public function readDB2($sql, $input, $output) {
...
$inputn = count($input) - 1;
for($i = 0; $i <= $inputn; $i++) {
if($i !== $inputn) {
$data .= '$input[' . $i . "],";
} else {
$data .= '$input[' . $i . "]";
}
$sp .= "s";
}
$bind = '$stmt->bind_param(\''. $sp . '\',' . $data . ');';
eval("return $bind");
...
}
in my result
$bind = '$stmt->bind_param(\''. $sp . '\',' . $data . ');';
gets
eval("return $bind");
gets
$stmt->bind_param('s', $input[0]);
not what you said.
Upvotes: 0
Views: 631
Reputation: 31823
fyi, call_user_func_array() is how you call functions with unknown number of arguments.
array_unshift($input, str_repeat('s', count($input)));
$callable = array($stmt, 'bind_param');
call_user_func_array($callable, $input);
array_unshift() pushes the 'sss' string element to the front of the array(we want the front because it needs to be the first argument fed to bind_param)
$callable is the callback psuedo type
also, in the future, if you find yourself using eval, familiarize yourself with php's var_export() function which can assist you with constructing safe strings. Try not to use eval though.
Upvotes: 2
Reputation: 239382
You're evaluating user-submitted data, effectively allowing attackers to execute arbitrary code. This is the absolute worst security hole your application can have, bar none. I mean that. Your programs has literally the worse vulnerability a program can have.
You're passing in $_POST['a']
as the $input
parameter. The $input
parameter gets treated like an array, and it's individual elements get appended to a string which is evaluated. If somebody posts executable code to your application, you could inadvertently run it.
I won't go so far as to actually post a working exploit, but suppose $_POST['a']
contained a single element, which had the string '); rmdir("/etc"); //
.
This line:
$bind = '$stmt->bind_param(\''. $sp . '\',' . $data . ');';
turns into something like this:
$stmt->bind_param(''); rmdir("/etc"); //);';
That is, the intent of your original statement is nullified, and instead the user has caused you to remove your /etc
directory. Again, this is probably not a working example, but this is the sort of attack you're opening yourself up to by trusting user-submitted data enough to pass it to eval
.
Upvotes: 4