Compare commits

...

3 Commits

8 changed files with 248 additions and 10 deletions

29
Dockerfile Normal file
View File

@@ -0,0 +1,29 @@
# --- Build frontend ---
FROM node:24-slim AS frontend-builder
WORKDIR /frontend
COPY frontend/package*.json ./
RUN npm install
COPY frontend/ .
RUN npm run build
# --- Backend setup ---
FROM node:24-slim
# Install ping for backend
RUN apt-get update && \
apt-get install -y iputils-ping && \
apt-get clean && rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy backend code
COPY backend/package*.json ./
RUN npm install
COPY backend/ .
# Copy built frontend into backend expected location
COPY --from=frontend-builder /frontend/dist /frontend/dist
EXPOSE 5000
CMD ["node", "index.js"]

View File

@@ -1,5 +1,10 @@
FROM node:23-slim FROM node:23-slim
RUN apt update && \
apt install -y iputils-ping \
&& apt clean \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app WORKDIR /app
COPY . . COPY . .
RUN npm install RUN npm install

View File

@@ -1,12 +1,15 @@
const express = require('express'); const express = require('express');
const mysql = require('mysql2'); const mysql = require('mysql2');
const cors = require('cors'); const cors = require('cors');
const { exec } = require('child_process');
const app = express(); const app = express();
const path = require('path');
const db = require('./db'); const db = require('./db');
app.use(cors()); app.use(cors());
app.use(express.json()); app.use(express.json());
const serveStatic = express.static(path.join(__dirname, '../frontend/dist'));
app.post('/api/login', (req, res) => { app.post('/api/login', (req, res) => {
const { username, password } = req.body; const { username, password } = req.body;
@@ -35,4 +38,58 @@ app.post('/api/login', (req, res) => {
}); });
}); });
app.post('/api/ping', (req, res) => {
const { ip } = req.body;
// 🚨 INTENTIONALLY VULNERABLE TO COMMAND INJECTION
const command = `ping -c 4 ${ip}`;
exec(command, (err, stdout, stderr) => {
if (err) {
return res.status(500).json({
output: stderr,
command: command
});
}
res.json({
output: stdout,
command: command
});
});
});
// Endpoint to add a new post
app.post('/api/posts', (req, res) => {
const { post } = req.body;
if (!post || post.trim() === '') {
return res.status(400).json({ message: 'Post content cannot be empty' });
}
const query = 'INSERT INTO posts (content) VALUES (?)';
db.query(query, [post], (err, results) => {
if (err) {
return res.status(500).json({ message: 'Error adding post', error: err });
}
res.json({ message: 'Post added successfully', postId: results.insertId });
});
});
// Endpoint to get all posts
app.get('/api/posts', (req, res) => {
const query = 'SELECT * FROM posts';
db.query(query, (err, results) => {
if (err) {
return res.status(500).json({ message: 'Error fetching posts', error: err });
}
res.json({ posts: results });
});
});
app.use((req, res, next) => {
if (!req.path.startsWith('/api')) {
return serveStatic(req, res, next);
}
next();
});
app.listen(5000, () => console.log('Backend running on port 5000')); app.listen(5000, () => console.log('Backend running on port 5000'));

View File

@@ -4,5 +4,10 @@ CREATE TABLE users (
password VARCHAR(255) password VARCHAR(255)
); );
CREATE TABLE posts (
id INT AUTO_INCREMENT PRIMARY KEY,
content TEXT NOT NULL
);
INSERT INTO users (username, password) VALUES ('admin', 'admin123'); INSERT INTO users (username, password) VALUES ('admin', 'admin123');
INSERT INTO users (username, password) VALUES ('user', 'password'); INSERT INTO users (username, password) VALUES ('user', 'password');

View File

@@ -15,19 +15,13 @@ services:
timeout: 10s timeout: 10s
retries: 3 retries: 3
backend: service:
build: ./backend build: ./
restart: always restart: always
ports: ports:
- "5000:5000" - "5000:5000"
cap_add:
- NET_RAW
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy
frontend:
build: ./frontend
restart: always
ports:
- "3000:3000"
depends_on:
- backend

View File

