Notice the ".git/refs/heads/master" path? This indicates the live website was probably pushed as a git repo which is confirmed when we take a look at the logs in http://54.175.3.248:8089/.git/logs/HEAD:

Having obtained the source let's take a look at sign_up.php to see if we can learn something about the target username and password:

if ($_SERVER['REQUEST_METHOD'] ==='POST'){require_once'db.php';$link=mysql_connect($DB_HOST, $SQL_USER, $SQL_PASSWORD) ordie('Could not connect: '.mysql_error());mysql_select_db('users') ordie("Mysql error");$user=mysql_real_escape_string($_POST['username']);// check to see if the username is available$query="SELECT username FROM users WHERE username LIKE'$user';";$result=mysql_query($query) ordie('Query failed: '.mysql_error());$line=mysql_fetch_row($result, MYSQL_ASSOC);if ($line==NULL){// Signing up for premium is still in developmentecho'<h2 style="margin: 60px;">Lawn Care Simulator 2015 is currently in a private beta. Please check back later</h2>'; }else {echo'<h2 style="margin: 60px;">Username: '.$line['username'] ." is not available</h2>"; }}

We see mysql_real_escape_string is used in an attempt to mitigate SQL injection but since the SQL query is evaluated using LIKE (and the taken username is echoed back to us in full) we can simply see if there are any usernames with 'flag' in them by trying to sign up with the username %flag% which gives the error message:

We can see our hashed password (which is hashed client-side) first gets its length compared to the retrieved hash length and then gets evaluated on a byte-by-byte bases, returning false if a byte at a given position mismatches. At every index PHP's usleep is called sleeping for 300000 microseconds to thwart brute-force attacks.

This approach, however, is trivially vulnerable to a remote timingattack. Given an index i within our hash if we submitted the correct byte candidate during a try it will take noticeably longer than an incorrect candidate since a correct candidate will make the routine sleep and proceed to the next index while an incorrect candidate will terminate early. This allows us to iteratively determine the correct hash using the following timing attack script which takes the average response time (in milliseconds) of n = 3 requests for a given byte candidate at a given position and determines whether it is a) greater than or equal to the cumulative delay (which is 300 milliseconds times the current position) and b) greater than the longest average response time so far.