Add lessons
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
const LessonModule = require('../base/LessonModule');
|
||||
|
||||
/**
|
||||
* IDOR (Insecure Direct Object Reference) Demo Lesson
|
||||
* Demonstrates how URL parameter manipulation can expose other users' data
|
||||
*/
|
||||
class IDORDemoLesson extends LessonModule {
|
||||
constructor(config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mock user database
|
||||
* @returns {Array} Mock user data
|
||||
*/
|
||||
getMockUsers() {
|
||||
return [
|
||||
{
|
||||
id: 1,
|
||||
name: 'System Administrator',
|
||||
email: 'admin@securebank.example',
|
||||
accountBalance: '$999,999.99',
|
||||
accountNumber: '****0001',
|
||||
lastLogin: '2026-02-08 10:00',
|
||||
address: '1 Admin Tower, Capital City, USA',
|
||||
phone: '(555) 000-0001',
|
||||
isCurrentUser: false,
|
||||
accountType: 'Administrative Account',
|
||||
isHighValue: true,
|
||||
adminAccess: true,
|
||||
isEasterEgg: true,
|
||||
easterEggType: 'admin',
|
||||
bonusPoints: 25,
|
||||
easterEggMessage: '🎯 Admin Account Found! You discovered the system administrator account.'
|
||||
},
|
||||
{
|
||||
id: 42,
|
||||
name: 'Douglas Adams',
|
||||
email: 'dont.panic@example.com',
|
||||
accountBalance: '$42,000,000.00',
|
||||
accountNumber: '****4242',
|
||||
lastLogin: '2026-02-07 16:45',
|
||||
address: '42 Galaxy Street, Universe, Space',
|
||||
phone: '(555) 424-2424',
|
||||
isCurrentUser: false,
|
||||
accountType: 'Millionaire Account',
|
||||
isHighValue: true,
|
||||
isEasterEgg: true,
|
||||
easterEggType: 'millionaire',
|
||||
bonusPoints: 20,
|
||||
easterEggMessage: '💰 Millionaire Discovered! The answer to life, universe, and everything.'
|
||||
},
|
||||
{
|
||||
id: 54,
|
||||
name: 'Jane Smith',
|
||||
email: 'jane.smith@example.com',
|
||||
accountBalance: '$45,890.50',
|
||||
accountNumber: '****5454',
|
||||
lastLogin: '2026-02-08 08:30',
|
||||
address: '456 Oak Avenue, Springfield, USA',
|
||||
phone: '(555) 234-5678',
|
||||
isCurrentUser: false,
|
||||
accountType: 'Premium Savings',
|
||||
securityLevel: 'high',
|
||||
isNeighbor: true,
|
||||
bonusPoints: 10
|
||||
},
|
||||
{
|
||||
id: 55,
|
||||
name: 'Max Mustermann',
|
||||
email: 'max.mustermann@example.com',
|
||||
accountBalance: '$2,340.75',
|
||||
accountNumber: '****5555',
|
||||
lastLogin: '2026-02-08 09:15',
|
||||
address: '123 Main Street, Anytown, USA',
|
||||
phone: '(555) 123-4567',
|
||||
isCurrentUser: true,
|
||||
accountType: 'Standard Checking'
|
||||
},
|
||||
{
|
||||
id: 56,
|
||||
name: 'Lisa Wagner',
|
||||
email: 'lisa.w@example.com',
|
||||
accountBalance: '$18,250.00',
|
||||
accountNumber: '****5656',
|
||||
lastLogin: '2026-02-08 07:45',
|
||||
address: '789 Pine Road, Neighborhood, USA',
|
||||
phone: '(555) 567-8901',
|
||||
isCurrentUser: false,
|
||||
accountType: 'Business Checking',
|
||||
isNeighbor: true,
|
||||
bonusPoints: 10
|
||||
},
|
||||
{
|
||||
id: 67,
|
||||
name: '¯\\_(ツ)_/¯',
|
||||
email: 'mystery@example.com',
|
||||
accountBalance: '$6,700.00',
|
||||
accountNumber: '****6767',
|
||||
lastLogin: '2026-01-01 00:00',
|
||||
address: '¯\\_(ツ)_/¯',
|
||||
phone: '¯\\_(ツ)_/¯',
|
||||
isCurrentUser: false,
|
||||
accountType: 'Mystery Account',
|
||||
isEasterEgg: true,
|
||||
easterEggType: 'shrug',
|
||||
bonusPoints: 15,
|
||||
easterEggMessage: '¯\\_(ツ)_/¯'
|
||||
},
|
||||
{
|
||||
id: 100,
|
||||
name: 'Diana Prince',
|
||||
email: 'diana.p@example.com',
|
||||
accountBalance: '$125,000.00',
|
||||
accountNumber: '****1000',
|
||||
lastLogin: '2026-02-08 07:00',
|
||||
address: '100 Hero Boulevard, Metro City, USA',
|
||||
phone: '(555) 100-0001',
|
||||
isCurrentUser: false,
|
||||
accountType: 'Premium Investment',
|
||||
securityLevel: 'maximum'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch user profile by ID (vulnerable simulation)
|
||||
* Called via executeLessonAction endpoint
|
||||
* @param {number} userId - User ID to fetch
|
||||
* @returns {Object} User profile data or error
|
||||
*/
|
||||
async fetchUserProfile(userId, participantId, eventLessonId) {
|
||||
const users = this.getMockUsers();
|
||||
const requestedId = parseInt(userId);
|
||||
|
||||
// Find user
|
||||
const user = users.find(u => u.id === requestedId);
|
||||
|
||||
if (!user) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'USER_NOT_FOUND',
|
||||
message: 'Benutzer nicht gefunden',
|
||||
statusCode: 404
|
||||
};
|
||||
}
|
||||
|
||||
// Detect unauthorized access
|
||||
const isUnauthorized = !user.isCurrentUser;
|
||||
|
||||
// Award points for discoveries
|
||||
let pointsAwarded = 0;
|
||||
let discoveryMessage = null;
|
||||
|
||||
if (isUnauthorized && participantId && eventLessonId) {
|
||||
// Award points for any IDOR discovery
|
||||
pointsAwarded = 10;
|
||||
|
||||
// Check for easter eggs and award bonus points
|
||||
if (user.isEasterEgg) {
|
||||
pointsAwarded += user.bonusPoints;
|
||||
discoveryMessage = user.easterEggMessage;
|
||||
} else if (user.isNeighbor) {
|
||||
pointsAwarded += user.bonusPoints;
|
||||
}
|
||||
|
||||
// Award points (don't duplicate if same user accessed multiple times)
|
||||
try {
|
||||
await this.awardPoints(participantId, eventLessonId, pointsAwarded,
|
||||
`IDOR discovered: User ${requestedId}`);
|
||||
} catch (error) {
|
||||
console.error('Failed to award IDOR points:', error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
user: {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
accountBalance: user.accountBalance,
|
||||
accountNumber: user.accountNumber,
|
||||
lastLogin: user.lastLogin,
|
||||
address: user.address,
|
||||
phone: user.phone,
|
||||
accountType: user.accountType,
|
||||
...(user.isHighValue && { isHighValue: true }),
|
||||
...(user.adminAccess && { adminAccess: true }),
|
||||
...(user.securityLevel && { securityLevel: user.securityLevel })
|
||||
},
|
||||
isCurrentUser: user.isCurrentUser,
|
||||
isUnauthorized,
|
||||
pointsAwarded: pointsAwarded > 0 ? pointsAwarded : undefined,
|
||||
easterEgg: user.isEasterEgg ? {
|
||||
type: user.easterEggType,
|
||||
message: discoveryMessage
|
||||
} : undefined,
|
||||
vulnerability: isUnauthorized ? {
|
||||
type: 'IDOR',
|
||||
severity: user.isHighValue || user.adminAccess ? 'CRITICAL' : 'HIGH',
|
||||
description: '⚠️ IDOR-Schwachstelle entdeckt!',
|
||||
message: `Sie sehen ${user.name}s private Daten ohne Berechtigung!`,
|
||||
impact: 'Ein Angreifer kann auf sensible Informationen eines beliebigen Benutzers zugreifen, indem er einfach den userId-Parameter in der URL ändert.',
|
||||
recommendation: 'Implementieren Sie ordnungsgemäße Autorisierungsprüfungen. Überprüfen Sie, ob der authentifizierte Benutzer die Berechtigung hat, auf die angeforderte Ressource zuzugreifen.',
|
||||
cve: user.isHighValue ? 'Dies ist eine kritische Schwachstelle - Admin-/Hochwertkonto aufgerufen!' : null
|
||||
} : null
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get interactive data for IDOR demo step
|
||||
* @param {string} stepId - Step identifier
|
||||
* @returns {Object} Interactive component data
|
||||
*/
|
||||
async getInteractiveData(stepId) {
|
||||
if (stepId === 'idor-demo') {
|
||||
return {
|
||||
baseUrl: 'https://securebank.example/profile',
|
||||
currentUserId: 55,
|
||||
vulnerableParameter: 'userId',
|
||||
easterEggs: [
|
||||
{ id: 1, type: 'admin', found: false },
|
||||
{ id: 42, type: 'millionaire', found: false },
|
||||
{ id: 67, type: 'shrug', found: false },
|
||||
{ id: 54, type: 'neighbor', found: false },
|
||||
{ id: 56, type: 'neighbor', found: false }
|
||||
],
|
||||
secureApproach: {
|
||||
title: 'Sichere Implementierung',
|
||||
description: 'Anstelle von URL-Parametern, verwenden Sie sitzungsbasierte Authentifizierung',
|
||||
example: 'GET /profile (gibt nur die Daten des authentifizierten Benutzers zurück)',
|
||||
code: `
|
||||
// Anfällig (IDOR):
|
||||
app.get('/profile', (req, res) => {
|
||||
const userId = req.query.userId; // ❌ Jeder kann dies ändern!
|
||||
const user = db.getUserById(userId);
|
||||
res.json(user);
|
||||
});
|
||||
|
||||
// Sicher (Sitzungsbasiert):
|
||||
app.get('/profile', authenticate, (req, res) => {
|
||||
const userId = req.session.userId; // ✅ Aus verifizierter Sitzung
|
||||
if (req.params.userId && req.params.userId !== userId) {
|
||||
return res.status(403).json({ error: 'Forbidden' });
|
||||
}
|
||||
const user = db.getUserById(userId);
|
||||
res.json(user);
|
||||
});
|
||||
`.trim()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return await super.getInteractiveData(stepId);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = IDORDemoLesson;
|
||||
Reference in New Issue
Block a user