In my previous post regarding PHP session compromise, I demonstrated how a poorly configured session ID can be brute forced by an attacker. In this post we shall look at how poor coding practices can also lead to session compromise without discovering a protected session ID. This attack will be demonstrated against level 20 of the Natas hacking game.
Upon loading this level, we are presented with an input form that allows us to change the name of the current user:
Examining the session ID reveals that it is 26 characters long and well randomized, brute forcing it would take a very long time:
Thus, we next turn to the source code for possible entry points. It is immediately revealed that the script uses the PHP function session_set_save_handler() to define custom functions that store and manage session data:
A closer look at these functions reveals that mywrite() and myread() are used to store and retrieve session data from a custom file:
Further analysis of myread() reveals the first signs of an attack surface. While the purpose of this function is primarily to read data from the session file to the debug output, it also writes this data to the built in $_SESSION array:
The above code determines how these two operations are handled. This code makes use of the PHP function explode() which creates an array of strings based on a dividing character. The session file contents are first read into a the variable $data, then the array $line is created with each value from $data as separated by a newline character. Each value in $line divided by a space is then sorted into another array $parts. Finally, the values of $parts are written to the $_SESSION array.
We now have enough information to formulate an attack hypothesis. If we are able to write the string “admin 1” on a separate line within the session file, then this value will be written to the $_SESSION array by invoking myread(). However, in order to implement this attack we must first bypass a restriction imposed by the following lines of code:
The script only accepts a single line of input from the form as the value of “name”. This can be bypassed by supplying the newline character with URL encoding: %0a. Thus, our input will be: “alphanine%0aadmin 1” and is injected using Burp Suite to avoid double encoding:
We now have written “alphanine” and “admin 1” on separate lines in the session file. Submitting the form again will call myread() to create the admin key in the $_SESSION array and reveal the password: