0

In the past few days, I have created my own webserver to serve as my sandbox for learning pen-testing. I saw this blog (https://outpost24.com/blog/from-local-file-inclusion-to-remote-code-execution-part-1) and wanted to attempt something similar and build it on my webserver.

This is what I have done so far:

My index.php page here submits the input and parses the values to my action page: verify_email.php.

echo "
<span class='psw'><a href='#' onclick=\"document.getElementById('id04').style.display='block'\">Subscribe To Our Newsletter?</a></span>

<div id='id04' class='fpass-modal'>
<span onclick=\"document.getElementById('id04').style.display='none';\"
class='close-fpass' title='Close Modal'>&times;</span>

<!-- Modal Content -->
<form class='fpass-modal-content' action='php/verify_email.php' method='post'>
<div class='container4'>
<h1>Subscribe To Our Newsletter</h1>
<br>
<p>Join our subscribers list to get the latest news, updates, and special offers delivered directly in your inbox</p>
<input type='text' placeholder='Name' name='name' id='name' onfocus=\"this.value = ''\"
    required>
<input type='text' placeholder='Email' name='email' id='email' onfocus=\"this.value = ''\"
    required>
                <!-- Select language box -->
                <div class='custom-select' style='padding: 8px;'>
                    <select id='language' name='language'>
                        <option value='0'>Select language:</option>
                        <option value='english.php'>English</option>
                        <option value='german.php'>German</option>
                    </select>
                </div>

<a href='#' onclick=\"document.getElementById('id04').style.display='none';\" class='backtologin'>No, thank you</a>

<div class='clearfix'>
    <button type='submit' class='fpassbtn'>Subscribe</button>
</div>
</div>
</form>
</div>";

The action page then uses SendGrip to automatically send an email to the submitted email for the user to verify his/her email using the included link. (something like this:)

if ($language == "english.php") {
            $output='<p>Dear '.$name.',</p>';
            $output.='<p>Please click on the following link to verify your email.</p>';
            $output.='<p>-------------------------------------------------------------</p>';
            $output.='<p><a href="http://10.0.0.69/php/verified.php?key='.$verify_code.'&email='.$email.'&language='.$language.'&action=verify" target="_blank">
            http://10.0.0.69/php/verified.php?key='.$verify_code.'&email='.$email.'&language='.$language.'&action=verify</a></p>';
            $output.='<p>-------------------------------------------------------------</p>';
            $output.='<p>Please be sure to copy the entire link into your browser.
            The link will expire after 1 day for security reason.</p>';
            $output.='<p>If you did not request this newsletter subscription, no action 
            is needed, your account will not be subscribed to our newsletter.</p>';
            $output.='<p>For further enquiries, please reply to example@gmail.com</p>';
            $output.='<p>Thanks,</p>';
            $output.='<p>Example Team</p>';
            $body = $output;
            $subject = "Email Verification";
        }
        if ($language == "german.php") {
            $output='<p>Lieber '.$name.',</p>';
            $output.='<p>Bitte klicken Sie auf den folgenden Link, um Ihre E-Mail zu bestätigen.</p>';
            $output.='<p>-------------------------------------------------------------</p>';
            $output.='<p><a href="http://10.0.0.69/php/verified.php?key='.$verify_code.'&email='.$email.'&language='.$language.'&action=verify" target="_blank">
            http://10.0.0.69/php/verified.php?key='.$verify_code.'&email='.$email.'&language='.$language.'&action=verify</a></p>';
.
.
.

(sorry, I cannot expose the full codes for this part)

Based on this link, I included the language field which can be either english.php or german.php depending on what the user selected in the form previously. Based on this field, the browser will display a page of that language to the user.

<?php
include 'connect.php';
include 'validation.php';

session_start();
echo $_GET["key"];
echo $_GET["email"];
echo $_GET["language"];
echo $_GET["action"];

$error="";

$language = $_GET["language"];

include '/var/www/webdav/php/' . $language;
?>

My english.php page:

<?php
if (isset($_GET["key"]) && isset($_GET["email"]) && isset($_GET["language"]) && isset($_GET["action"])
&& ($_GET["action"]=="verify")){
  $key = $_GET["key"];
  $email = $_GET["email"];
  $curDate = date("Y-m-d H:i:s");
  $query = $pdo->prepare("SELECT * FROM `subscribers` WHERE `verify_code`=:key and `email`=:email;");
  $query->bindParam(":key",$key);
  $query->bindParam(":email",$email);
  $query->execute();
  $row =$query->rowCount();
  if ($row=="") {
    $error .= '<h2>Invalid Link</h2>
    <p>The link is invalid/expired. Either you did not copy the correct link
    from the email, or you have already used the key in which case it is 
    deactivated.</p>
    <p><a href="http://10.0.0.69/">
    Click here</a> to reset password.</p>';
  }
  else{
    $reset = $query->fetch(PDO::FETCH_ASSOC);
    $expDate = $reset['expDate'];
    echo "expDate is ".$expDate;
    if ($expDate >= $curDate){
          $error="";
          $curDate = date("Y-m-d H:i:s");
        if($error!=""){
          echo "<div class='error'>".$error."</div><br />";
        }
        else{
          $query = $pdo->prepare("UPDATE `subscribers` SET `is_verified`=1, `modified`=:modified WHERE `verify_code`=:verify_code;");
$query->bindParam(":modified",$curDate);
          $query->bindParam(":verify_code",$_GET["key"]);
          $result=$query->execute();
          if ($result) {
            echo "UPDATE OK<br>";
          }
          else{
            echo ("UPDATE Failed<br>");
            exit();
          }

          echo '<div class="error"><p>Thank you for subscribing to our newsletter!</p>
          <p><a href="http://localhost/cbch/mp/index.php">
          Click here</a> to Login.</p></div><br />';
        }
    }
    else{
      $error .= "<h2>Link Expired</h2>
      <p>The link is expired. You are trying to use the expired link which 
      as valid only 24 hours (1 days after request).<br /><br /></p>";
    }
  }
  if($error!=""){
    echo "<div class='error'>".$error."</div><br />";
  }
} // isset email key validate end
?>

Based on this, I tried doing something like http://10.0.0.69/php/verified.php?language=../../../../var/log/apache2/access.log which should theoretically display the access.log page. However, I seem to be getting a blank page instead.

Any help would be appreciated! (PS. Sorry for making you read through this ton of codes)

CBCH
  • 15
  • 5

2 Answers2

1

Most linux distros/web servers have safe file permissions on logs. You would need to be root to read the access log. Exploitation is a puzzle, but you can just superglue various behaviours together as long as they mostly do something useful and does not alter execution of the program fatally. So whatever works is a good enough way to exploit it...

Depending on the distro you may have luck with ../../../proc/self/environ and sending some PHP in a header such as the user agent.

Failing that you can try the following options:

  • Upload a file to the server and include it
  • Include your session file (if you can manipulate its content, ie: make your firstname or username a php payload)
  • Include a mysql table file that contains some data you can manipulate to be a PHP payload
  • Don't forget you can leak the session file location, etc, by reading php.ini

Other options:

  • Find a better bug
  • Find another bug to combine it with, ie: a CSV export function that leaves a file on the disk which you can place a PHP payload into.
  • Leak .env or config files outside webroot and use the AWS keys to access the box using SSM (if its using aws, etc).

Extreme options:

  • Bruteforce include a temporary file (upload uploads live in /tmp with a random filename for short time even if the PHP script doesn't handle file uploads, just POST the file to any PHP script with curl) Some time before the heat death of the universe you should get a hit.
  • Do a password reset for an admin account with higher privilege and try to extract the password reset token/url from the raw mysql table by including it.
wireghoul
  • 5,745
  • 2
  • 17
  • 26
  • Hey there, sorry about the late reply. I have already tried the `../../../proc/self/environ` and it doesn't work unfortunately. Also, regarding your other suggested options, I would like to stick to the local file inclusion using the include statement as I'm just testing out what I read from a blog recently. Thanks a lot for your help – CBCH Jun 03 '21 at 09:09
1

Let's suppose that the english.php is the default page.

The condition if (isset($_GET["key"]) && isset($_GET["email"]) && isset($_GET["language"]) && isset($_GET["action"]) && ($_GET["action"]=="verify")) will fail, as only the language is set, which explain the blank page.

PS: There is a missing '<' at the begging of the file "english.php".

St0rm
  • 527
  • 2
  • 9
  • 1
    Does the `if` condition matter? Since the arguments are supposed to run based on the include statement itself and not what it actually does on the page it includes. I have already tested out your suggestion previously and it still does not show me the access.log file. (that is except displaying the echo statements, which doesn't really matter) PS: The missing '<' at the beginning of the english.php file was a typo, sorry about that woops. Still, thank you for your help sir. – CBCH Jun 03 '21 at 09:04
  • Did you tried viewing the source code of the page or using curl ? – St0rm Jun 03 '21 at 09:20
  • Yep, it only shows the arguments in the url since I echoed it in the `verified.php` file – CBCH Jun 03 '21 at 09:43