We are presented with a PHP web application that has a custom database implementation DB.
Looking at register, we spot a bug in the way code is being processed.
//Register.php// Checks if the code is a string and is setif(isset($_REQUEST['username'])&&isset($_REQUEST['password'])&&isset($_REQUEST['code'])&&is_string($_REQUEST['code'])){if(is_valid_invite($_POST['code'])){$username=$_POST['username'];if(get_user_by_name($username)){show_error('Username already taken.');}else{add_tmp_perms(perms:'users_add');$user=create_user($username,$_POST['password']);rm_tmp_perms('users_add');if($user){show_success('Account created successfully.');header('Location: login.php');}else{show_error('Failed to create user.');}}}else{show_error('Invalid invite code.');}}// Invites.phpfunctionis_valid_invite($code){global$db;$res=$db->select(['SELECT'=>'*','FROM'=>'invites','WHERE'=>['code'=>$code],]);returncount($res)===1;}
Reading PHP's documentation, it states the following
This means we can set two code values, one in Cookie to satisfy the is_string check and one in $_POST data which is seen in the debugger.
We can use a payload like the following to then create a valid user. Since there is only one invite code from install.php, this returns one row, satisfying the
Now, we know that a admin user is created at the start via install.php. Looking at what our user can do, we see a interesting delete function in settings.php
Flow of the function
Check perms (Only for admin it seems)
if present - delete any user via uid
Else If session uid === delete uid (strict comparison)
add temporary perms add_tmp_perms
delete user
show success message
remove temporary perms rm_tmp_perms
Something interesting is that we can directly pass the msg parameter into show_success which expects a string type.
This can cause a crash, allowing us to retain our delete_user privileges, assuming we do not follow the redirect to logout.php
We can thus formulate the exploit
Create two sessions, A and B
Use session A to do the following
pass in a errorneous type into message, giving us delete_user privileges
delete admin user by specifying uid=1
Create a new user that will take the uid=2 using the bug above
Run install.php to create the new admin user as uid=2
Use session B which still retains the uid of 2 to view the flag