@@ -1,6 +1,8 @@
import { BrowserRouter as Router, Route, Routes, Link } from 'react-router-dom'; import { BrowserRouter as Router, Route, Routes, Link } from 'react-router-dom';
import SQLInjection from './pages/SQLInjection'; import SQLInjection from './pages/SQLInjection';
import { Navbar, Nav, Container } from 'react-bootstrap'; import { Navbar, Nav, Container } from 'react-bootstrap';
import CommandInjection from './pages/CommandInjection';
import XSS from './pages/XSS';
function App() { function App() {
return ( return (
@@ -12,6 +14,8 @@ function App() {
<Navbar.Collapse id="basic-navbar-nav"> <Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto"> <Nav className="me-auto">
<Nav.Link as={Link} to="/sqli">SQL Injection</Nav.Link> <Nav.Link as={Link} to="/sqli">SQL Injection</Nav.Link>
<Nav.Link as={Link} to="/cmdi">Command Injection</Nav.Link>
<Nav.Link as={Link} to="/xss">XSS</Nav.Link>
</Nav> </Nav>
</Navbar.Collapse> </Navbar.Collapse>
</Container> </Container>
@@ -19,6 +23,8 @@ function App() {
<div style={{ padding: 20 }}> <div style={{ padding: 20 }}>
<Routes> <Routes>
<Route path="/sqli" element={<SQLInjection />} /> <Route path="/sqli" element={<SQLInjection />} />
<Route path="/cmdi" element={<CommandInjection />} />
<Route path="/xss" element={<XSS />} />
</Routes> </Routes>
</div> </div>
</Router> </Router>

View File

@@ -0,0 +1,68 @@
import React, { useState } from "react";
import { Card, Container, Form, Button, Alert } from "react-bootstrap";
export default function CommandInjection() {
const [ipAddress, setIpAddress] = useState("");
const [output, setOutput] = useState("");
const [command, setCommand] = useState("");
const [showCommand, setShowCommand] = useState(false);
const [status, setStatus] = useState(null);
const handlePing = async () => {
const res = await fetch("http://localhost:5000/api/ping", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ip: ipAddress }),
});
const data = await res.json();
setStatus(res.status);
setOutput(data.output);
setCommand(data.command);
};
return (
<Container className="mt-5">
<Card className="shadow">
<Card.Body>
<Card.Title className="text-center">Ping Test</Card.Title>
<Form>
<Form.Group className="mb-3" controlId="formIpAddress">
<Form.Control
type="text"
placeholder="Enter IP Address"
onChange={(e) => setIpAddress(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="formShowCommand">
<Form.Check
type="checkbox"
label="Show command"
checked={showCommand}
onChange={(e) => setShowCommand(e.target.checked)}
/>
</Form.Group>
<div className="d-grid">
<Button variant="primary" onClick={handlePing}>
Ping
</Button>
</div>
</Form>
{output && (
<Alert
className={`mt-3 ${
status === 200 ? "alert-success" : "alert-warning"
}`}
>
<strong>Output:</strong> {output}
</Alert>
)}
{showCommand && command && (
<Alert className="mt-3 info">
<strong>Command:</strong> {command}
</Alert>
)}
</Card.Body>
</Card>
</Container>
);
}

View File

@@ -0,0 +1,74 @@
import React, { useState } from "react";
import { Card, Container, Form, Button, Alert, ListGroup } from "react-bootstrap";
export default function XSS() {
const [posts, setPosts] = useState([]);
const [newPost, setNewPost] = useState("");
const [searchQuery, setSearchQuery] = useState("");
const handleAddPost = () => {
if (newPost.trim()) {
setPosts([...posts, newPost]);
setNewPost("");
}
};
const filteredPosts = posts.filter((post) =>
post.toLowerCase().includes(searchQuery.toLowerCase())
);
return (
<Container className="mt-5">
<Card className="shadow">
<Card.Body>
<Card.Title className="text-center">Posts</Card.Title>
{/* List of Posts */}
<h5>Posts</h5>
<ListGroup className="mb-3">
{filteredPosts.length > 0 ? (
filteredPosts.map((post, index) => (
<ListGroup.Item key={index}>
{/* Rendering posts directly (stored XSS vulnerability) */}
<span dangerouslySetInnerHTML={{ __html: post }} />
</ListGroup.Item>
))
) : (
<ListGroup.Item>No posts available</ListGroup.Item>
)}
</ListGroup>
{/* Add New Post */}
<Form>
<Form.Group className="mb-3" controlId="formNewPost">
<Form.Control
type="text"
placeholder="Enter new post"
value={newPost}
onChange={(e) => setNewPost(e.target.value)}
/>
</Form.Group>
<div className="d-grid">
<Button variant="primary" onClick={handleAddPost}>
Add Post
</Button>
</div>
</Form>
{/* Search Functionality */}
<h5 className="mt-4">Search</h5>
<Form>
<Form.Group className="mb-3" controlId="formSearchQuery">
<Form.Control
type="text"
placeholder="Enter search query"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</Form.Group>
</Form>
</Card.Body>
</Card>
</Container>
);
}