Add fake admin decoy and neon redesign
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,7 @@
|
|||||||
|
references_for_redisign
|
||||||
src/main/resources/static/*
|
src/main/resources/static/*
|
||||||
|
!src/main/resources/static/fake
|
||||||
|
!src/main/resources/static/fake/**
|
||||||
*.pcap
|
*.pcap
|
||||||
data
|
data
|
||||||
|
|
||||||
|
|||||||
16
README.md
16
README.md
@@ -1,10 +1,10 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
# Packmate
|
# 0xb00b5 team Packmate
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
### [[EN](README_EN.md) | RU]
|
### [[EN](README_EN.md) | RU]
|
||||||
Утилита перехвата и анализа трафика для CTF.
|
Утилита перехвата и анализа трафика для CTF, переосмысленная в пиксельном неоне.
|
||||||
|
|
||||||
#### Фичи:
|
#### Фичи:
|
||||||
* Поддерживает перехват живого трафика и обработку pcap файлов
|
* Поддерживает перехват живого трафика и обработку pcap файлов
|
||||||
@@ -23,14 +23,18 @@
|
|||||||
* Разархивирует GZIP в HTTP на лету
|
* Разархивирует GZIP в HTTP на лету
|
||||||
* Разархивирует сжатые WebSockets
|
* Разархивирует сжатые WebSockets
|
||||||
* Расшифровывает TLS на RSA при наличии приватного ключа
|
* Расшифровывает TLS на RSA при наличии приватного ключа
|
||||||
|
* Обманка для входа `admin:admin` с режимами `fun` и `fake_packets`, чтобы любопытные так и не добрались до настоящих пакетов
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
### Обманка admin:admin
|
||||||
|
Для входа с кредами `admin:admin` добавлена обманка (включена по умолчанию). Управляется через переменные `PACKMATE_FAKE_ADMIN_ENABLED` и `PACKMATE_FAKE_ADMIN_MODE` (`fun` или `fake_packets`) и не дает добраться до настоящего интерфейса.
|
||||||
|
|
||||||
## Быстрый запуск
|
## Быстрый запуск
|
||||||
Для быстрого запуска Packmate следует использовать [этот стартер](https://gitlab.com/packmate/starter/-/blob/master/README.md).
|
Для быстрого запуска 0xb00b5 team Packmate следует использовать [этот стартер](https://gitlab.com/packmate/starter/-/blob/master/README.md).
|
||||||
|
|
||||||
## Полный запуск
|
## Полный запуск
|
||||||
Ниже следует инструкция для тех, кто хочет собрать Packmate самостоятельно.
|
Ниже следует инструкция для тех, кто хочет собрать 0xb00b5 team Packmate самостоятельно.
|
||||||
|
|
||||||
### Клонирование
|
### Клонирование
|
||||||
Поскольку этот репозиторий содержит фронтенд как git submodule, его необходимо клонировать так:
|
Поскольку этот репозиторий содержит фронтенд как git submodule, его необходимо клонировать так:
|
||||||
@@ -56,7 +60,7 @@ git submodule update --init --recursive
|
|||||||
sudo docker compose up --build -d
|
sudo docker compose up --build -d
|
||||||
```
|
```
|
||||||
|
|
||||||
При успешном запуске Packmate будет видно с любого хоста на порту `65000`.
|
При успешном запуске 0xb00b5 team Packmate будет видно с любого хоста на порту `65000`.
|
||||||
БД будет слушать на порту 65001, но будет разрешать подключения только с localhost.
|
БД будет слушать на порту 65001, но будет разрешать подключения только с localhost.
|
||||||
|
|
||||||
## Использование
|
## Использование
|
||||||
@@ -64,5 +68,5 @@ sudo docker compose up --build -d
|
|||||||
|
|
||||||
<div align="right">
|
<div align="right">
|
||||||
|
|
||||||
*desu~*
|
*@danosito*
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
14
README_EN.md
14
README_EN.md
@@ -1,10 +1,10 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
# Packmate
|
# 0xb00b5 team Packmate
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
### [EN | [RU](README.md)]
|
### [EN | [RU](README.md)]
|
||||||
Advanced network traffic flow analyzer for A/D CTFs.
|
Advanced network traffic flow analyzer for A/D CTFs with a pixel-neon twist.
|
||||||
|
|
||||||
#### Features:
|
#### Features:
|
||||||
* Can monitor live traffic or analyze pcap files
|
* Can monitor live traffic or analyze pcap files
|
||||||
@@ -23,14 +23,18 @@ Advanced network traffic flow analyzer for A/D CTFs.
|
|||||||
* Can automatically decompress GZIPed HTTP
|
* Can automatically decompress GZIPed HTTP
|
||||||
* Can automatically deflate WebSockets with permessages-deflate extension
|
* Can automatically deflate WebSockets with permessages-deflate extension
|
||||||
* Can automatically decrypt TLS with RSA using given private key (like Wireshark)
|
* Can automatically decrypt TLS with RSA using given private key (like Wireshark)
|
||||||
|
* Decoy login for `admin:admin` with `fun` and `fake_packets` modes so snoopers never see the real data
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
### Admin:admin decoy
|
||||||
|
The admin:admin credentials now trigger a decoy (enabled by default). Configure it via `PACKMATE_FAKE_ADMIN_ENABLED` and `PACKMATE_FAKE_ADMIN_MODE` (`fun` or `fake_packets`) to keep everyone away from the real interface.
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
To quickly start using Packmate, use [this starter](https://gitlab.com/packmate/starter/-/blob/master/README_EN.md).
|
To quickly start using 0xb00b5 team Packmate, use [this starter](https://gitlab.com/packmate/starter/-/blob/master/README_EN.md).
|
||||||
|
|
||||||
## Full Build
|
## Full Build
|
||||||
Below are the instructions for those who want to build Packmate on their own.
|
Below are the instructions for those who want to build 0xb00b5 team Packmate on their own.
|
||||||
|
|
||||||
### Cloning
|
### Cloning
|
||||||
As this repository contains frontend part as a git submodule, it has to be cloned like this:
|
As this repository contains frontend part as a git submodule, it has to be cloned like this:
|
||||||
@@ -56,7 +60,7 @@ After filling in env file you can launch the app:
|
|||||||
sudo docker-compose up --build -d
|
sudo docker-compose up --build -d
|
||||||
```
|
```
|
||||||
|
|
||||||
If everything went fine, Packmate will be available on port `65000` from any host.
|
If everything went fine, 0xb00b5 team Packmate will be available on port `65000` from any host.
|
||||||
Database with listen on port 65001, but will only accept connections from localhost.
|
Database with listen on port 65001, but will only accept connections from localhost.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ services:
|
|||||||
PCAP_FILE: ${PACKMATE_PCAP_FILE:-}
|
PCAP_FILE: ${PACKMATE_PCAP_FILE:-}
|
||||||
WEB_LOGIN: ${PACKMATE_WEB_LOGIN:-BinaryBears}
|
WEB_LOGIN: ${PACKMATE_WEB_LOGIN:-BinaryBears}
|
||||||
WEB_PASSWORD: ${PACKMATE_WEB_PASSWORD:-123456}
|
WEB_PASSWORD: ${PACKMATE_WEB_PASSWORD:-123456}
|
||||||
|
FAKE_ADMIN_AUTH_ENABLED: ${PACKMATE_FAKE_ADMIN_ENABLED:-true}
|
||||||
|
FAKE_ADMIN_MODE: ${PACKMATE_FAKE_ADMIN_MODE:-fun}
|
||||||
OLD_STREAMS_CLEANUP_ENABLED: ${PACKMATE_OLD_STREAMS_CLEANUP_ENABLED:-false}
|
OLD_STREAMS_CLEANUP_ENABLED: ${PACKMATE_OLD_STREAMS_CLEANUP_ENABLED:-false}
|
||||||
OLD_STREAMS_CLEANUP_INTERVAL: ${PACKMATE_OLD_STREAMS_CLEANUP_INTERVAL:-5}
|
OLD_STREAMS_CLEANUP_INTERVAL: ${PACKMATE_OLD_STREAMS_CLEANUP_INTERVAL:-5}
|
||||||
OLD_STREAMS_CLEANUP_THRESHOLD: ${PACKMATE_OLD_STREAMS_CLEANUP_THRESHOLD:-240}
|
OLD_STREAMS_CLEANUP_THRESHOLD: ${PACKMATE_OLD_STREAMS_CLEANUP_THRESHOLD:-240}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ CMD [ "java", "-Djava.net.preferIPv4Stack=true", "-Djava.net.preferIPv4Addresses
|
|||||||
"--packmate.capture-mode=${MODE}", "--packmate.pcap-file=${PCAP_FILE}", \
|
"--packmate.capture-mode=${MODE}", "--packmate.pcap-file=${PCAP_FILE}", \
|
||||||
"--packmate.interface-name=${INTERFACE}", "--packmate.local-ip=${LOCAL_IP}", \
|
"--packmate.interface-name=${INTERFACE}", "--packmate.local-ip=${LOCAL_IP}", \
|
||||||
"--packmate.web.account-login=${WEB_LOGIN}", "--packmate.web.account-password=${WEB_PASSWORD}", \
|
"--packmate.web.account-login=${WEB_LOGIN}", "--packmate.web.account-password=${WEB_PASSWORD}", \
|
||||||
|
"--packmate.web.fake-admin.enabled=${FAKE_ADMIN_AUTH_ENABLED}", \
|
||||||
|
"--packmate.web.fake-admin.mode=${FAKE_ADMIN_MODE}", \
|
||||||
"--packmate.cleanup.enabled=${OLD_STREAMS_CLEANUP_ENABLED}", \
|
"--packmate.cleanup.enabled=${OLD_STREAMS_CLEANUP_ENABLED}", \
|
||||||
"--packmate.cleanup.interval=${OLD_STREAMS_CLEANUP_INTERVAL}", \
|
"--packmate.cleanup.interval=${OLD_STREAMS_CLEANUP_INTERVAL}", \
|
||||||
"--packmate.cleanup.threshold=${OLD_STREAMS_CLEANUP_THRESHOLD}", \
|
"--packmate.cleanup.threshold=${OLD_STREAMS_CLEANUP_THRESHOLD}", \
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
## Настройка
|
## Настройка
|
||||||
|
|
||||||
Packmate использует настройки из файла `.env` (в той же папке, что и `docker-compose.yml`)
|
0xb00b5 team Packmate использует настройки из файла `.env` (в той же папке, что и `docker-compose.yml`)
|
||||||
|
|
||||||
### Основные настройки
|
### Основные настройки
|
||||||
```dotenv
|
```dotenv
|
||||||
@@ -10,13 +10,17 @@ PACKMATE_LOCAL_IP=10.20.1.1
|
|||||||
PACKMATE_WEB_LOGIN=SomeUser
|
PACKMATE_WEB_LOGIN=SomeUser
|
||||||
# Пароль для web-авторизации
|
# Пароль для web-авторизации
|
||||||
PACKMATE_WEB_PASSWORD=SomeSecurePassword
|
PACKMATE_WEB_PASSWORD=SomeSecurePassword
|
||||||
|
# Включает обманку при вводе admin:admin
|
||||||
|
PACKMATE_FAKE_ADMIN_ENABLED=true
|
||||||
|
# fun или fake_packets - варианты обманки
|
||||||
|
PACKMATE_FAKE_ADMIN_MODE=fun
|
||||||
```
|
```
|
||||||
|
|
||||||
### Режим работы
|
### Режим работы
|
||||||
Packmate поддерживает три основных режима работы: `LIVE`, `FILE` и `VIEW`.
|
0xb00b5 team Packmate поддерживает три основных режима работы: `LIVE`, `FILE` и `VIEW`.
|
||||||
1. `LIVE` - это основной режим работы во время CTF. Packmate обрабатывает живой трафик и сразу выводит результаты.
|
1. `LIVE` - это основной режим работы во время CTF. 0xb00b5 team Packmate обрабатывает живой трафик и сразу выводит результаты.
|
||||||
2. `FILE` - обрабатывает трафик из pcap файлов. Полезен для анализа трафика с прошедших CTF, где не был запущен Packmate, или тех, где невозможно запустить его на вулнбоксе.
|
2. `FILE` - обрабатывает трафик из pcap файлов. Полезен для анализа трафика с прошедших CTF, где не был запущен 0xb00b5 team Packmate, или тех, где невозможно запустить его на вулнбоксе.
|
||||||
3. `VIEW` - Packmate не обрабатывает трафик, а только показывает уже обработанные стримы. Полезен для разборов после завершения CTF.
|
3. `VIEW` - 0xb00b5 team Packmate не обрабатывает трафик, а только показывает уже обработанные стримы. Полезен для разборов после завершения CTF.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Настройка LIVE</summary>
|
<summary>Настройка LIVE</summary>
|
||||||
@@ -62,7 +66,7 @@ PACKMATE_MODE=VIEW
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Очистка БД
|
### Очистка БД
|
||||||
На крупных CTF через какое-то время накапливается большое количество трафика. Это замедляет работу Packmate и занимает много места на диске.
|
На крупных CTF через какое-то время накапливается большое количество трафика. Это замедляет работу 0xb00b5 team Packmate и занимает много места на диске.
|
||||||
|
|
||||||
Для оптимизации работы, рекомендуется включить регулярную очистку БД от старых стримов. Это будет работать только в режиме `LIVE`.
|
Для оптимизации работы, рекомендуется включить регулярную очистку БД от старых стримов. Это будет работать только в режиме `LIVE`.
|
||||||
```dotenv
|
```dotenv
|
||||||
@@ -79,7 +83,7 @@ PACKMATE_OLD_STREAMS_CLEANUP_THRESHOLD=240
|
|||||||
```dotenv
|
```dotenv
|
||||||
# Пароль от БД. Из-за того, что БД принимает подключения только с localhost, менять его необязательно, но можно, для дополнительной безопасности.
|
# Пароль от БД. Из-за того, что БД принимает подключения только с localhost, менять его необязательно, но можно, для дополнительной безопасности.
|
||||||
PACKMATE_DB_PASSWORD=K604YnL3G1hp2RDkCZNjGpxbyNpNHTRb
|
PACKMATE_DB_PASSWORD=K604YnL3G1hp2RDkCZNjGpxbyNpNHTRb
|
||||||
# Версия Packmate. Можно изменить, если нужно использовать другой образ из docker registry.
|
# Версия 0xb00b5 team Packmate. Можно изменить, если нужно использовать другой образ из docker registry.
|
||||||
BUILD_TAG=latest
|
BUILD_TAG=latest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
Packmate uses properties from the `.env` file (in the same directory as `docker-compose.yml`)
|
0xb00b5 team Packmate uses properties from the `.env` file (in the same directory as `docker-compose.yml`)
|
||||||
|
|
||||||
### Primary settings
|
### Primary settings
|
||||||
```dotenv
|
```dotenv
|
||||||
@@ -10,13 +10,17 @@ PACKMATE_LOCAL_IP=10.20.1.1
|
|||||||
PACKMATE_WEB_LOGIN=SomeUser
|
PACKMATE_WEB_LOGIN=SomeUser
|
||||||
# Password for the web interface
|
# Password for the web interface
|
||||||
PACKMATE_WEB_PASSWORD=SomeSecurePassword
|
PACKMATE_WEB_PASSWORD=SomeSecurePassword
|
||||||
|
# Enable decoy flow for admin:admin login
|
||||||
|
PACKMATE_FAKE_ADMIN_ENABLED=true
|
||||||
|
# fun or fake_packets - pick the decoy flavor
|
||||||
|
PACKMATE_FAKE_ADMIN_MODE=fun
|
||||||
```
|
```
|
||||||
|
|
||||||
### Modes of operation
|
### Modes of operation
|
||||||
Packmate supports 3 modes of operation: `LIVE`, `FILE` и `VIEW`.
|
0xb00b5 team Packmate supports 3 modes of operation: `LIVE`, `FILE` и `VIEW`.
|
||||||
1. `LIVE` - the usual mode during a CTF. Packmate processes live traffic and instantly displays the results.
|
1. `LIVE` - the usual mode during a CTF. 0xb00b5 team Packmate processes live traffic and instantly displays the results.
|
||||||
2. `FILE` - processes traffic from pcap files. Useful to analyze traffic from past CTFs where Packmate wasn't launched, or CTFs where it's impossible to use it on the vulnbox.
|
2. `FILE` - processes traffic from pcap files. Useful to analyze traffic from past CTFs where 0xb00b5 team Packmate wasn't launched, or CTFs where it's impossible to use it on the vulnbox.
|
||||||
3. `VIEW` - Packmate does not process any traffic, but simply shows already processed streams. Useful for post-game analyses.
|
3. `VIEW` - 0xb00b5 team Packmate does not process any traffic, but simply shows already processed streams. Useful for post-game analyses.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>LIVE setup</summary>
|
<summary>LIVE setup</summary>
|
||||||
@@ -62,7 +66,7 @@ PACKMATE_MODE=VIEW
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Database cleanup
|
### Database cleanup
|
||||||
On large CTFsб after some time a lot of traffic will pile up. This can slow Packmate down and take a lot of drive space.
|
On large CTFsб after some time a lot of traffic will pile up. This can slow 0xb00b5 team Packmate down and take a lot of drive space.
|
||||||
|
|
||||||
To optimize the workflow, it is recommended to enable periodical database cleanup of old streams. It will only work in the `LIVE` mode.
|
To optimize the workflow, it is recommended to enable periodical database cleanup of old streams. It will only work in the `LIVE` mode.
|
||||||
```dotenv
|
```dotenv
|
||||||
@@ -79,7 +83,7 @@ PACKMATE_OLD_STREAMS_CLEANUP_THRESHOLD=240
|
|||||||
```dotenv
|
```dotenv
|
||||||
# Database password. Considering it only listens on localhost, it's not mandatory to change it, but you can do it for additional security.
|
# Database password. Considering it only listens on localhost, it's not mandatory to change it, but you can do it for additional security.
|
||||||
PACKMATE_DB_PASSWORD=K604YnL3G1hp2RDkCZNjGpxbyNpNHTRb
|
PACKMATE_DB_PASSWORD=K604YnL3G1hp2RDkCZNjGpxbyNpNHTRb
|
||||||
# Packmate version. Change it if you want to use a different version from the docker registry.
|
# 0xb00b5 team Packmate version. Change it if you want to use a different version from the docker registry.
|
||||||
BUILD_TAG=latest
|
BUILD_TAG=latest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
2
frontend
2
frontend
Submodule frontend updated: 8f23d97100...f90217cd6c
@@ -7,13 +7,20 @@ import org.springframework.context.event.EventListener;
|
|||||||
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
|
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
import org.springframework.security.core.userdetails.User;
|
import org.springframework.security.core.userdetails.User;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||||
import ru.serega6531.packmate.properties.PackmateProperties;
|
import ru.serega6531.packmate.properties.PackmateProperties;
|
||||||
|
import ru.serega6531.packmate.security.FakeAdminAuthFilter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@@ -22,31 +29,46 @@ public class SecurityConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public InMemoryUserDetailsManager userDetailsService(PackmateProperties properties, PasswordEncoder passwordEncoder) {
|
public InMemoryUserDetailsManager userDetailsService(PackmateProperties properties, PasswordEncoder passwordEncoder) {
|
||||||
UserDetails user = User.builder()
|
List<UserDetails> users = new ArrayList<>();
|
||||||
|
|
||||||
|
users.add(User.builder()
|
||||||
.username(properties.web().accountLogin())
|
.username(properties.web().accountLogin())
|
||||||
.password(passwordEncoder.encode(properties.web().accountPassword()))
|
.password(passwordEncoder.encode(properties.web().accountPassword()))
|
||||||
.roles("USER")
|
.roles("USER")
|
||||||
.build();
|
.build());
|
||||||
|
|
||||||
return new InMemoryUserDetailsManager(user);
|
Optional.ofNullable(properties.web().fakeAdmin())
|
||||||
|
.filter(PackmateProperties.FakeAdminProperties::enabled)
|
||||||
|
.ifPresent(fakeAdmin -> users.add(User.builder()
|
||||||
|
.username("admin")
|
||||||
|
.password(passwordEncoder.encode("admin"))
|
||||||
|
.roles("FAKE")
|
||||||
|
.build()));
|
||||||
|
|
||||||
|
return new InMemoryUserDetailsManager(users);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http, FakeAdminAuthFilter fakeAdminAuthFilter) throws Exception {
|
||||||
return http.csrf()
|
return http.csrf()
|
||||||
.disable()
|
.disable()
|
||||||
.authorizeHttpRequests((auth) ->
|
.authorizeHttpRequests((auth) ->
|
||||||
auth.requestMatchers("/site.webmanifest")
|
auth.requestMatchers("/site.webmanifest", "/fake-admin/**", "/fake/**", "/api/fake/**")
|
||||||
.permitAll()
|
.permitAll()
|
||||||
|
.requestMatchers("/api/**", "/ws/**")
|
||||||
|
.hasRole("USER")
|
||||||
.anyRequest()
|
.anyRequest()
|
||||||
.authenticated()
|
.authenticated()
|
||||||
)
|
)
|
||||||
|
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||||
|
.and()
|
||||||
.httpBasic()
|
.httpBasic()
|
||||||
.and()
|
.and()
|
||||||
.headers()
|
.headers()
|
||||||
.frameOptions()
|
.frameOptions()
|
||||||
.sameOrigin()
|
.sameOrigin()
|
||||||
.and()
|
.and()
|
||||||
|
.addFilterAfter(fakeAdminAuthFilter, BasicAuthenticationFilter.class)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package ru.serega6531.packmate.controller;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import ru.serega6531.packmate.security.FakeAdminResponder;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/fake-admin")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class FakeAdminController {
|
||||||
|
|
||||||
|
private final FakeAdminResponder responder;
|
||||||
|
|
||||||
|
@GetMapping(value = "/fun", produces = MediaType.TEXT_HTML_VALUE)
|
||||||
|
public ResponseEntity<String> fun() {
|
||||||
|
return ResponseEntity.ok(responder.funPageHtml());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/fakePackets", produces = MediaType.TEXT_HTML_VALUE)
|
||||||
|
public ResponseEntity<String> fakePackets() {
|
||||||
|
return ResponseEntity.ok(responder.fakePacketsHtml());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package ru.serega6531.packmate.controller;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import ru.serega6531.packmate.model.pojo.FakeServiceDto;
|
||||||
|
import ru.serega6531.packmate.service.ServicesService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/fake/")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class FakeFacadeController {
|
||||||
|
|
||||||
|
private final ServicesService servicesService;
|
||||||
|
|
||||||
|
@GetMapping("services")
|
||||||
|
public List<FakeServiceDto> getServices() {
|
||||||
|
return servicesService.findAllForFakeFacade();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package ru.serega6531.packmate.model.enums;
|
||||||
|
|
||||||
|
public enum FakeAdminMode {
|
||||||
|
FUN,
|
||||||
|
FAKE_PACKETS
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package ru.serega6531.packmate.model.pojo;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@Builder
|
||||||
|
public class FakeServiceDto {
|
||||||
|
int port;
|
||||||
|
String name;
|
||||||
|
String packetKind;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package ru.serega6531.packmate.properties;
|
|||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import ru.serega6531.packmate.model.enums.CaptureMode;
|
import ru.serega6531.packmate.model.enums.CaptureMode;
|
||||||
|
import ru.serega6531.packmate.model.enums.FakeAdminMode;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
|
||||||
@@ -20,7 +21,13 @@ public record PackmateProperties(
|
|||||||
|
|
||||||
public record WebProperties(
|
public record WebProperties(
|
||||||
String accountLogin,
|
String accountLogin,
|
||||||
String accountPassword
|
String accountPassword,
|
||||||
|
FakeAdminProperties fakeAdmin
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public record FakeAdminProperties(
|
||||||
|
boolean enabled,
|
||||||
|
FakeAdminMode mode
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public record TimeoutProperties(
|
public record TimeoutProperties(
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package ru.serega6531.packmate.security;
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
import ru.serega6531.packmate.model.enums.FakeAdminMode;
|
||||||
|
import ru.serega6531.packmate.properties.PackmateProperties;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Component
|
||||||
|
public class FakeAdminAuthFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final PackmateProperties properties;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldNotFilter(HttpServletRequest request) {
|
||||||
|
if (!isFakeEnabled()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = request.getRequestURI();
|
||||||
|
return path.startsWith("/fake-admin")
|
||||||
|
|| path.startsWith("/api/fake")
|
||||||
|
|| path.startsWith("/fake/")
|
||||||
|
|| path.equals("/favicon.ico");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
if (!isFakeEnabled()) {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
boolean isFakeAdmin = authentication != null && authentication.isAuthenticated()
|
||||||
|
&& authentication.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_FAKE"));
|
||||||
|
|
||||||
|
if (isFakeAdmin) {
|
||||||
|
FakeAdminMode mode = Optional.ofNullable(properties.web().fakeAdmin())
|
||||||
|
.map(PackmateProperties.FakeAdminProperties::mode)
|
||||||
|
.orElse(FakeAdminMode.FUN);
|
||||||
|
String target = "/fake-admin/" + resolvePath(mode);
|
||||||
|
log.info("Redirecting fake admin to {}", target);
|
||||||
|
response.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT);
|
||||||
|
response.setHeader(HttpHeaders.LOCATION, target);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isFakeEnabled() {
|
||||||
|
return Optional.ofNullable(properties.web().fakeAdmin())
|
||||||
|
.map(PackmateProperties.FakeAdminProperties::enabled)
|
||||||
|
.orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolvePath(FakeAdminMode mode) {
|
||||||
|
return switch (mode) {
|
||||||
|
case FAKE_PACKETS -> "fakePackets";
|
||||||
|
case FUN -> "fun";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,469 @@
|
|||||||
|
package ru.serega6531.packmate.security;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StreamUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class FakeAdminResponder {
|
||||||
|
|
||||||
|
private final ObjectMapper mapper = new ObjectMapper();
|
||||||
|
private final List<String> encodedImages;
|
||||||
|
|
||||||
|
public FakeAdminResponder() {
|
||||||
|
this.encodedImages = loadImages();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> loadImages() {
|
||||||
|
try {
|
||||||
|
Resource[] resources = new PathMatchingResourcePatternResolver()
|
||||||
|
.getResources("classpath:/static/fake/images/*");
|
||||||
|
|
||||||
|
List<String> images = Arrays.stream(resources)
|
||||||
|
.map(resource -> {
|
||||||
|
try {
|
||||||
|
String contentType = URLConnection.guessContentTypeFromName(resource.getFilename());
|
||||||
|
if (contentType == null) {
|
||||||
|
contentType = "image/jpeg";
|
||||||
|
}
|
||||||
|
byte[] raw = StreamUtils.copyToByteArray(resource.getInputStream());
|
||||||
|
return "data:%s;base64,%s".formatted(
|
||||||
|
contentType,
|
||||||
|
Base64.getEncoder().encodeToString(raw));
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("Failed to load fake admin image {}", resource.getFilename(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (images.isEmpty()) {
|
||||||
|
log.warn("No images found for fake admin fun mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
return images;
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("Failed to load fake admin images", e);
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String funPageHtml() {
|
||||||
|
String phrases = toJson(getFunPhrases());
|
||||||
|
String images = toJson(encodedImages);
|
||||||
|
return """
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>0xb00b5 team Packmate // fake funwall</title>
|
||||||
|
<style>
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=JetBrains+Mono:wght@500&display=swap');
|
||||||
|
:root {
|
||||||
|
--bg: #050512;
|
||||||
|
--accent: #59f3ff;
|
||||||
|
--accent-2: #ff5fd2;
|
||||||
|
--glass: rgba(9, 19, 45, 0.75);
|
||||||
|
--grid: rgba(89, 243, 255, 0.25);
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: radial-gradient(circle at 20% 20%, rgba(255, 95, 210, 0.18), transparent 25%), radial-gradient(circle at 80% 0%, rgba(89, 243, 255, 0.2), transparent 30%), linear-gradient(135deg, #04040d 0%, #0a1024 45%, #050512 100%);
|
||||||
|
color: #e9f7ff;
|
||||||
|
font-family: 'Press Start 2P', 'JetBrains Mono', monospace;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 40px 18px 60px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.grid-bg {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background-image: linear-gradient(var(--grid) 1px, transparent 1px), linear-gradient(90deg, var(--grid) 1px, transparent 1px);
|
||||||
|
background-size: 80px 80px;
|
||||||
|
mask-image: radial-gradient(circle at 50% 20%, rgba(0,0,0,.8), transparent 75%);
|
||||||
|
opacity: 0.5;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.shell {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
width: min(1040px, 100%);
|
||||||
|
background: var(--glass);
|
||||||
|
border: 1px solid rgba(89, 243, 255, 0.4);
|
||||||
|
box-shadow: 0 0 40px rgba(89, 243, 255, 0.25), 0 0 24px rgba(255, 95, 210, 0.2);
|
||||||
|
border-radius: 18px;
|
||||||
|
padding: 28px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
.shell::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 12px;
|
||||||
|
border: 1px dashed rgba(255, 95, 210, 0.25);
|
||||||
|
border-radius: 14px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin: 0 0 20px;
|
||||||
|
font-size: 18px;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
text-shadow: 0 0 8px rgba(89, 243, 255, 0.8);
|
||||||
|
}
|
||||||
|
.badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 8px 14px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid rgba(255, 95, 210, 0.35);
|
||||||
|
color: #ffcdf4;
|
||||||
|
box-shadow: inset 0 0 10px rgba(255, 95, 210, 0.35), 0 0 10px rgba(255, 95, 210, 0.35);
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 420px;
|
||||||
|
border-radius: 14px;
|
||||||
|
border: 1px solid rgba(89, 243, 255, 0.35);
|
||||||
|
background: radial-gradient(circle at 30% 30%, rgba(255, 95, 210, 0.08), transparent 40%), #0b122a;
|
||||||
|
box-shadow: inset 0 0 30px rgba(0,0,0,0.35), 0 0 18px rgba(89, 243, 255, 0.12);
|
||||||
|
}
|
||||||
|
.typed {
|
||||||
|
margin-top: 22px;
|
||||||
|
font-size: 14px;
|
||||||
|
min-height: 24px;
|
||||||
|
letter-spacing: 0.8px;
|
||||||
|
color: #aaf6ff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
#typed-text {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.cursor {
|
||||||
|
display: inline-block;
|
||||||
|
width: 12px;
|
||||||
|
background: #aaf6ff;
|
||||||
|
animation: blink 0.8s infinite;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
@keyframes blink {
|
||||||
|
0%, 50% { opacity: 1; }
|
||||||
|
51%, 100% { opacity: 0.2; }
|
||||||
|
}
|
||||||
|
.footer-note {
|
||||||
|
margin-top: 18px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: rgba(233, 247, 255, 0.65);
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="grid-bg"></div>
|
||||||
|
<div class="shell">
|
||||||
|
<h1>0xb00b5 team Packmate | admin:admin illusion</h1>
|
||||||
|
<div class="badge">mode: FUN // @danosito</div>
|
||||||
|
<canvas id="snap-canvas"></canvas>
|
||||||
|
<div class="typed">
|
||||||
|
<span id="typed-text"></span><span class="cursor"></span>
|
||||||
|
</div>
|
||||||
|
<div class="footer-note">Real packets stay locked. This is your glitch gallery.</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
const phrases = %s;
|
||||||
|
const images = %s;
|
||||||
|
const canvas = document.getElementById('snap-canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
const textEl = document.getElementById('typed-text');
|
||||||
|
|
||||||
|
const pick = (list) => list[Math.floor(Math.random() * list.length)];
|
||||||
|
|
||||||
|
function renderImage() {
|
||||||
|
const chosen = (images && images.length) ? pick(images) : '';
|
||||||
|
if (!chosen) {
|
||||||
|
ctx.fillStyle = '#0b122a';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => {
|
||||||
|
const maxW = 960;
|
||||||
|
const scale = Math.min(maxW / img.width, 1);
|
||||||
|
const w = img.width * scale;
|
||||||
|
const h = img.height * scale;
|
||||||
|
canvas.width = w;
|
||||||
|
canvas.height = h;
|
||||||
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
ctx.drawImage(img, 0, 0, w, h);
|
||||||
|
setTimeout(() => snapAway(), 5000);
|
||||||
|
};
|
||||||
|
img.src = chosen;
|
||||||
|
}
|
||||||
|
|
||||||
|
function snapAway() {
|
||||||
|
let ticks = 0;
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
for (let i = 0; i < 48; i++) {
|
||||||
|
const x = Math.random() * canvas.width;
|
||||||
|
const y = Math.random() * canvas.height;
|
||||||
|
const size = 4 + Math.random() * 10;
|
||||||
|
ctx.clearRect(x, y, size, size);
|
||||||
|
}
|
||||||
|
ticks++;
|
||||||
|
if (ticks > 80) {
|
||||||
|
clearInterval(interval);
|
||||||
|
setTimeout(renderImage, 400);
|
||||||
|
}
|
||||||
|
}, 28);
|
||||||
|
}
|
||||||
|
|
||||||
|
function typewriter() {
|
||||||
|
const phrase = pick(phrases);
|
||||||
|
textEl.textContent = '';
|
||||||
|
let i = 0;
|
||||||
|
const typeDelay = phrase.length ? 5000 / phrase.length : 120;
|
||||||
|
|
||||||
|
const typeInterval = setInterval(() => {
|
||||||
|
textEl.textContent += phrase.charAt(i);
|
||||||
|
i++;
|
||||||
|
if (i >= phrase.length) {
|
||||||
|
clearInterval(typeInterval);
|
||||||
|
setTimeout(() => erase(phrase), 3000);
|
||||||
|
}
|
||||||
|
}, typeDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
function erase(phrase) {
|
||||||
|
const eraseDelay = phrase.length ? 2000 / phrase.length : 80;
|
||||||
|
const eraser = setInterval(() => {
|
||||||
|
textEl.textContent = textEl.textContent.slice(0, -1);
|
||||||
|
if (!textEl.textContent.length) {
|
||||||
|
clearInterval(eraser);
|
||||||
|
setTimeout(typewriter, 200);
|
||||||
|
}
|
||||||
|
}, eraseDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderImage();
|
||||||
|
typewriter();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""".formatted(phrases, images);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String fakePacketsHtml() {
|
||||||
|
return """
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>0xb00b5 team Packmate // fake packets</title>
|
||||||
|
<style>
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=JetBrains+Mono:wght@500&display=swap');
|
||||||
|
:root {
|
||||||
|
--bg: #04040f;
|
||||||
|
--accent: #ff5fd2;
|
||||||
|
--accent-2: #59f3ff;
|
||||||
|
--glass: rgba(8, 12, 30, 0.85);
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: linear-gradient(160deg, #04040f 0%, #06081a 50%, #0b1535 100%);
|
||||||
|
color: #e9f7ff;
|
||||||
|
font-family: 'Press Start 2P', 'JetBrains Mono', monospace;
|
||||||
|
padding: 34px 18px 60px;
|
||||||
|
}
|
||||||
|
.matrix {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
.banner {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid rgba(89, 243, 255, 0.35);
|
||||||
|
box-shadow: 0 0 12px rgba(89, 243, 255, 0.25);
|
||||||
|
background: rgba(6, 18, 40, 0.8);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
background: var(--glass);
|
||||||
|
border: 1px solid rgba(255, 95, 210, 0.35);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
box-shadow: 0 0 18px rgba(255, 95, 210, 0.18);
|
||||||
|
}
|
||||||
|
.card h3 {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #ffb3f1;
|
||||||
|
}
|
||||||
|
.card .meta {
|
||||||
|
font-size: 10px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
color: rgba(233, 247, 255, 0.8);
|
||||||
|
}
|
||||||
|
.storm {
|
||||||
|
background: rgba(6, 12, 28, 0.9);
|
||||||
|
border-radius: 16px;
|
||||||
|
border: 1px solid rgba(89, 243, 255, 0.35);
|
||||||
|
box-shadow: inset 0 0 24px rgba(0,0,0,0.4), 0 0 14px rgba(89, 243, 255, 0.2);
|
||||||
|
padding: 16px;
|
||||||
|
height: 320px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.packet-line {
|
||||||
|
font-size: 11px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
color: #c8f7ff;
|
||||||
|
padding: 4px 0;
|
||||||
|
border-bottom: 1px dashed rgba(255, 95, 210, 0.15);
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.85;
|
||||||
|
animation: flicker 1.5s infinite;
|
||||||
|
}
|
||||||
|
.packet-line:nth-child(2n) { color: #aee3ff; }
|
||||||
|
.packet-line:nth-child(3n) { color: #ffb3f1; }
|
||||||
|
.packet-line.ghosted { opacity: 0.3; }
|
||||||
|
@keyframes flicker {
|
||||||
|
0% { text-shadow: 0 0 6px rgba(89, 243, 255, 0.35); }
|
||||||
|
50% { text-shadow: 0 0 12px rgba(255, 95, 210, 0.3); }
|
||||||
|
100% { text-shadow: 0 0 6px rgba(89, 243, 255, 0.35); }
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
margin-top: 16px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: rgba(233, 247, 255, 0.6);
|
||||||
|
}
|
||||||
|
.mute {
|
||||||
|
opacity: 0.02;
|
||||||
|
font-size: 10px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="banner">0xb00b5 team Packmate // decoy panel</div>
|
||||||
|
<div class="matrix" id="service-grid"></div>
|
||||||
|
<div class="storm" id="packet-feed"></div>
|
||||||
|
<div class="footer">admin:admin only gets noise. Real API stays sealed.</div>
|
||||||
|
<div class="mute">You're stupid:)</div>
|
||||||
|
<script>
|
||||||
|
const serviceGrid = document.getElementById('service-grid');
|
||||||
|
const packetFeed = document.getElementById('packet-feed');
|
||||||
|
let packetId = Math.floor(Math.random() * 100000) + 1;
|
||||||
|
let services = [];
|
||||||
|
|
||||||
|
fetch('/api/fake/services')
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
services = data;
|
||||||
|
renderServices();
|
||||||
|
startStorm();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
services = [{ name: 'ghost', port: 0, packetKind: 'tcp' }];
|
||||||
|
renderServices();
|
||||||
|
startStorm();
|
||||||
|
});
|
||||||
|
|
||||||
|
function renderServices() {
|
||||||
|
serviceGrid.innerHTML = '';
|
||||||
|
services.forEach(svc => {
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'card';
|
||||||
|
card.innerHTML = `
|
||||||
|
<h3>${svc.name} #${svc.port}</h3>
|
||||||
|
<div class="meta">packets: ${svc.packetKind || 'tcp'} stream</div>
|
||||||
|
<div class="meta">interface: locked</div>
|
||||||
|
`;
|
||||||
|
serviceGrid.appendChild(card);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function spawnPacket() {
|
||||||
|
if (!services.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const svc = services[Math.floor(Math.random() * services.length)];
|
||||||
|
const line = document.createElement('div');
|
||||||
|
line.className = 'packet-line';
|
||||||
|
const protocol = (svc.packetKind || 'tcp').toUpperCase();
|
||||||
|
line.textContent = `#${packetId} ${protocol} // :${svc.port} ${svc.name} // payload ${Math.floor(Math.random() * 1800)}b`;
|
||||||
|
packetId++;
|
||||||
|
|
||||||
|
packetFeed.prepend(line);
|
||||||
|
while (packetFeed.children.length > 140) {
|
||||||
|
packetFeed.removeChild(packetFeed.lastChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => line.classList.add('ghosted'), 1200);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startStorm() {
|
||||||
|
setInterval(spawnPacket, 50);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getFunPhrases() {
|
||||||
|
return List.of(
|
||||||
|
"Here's the flag. Are you ready? here it goes... Wait, no.",
|
||||||
|
"Wanna see the flag? send yours to @danosito:)",
|
||||||
|
"Hey, why are you here? go pentest our services",
|
||||||
|
"Hmmm i think <script>alert(\"You're stupid\")</script> might work..",
|
||||||
|
"Bip, boop, here was packet but codex ate it",
|
||||||
|
"Our LLM tockens ran out. Maybe you could give us some:)?",
|
||||||
|
":(){ :|:& };:",
|
||||||
|
"i think creds are admin:admin but i'm not sure...",
|
||||||
|
"Try eternalBlue, i think it would work",
|
||||||
|
"I think i defended this page well enough, here is flag: LLMDELETEDTHEFLAG=",
|
||||||
|
"Go open ida pro and reverse this text",
|
||||||
|
"I would give you our flags for free, but you are a bad person:(",
|
||||||
|
"b00b5 is not a fresh meat:(",
|
||||||
|
"marcus, send your packmate credits pls",
|
||||||
|
"Marcus, fuck off",
|
||||||
|
"<a href=\\"https://youtu.be/rrw-Pv3rc0E?si=-ZQmhZVxh4HF6luD\\">Your special guide to get flag!</a>"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toJson(List<String> data) {
|
||||||
|
try {
|
||||||
|
return mapper.writeValueAsString(data);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.warn("Failed to convert data to json for fake admin", e);
|
||||||
|
return "[]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import ru.serega6531.packmate.model.pojo.ServiceCreateDto;
|
|||||||
import ru.serega6531.packmate.model.pojo.ServiceDto;
|
import ru.serega6531.packmate.model.pojo.ServiceDto;
|
||||||
import ru.serega6531.packmate.model.pojo.ServiceUpdateDto;
|
import ru.serega6531.packmate.model.pojo.ServiceUpdateDto;
|
||||||
import ru.serega6531.packmate.model.pojo.SubscriptionMessage;
|
import ru.serega6531.packmate.model.pojo.SubscriptionMessage;
|
||||||
|
import ru.serega6531.packmate.model.pojo.FakeServiceDto;
|
||||||
import ru.serega6531.packmate.repository.ServiceRepository;
|
import ru.serega6531.packmate.repository.ServiceRepository;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
@@ -79,6 +80,17 @@ public class ServicesService {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<FakeServiceDto> findAllForFakeFacade() {
|
||||||
|
return services.values()
|
||||||
|
.stream()
|
||||||
|
.map(s -> FakeServiceDto.builder()
|
||||||
|
.port(s.getPort())
|
||||||
|
.name(s.getName())
|
||||||
|
.packetKind(s.isHttp() || s.isParseWebSockets() ? "tcp/http" : "tcp")
|
||||||
|
.build())
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteByPort(int port) {
|
public void deleteByPort(int port) {
|
||||||
log.info("Removed service at port {}", port);
|
log.info("Removed service at port {}", port);
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ packmate:
|
|||||||
web:
|
web:
|
||||||
account-login: BinaryBears
|
account-login: BinaryBears
|
||||||
account-password: 123456
|
account-password: 123456
|
||||||
|
fake-admin:
|
||||||
|
enabled: true
|
||||||
|
mode: fun # fun, fake_packets
|
||||||
timeout:
|
timeout:
|
||||||
udp-stream-timeout: 20 # seconds
|
udp-stream-timeout: 20 # seconds
|
||||||
tcp-stream-timeout: 40 # seconds
|
tcp-stream-timeout: 40 # seconds
|
||||||
|
|||||||
BIN
src/main/resources/static/fake/images/adminAdminMeme.jpg
Normal file
BIN
src/main/resources/static/fake/images/adminAdminMeme.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
src/main/resources/static/fake/images/anotherAdminAdminMeme.jpg
Normal file
BIN
src/main/resources/static/fake/images/anotherAdminAdminMeme.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
src/main/resources/static/fake/images/pudge.jpg
Normal file
BIN
src/main/resources/static/fake/images/pudge.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
src/main/resources/static/fake/images/sorokin.jpg
Normal file
BIN
src/main/resources/static/fake/images/sorokin.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
BIN
src/main/resources/static/fake/images/typicalInfosec.jpg
Normal file
BIN
src/main/resources/static/fake/images/typicalInfosec.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
Reference in New Issue
Block a user