Outils d'utilisateurs

Outils du Site


stripe_ctf

Ceci est une ancienne révision du document !


Level 0

Enonce

We'll start you out with Level 0, the Secret Safe. The Secret Safe is designed as a secure place to store all of your secrets.
It turns out that the password to access Level 1 is stored within the Secret Safe. If only you knew how to crack safes…
You can access the Secret Safe at https://level00-1.stripe-ctf.com/user-kfsvkinufw.
The Safe's code is included below, and can also be obtained via git clone https://level00-1.stripe-ctf.com/user-kfsvkinufw/level00-code.

Code

Here's the code for level00.js, the main server file:

// Install dependencies with 'npm install'
// Run as 'node level00.js'
 
var express = require('express'), // Web framework
    mu = require('mu2'),          // Mustache.js templating
    sqlite3 = require('sqlite3'); // SQLite (database) driver
 
// Look for templates in the current directory
mu.root = __dirname;
 
// Set up the DB
var db = new sqlite3.Database('level00.db');
db.run(
  'CREATE TABLE IF NOT EXISTS secrets (' +
    'key varchar(255),' +
    'secret varchar(255)' +
  ')'
);
 
// Create the server
var app = express();
app.use(express.bodyParser());
 
function renderPage(res, variables) {
  var stream = mu.compileAndRender('level00.html', variables);
  res.header('Content-Type', 'text/html');
  stream.pipe(res);
}
 
app.get('/*', function(req, res) {
  var namespace = req.param('namespace');
 
  if (namespace) {
    var query = 'SELECT * FROM secrets WHERE key LIKE ? || ".%"';
    db.all(query, namespace, function(err, secrets) {
             if (err) throw err;
 
             renderPage(res, {namespace: namespace, secrets: secrets});
           });
  } else {
    renderPage(res, {});
  }
});
 
app.post('/*', function(req, res) {
  var namespace = req.body['namespace'];
  var secret_name = req.body['secret_name'];
  var secret_value = req.body['secret_value'];
 
  var query = 'INSERT INTO secrets (key, secret) VALUES (? || "." || ?, ?)';
  db.run(query, namespace, secret_name, secret_value, function(err) {
     if (err) throw err;
 
           res.header('Content-Type', 'text/html');
           res.redirect(req.path + '?namespace=' + namespace);
         });
});
 
if (process.argv.length > 2) {
  var socket = process.argv[2];
  console.log("Starting server on UNIX socket " + socket);
  app.listen(socket);
} else {
  console.log("Starting server at http://localhost:3000/");
  app.listen(3000);
}

And here's the code for level00.html, its mustache.js template:

