Oray PgyBox X5 Pro Router firmware 7.6.1 allows an authenticated caller to inject strongSwan connection options through secproto in the retained ipsec_policy_set API. A line feed in that field reaches /etc/ipsec.conf and can set leftupdown; when a reachable IPsec peer establishes the CHILD_SA, the router runs the supplied command as root.
/www/cgi-bin/oraybox checks _pwd and applies a module's shell_inject_checker to the named request fields. The 7.6.1 decompilation of /usr/lib/lua/oraybox/api/ipsec_policy_set.lua lists checks for the connection name, addresses, authentication values and algorithms, but none for secproto. In index(), the value is assigned to context_table.secproto. The ipsec parameter has a separate meaning: if it is supplied, this early branch returns before the connection file is rebuilt. The injection request must leave it out.
if context_table.ipsec ~= nil then
ipsec_oper_uci(op, conn)
if context_table.ipsec == "no" then
secure_exec.exec("%s stop > /dev/null 2>&1", ipsec_bin)
elseif context_table.ipsec == "yes" then
secure_exec.exec("%s restart > /dev/null 2>&1", ipsec_bin)
end
secure_exec.exec("/etc/init.d/orayboxvpn_s restart > /dev/null 2>&1")
return {code = ret_code}
end
For advance=yes, ipsec_oper_uci() only compares secproto with esp or ah to remove the other algorithm option. An unrecognized value still reaches the final uci:set. This is the relevant 7.6.1 excerpt:
if context_table.advance == "yes" then
if context_table.secproto == "esp" then
uci:delete(strongswan_config, conn, "ah")
elseif context_table.secproto == "ah" then
uci:delete(strongswan_config, conn, "esp")
end
uci:set(strongswan_config, conn, "secproto", context_table.secproto)
end
The same module formats each included UCI option using "\t%s=%s\n"; its value is inserted into that format without escaping. For an enabled connection, the following part of ipsec_uci_to_strongswan_conf() includes secproto in the output. ipsec_update_conf() opens /etc/ipsec.conf with mode w, and ipsec_op() restarts /usr/sbin/ipsec.
conn_options = uci:get_all(strongswan_config, section)
if conn_options.status ~= "no" then
ret_str = ret_str .. string.format(conn_definition_head_format, section)
for i, v in pairs(conn_options) do
if i ~= "psk" and i ~= "xauth" and i ~= "conn" and i ~= "interfaces" and i ~= ".name" and i ~= ".anonymous" and i ~= ".type" and i ~= "ikecipher" and i ~= "ikehash" and i ~= "espcipher" and i ~= "esphash" and i ~= "dhgroup" and i ~= "advance" and v then
ret_str = ret_str .. string.format(body_format, i, v)
end
end
end
With the PoC value, the secproto record ends at the line feed and the next line becomes a separate connection setting:
secproto=esp
leftupdown="/bin/busybox id > /tmp/ipsec_lf_uid.txt"
In strongSwan 5.9.2, leftupdown is a recognized connection keyword, while secproto is an unknown, nonfatal keyword. The firmware includes libstrongswan-updown.so and enables it in /etc/strongswan.d/charon/updown.conf. The updown plugin invokes the configured command when the CHILD_SA comes up; the 5.9.2 listener starts it through a shell.
Use the management system password and a Linux host running strongSwan that can exchange IKE traffic with the router. The commands below use 10.168.1.1 for the router, 10.168.1.10 for the peer, and 10.254.77.1/32 on the peer's loopback interface; adjust them to the actual network. A router shell is used to save the original files and read the result. The configuration request goes through the management API with _pwd.
First, on the router shell, save the files that this API rewrites. Use a fresh directory so a previous snapshot cannot be overwritten: