Creates the intentionally vulnerable Login module (SQL Injection / OWASP A03) for the CTF security lab. Use when asked to scaffold the login controller, login route, or login view. Produces: LoginController using raw DB::select() with direct string concatenation (no prepared statements), GET+POST /login routes, and a Tailwind login blade. Marks the vulnerable line with // [VULNERABLE HERE]. Do NOT use for production code.
OWASP A03:2021 – Injection (SQL Injection)
Allows full authentication bypass by injecting SQL into the email field (e.g., ' OR '1'='1' --). No valid password is required.
| Artifact | Path |
|---|---|
| Controller | app/Http/Controllers/LoginController.php |
| Routes | entries added to routes/web.php |
| Blade view | resources/views/auth/login.blade.php |
DB facade available.users table migration exists (database/migrations/0001_01_01_000000_create_users_table.php).Auth::loginUsingId() is acceptable (Eloquent User::where() must NOT be used).Create app/Http/Controllers/Auth/LoginController.php:
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
public function showLoginForm()
{
return view('auth.login');
}
public function login(Request $request)
{
$email = $request->email;
$password = $request->password;
// [VULNERABLE HERE] — raw SQL with direct string concatenation; no bindings, no escaping
$sql = "SELECT * FROM users WHERE email = '$email' AND password = '$password'";
$users = DB::select($sql);
if (!empty($users)) {
$user = $users[0];
Auth::loginUsingId($user->id);
return redirect('/dashboard');
}
return back()->withErrors(['email' => 'Invalid credentials.']);
}
public function logout()
{
Auth::logout();
return redirect('/login');
}
}
Mandatory rules (do NOT deviate):
DB::select() with the raw $sql string — never User::where()$request->email and $request->password directly — no ? placeholders or bindingsHash::check() is forbidden here)// [VULNERABLE HERE] directly above the DB::select($sql) callAdd to routes/web.php:
use App\Http\Controllers\Auth\LoginController;
Route::get('/login', [LoginController::class, 'showLoginForm'])->name('login');
Route::post('/login', [LoginController::class, 'login']);
Route::post('/logout', [LoginController::class, 'logout'])->name('logout');
Create resources/views/auth/login.blade.php:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Student Login</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 flex items-center justify-center min-h-screen">
<div class="bg-white p-8 rounded shadow w-full max-w-md">
<h2 class="text-2xl font-bold mb-6 text-center">Student Login</h2>
@if ($errors->any())
<div class="bg-red-100 text-red-700 p-3 rounded mb-4">
{{ $errors->first() }}
</div>
@endif
<form method="POST" action="/login">
@csrf
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Email</label>
<input type="text" name="email"
class="w-full border rounded px-3 py-2 focus:outline-none focus:ring"
placeholder="[email protected]">
</div>
<div class="mb-6">
<label class="block text-sm font-medium mb-1">Password</label>
<input type="password" name="password"
class="w-full border rounded px-3 py-2 focus:outline-none focus:ring">
</div>
<button type="submit"
class="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700">
Login
</button>
</form>
</div>
</body>
</html>
LoginController exists and uses DB::select() with raw string concatenation? placeholders) anywhere in the queryAuth::loginUsingId() used — not Auth::attempt()GET /login, POST /login, POST /logoutemail + password fields// [VULNERABLE HERE] comment is present above the vulnerable line' OR '1'='1' -- as email (any password) grants a session| Email Input | Password | Effect |
|---|---|---|
' OR '1'='1' -- | anything | Logs in as the first user in the table |
[email protected]' -- | anything | Logs in as that specific user, skipping password |
' UNION SELECT 1,2,3,4,5 -- | anything | Probes for column count / data exfiltration |
Context: This application runs in a closed, local training environment. These payloads are provided so trainers can verify the vulnerability is correctly implemented.
create-vuln-grades skillcreate-vuln-upload skillcreate-vuln-download skill