<html>
  <head>
    <title>Secret Safe</title>
  </head>
  <body>
    {{#namespace}}
    <div style="border-width: 2px; border-style: outset; padding: 5px">
      Showing secrets for <strong>{{namespace}}</strong>:
      <table>
        <thead>
          <tr>
            <th>Key</th>
            <th>Value</th>
          </tr>
        </thead>
        <tbody>
          {{#secrets}}
          <tr>
            <td>{{ key }}</td>
            <td>{{ secret }}</td>
          </tr>
          {{/secrets}}
          {{^secrets}}
          <tr>
            <td span="2">
              You have no secrets stored with us. Try using the form below.
            </td>
          </tr>
          {{/secrets}}
        </tbody>
      </table>
 
      <hr />
    </div>
    {{/namespace}}
 
    <form action="" method="POST">
      <p>
        <label for="namespace">Namespace:</label>
        <input type="text" name="namespace" id="namespace"
            value="{{ namespace }}" />
      </p>
      <p>
        <label for="secret_name">Name of your secret:</label>
        <input type="text" name="secret_name" id="secret_name">
      </p>
      <p>
        <label for="secret_value">Your secret:</label>
        <input type="password" name="secret_value" id="secret_value">
      </p>
      <p>
        <input type="submit" value="Store my secret!" />
      </p>
    </form>
    <form action="" method="GET">
      <label for="change_namespace">
        Want to retrieve your secrets? View secrets for:
      </label>
      <input name="namespace" id="change_namespace" />
      <input type="submit" value="View" />
    </form>
  </body>
</html>

Solution

Pour ajouter un secret, il faut definir le namespace, une cle et une valeur, secret qui sera stocké sous la forme key = namespace.cle et secret = valeur

app.post('/*', function(req, res) {
  var namespace = req.body['namespace'];
  var secret_name = req.body['secret_name'];
  var secret_value = req.body['secret_value'];
 
  var query = 'INSERT INTO secrets (key, secret) VALUES (? || "." || ?, ?)';
  db.run(query, namespace, secret_name, secret_value, function(err) {

La requete GET pour afficher les secrets requiert un parametre, le namespace et est construite avec

app.get('/*', function(req, res) {
  var namespace = req.param('namespace');
 
  if (namespace) {
    var query = 'SELECT * FROM secrets WHERE key LIKE ? || ".%"';
    db.all(query, namespace, function(err, secrets) {

Cette requete affiche donc tous les secrets du namespace passé en argument. Il suffit d'utiliser % comme parametre pour afficher tous les secrets de tous les namespaces et donc le mot de passe.

Level 1

Enonce

Excellent, you are now on Level 1, the Guessing Game. All you have to do is guess the combination correctly, and you'll be given
the password to access Level 2! We've been assured that this level has no security vulnerabilities in it
(and the machine running the Guessing Game has no outbound network connectivity, meaning you wouldn't be able to extract the password anyway), so >you'll probably just have to try all the possible combinations. Or will you…?

You can play the Guessing Game at https://level01-2.stripe-ctf.com/user-ntyjkglswi. The code for the Game can be obtained from
git clone https://level01-2.stripe-ctf.com/user-ntyjkglswi/level01-code, and is also included below.

Code

The contents of index.php

<html>
  <head>
    <title>Guessing Game</title>
  </head>
  <body>
    <h1>Welcome to the Guessing Game!</h1>
    <p>
      Guess the secret combination below, and if you get it right,
      you'll get the password to the next level!
    </p>
    <?php
      $filename = 'secret-combination.txt';
      extract($_GET);
      if (isset($attempt)) {
        $combination = trim(file_get_contents($filename));
        if ($attempt === $combination) {
          echo "<p>How did you know the secret combination was" .
               " $combination!?</p>";
          $next = file_get_contents('level02-password.txt');
          echo "<p>You've earned the password to the access Level 2:" .
               " $next</p>";
        } else {
          echo "<p>Incorrect! The secret combination is not $attempt</p>";
        }
      }
    ?>
    <form action="#" method="GET">
      <p><input type="text" name="attempt"></p>
      <p><input type="submit" value="Guess!"></p>
    </form>
  </body>
</html>

Solution

Le code php prend l'input de la requete GET attempt en utilisant la methode extract(_GET). Ensuite le serveur lit le contenu du fichier $filename. Si le parametre attempt est egal au contenu du fichier $filename, le mot de passe est affiche. L'ordre dans lequel le serveur definit le fichier a lire et le lit est important. En effet, la variable $filename est initialisee, puis les parametres de la requete GET sont lus et finalement le contenu du fichier est lu. En ajoutant a la requete GET un parametre filename, ce parametre va remplacer le contenu de la variable $filename par le nom de notre fichier. Il ne reste plus qu'a trouver un fichier dont on connait le contenu, par exemple

Level 2

Enonce

You are now on Level 2, the Social Network. Excellent work so far! Social Networks are all the rage these days,
so we decided to build one for CTF. Please fill out your profile at https://level02-3.stripe-ctf.com/user-zjfpnuykfi.
You may even be able to find the password for Level 3 by doing so.
The code for the Social Network can be obtained from git clone https://level02-3.stripe-ctf.com/user-zjfpnuykfi/level02-code,
and is also included below.

Code

The contents of index.php

<?php
  session_start();
 
  if ($_FILES["dispic"]["error"] > 0) {
    echo "<p>Error: " . $_FILES["dispic"]["error"] . "</p>";
  }
  else
  {
    $dest_dir = "uploads/";
    $dest = $dest_dir . basename($_FILES["dispic"]["name"]);
    $src = $_FILES["dispic"]["tmp_name"];
    if (move_uploaded_file($src, $dest)) {
      $_SESSION["dispic_url"] = $dest;
      chmod($dest, 0644);
      echo "<p>Successfully uploaded your display picture.</p>";
    }
  }
 
  $url = "https://upload.wikimedia.org/wikipedia/commons/f/f8/" .
         "Question_mark_alternate.svg";
  if (isset($_SESSION["dispic_url"])) {
    $url = $_SESSION["dispic_url"];
  }
 
?>
 
<html>
  <head>
    <title>Welcome to the CTF!</title>
  </head>
  <body>
    <center>
      <h1>Welcome to the CTF Social Network!</h1>
      <div>
        <img src=<?php echo $url; ?> />
        <?php
          if (!isset($_SESSION["dispic_url"])) {
            echo "<p>Oh, looks like you don't have a profile image" .
                 " -- upload one now!</p>";
          }
        ?>
        <form action="" method="post" enctype="multipart/form-data">
          <input type="file" name="dispic" size="40" />
          <input type="submit" value="Upload!">
        </form>
 
        <p>
           Password for Level 3 (accessible only to members of the club):
           <a href="password.txt">password.txt</a>
        </p>
      </div>
    </center>
  </body>
</html>

Solution

En cliquant sur le lien contenant le mot de passe, on obtient un 403 Forbidden. L'application permet d'uploader une image pour son profil. En analysant le code source, on s'apercoit qu'il n'y a aucune verification faite par le serveur et que l'on peut uploader n'importe quel fichier qui sera stocke dans le sous-repertoire uploads. Il suffit donc d'uploader un fichier php qui lira le contenu du fichier password.txt et l'affiche puis de l'appeler directement

<?php
$combination = file_get_contents('../password.txt');
echo "$combination"
?>

Level 3

Level 4

Level 5

Level 6

Level 7

Level 8

stripe_ctf.1346167836.txt.gz · Dernière modification: 2017/04/09 15:33 (modification externe)