java-topology/defects/irssi-0001/patch/irssi-0001-cwe312.md

3.8 KiB

UNDF: UNDF-2026-000001105

UNDF: (assigned later)

Target: irssi

MOAD: 0004 — CWE-312 Cleartext Storage of Sensitive Information

Severity: HIGH

File: src/core/rawlog.c + src/irc/core/irc-servers.c

Summary

irssi's rawlog facility records all outbound IRC commands verbatim to an in-memory ring buffer (default 200 lines) and optionally to a file on disk via /rawlog open <file>. Every command sent via irc_send_cmd_now() flows through rawlog_output(), including credentials sent during connection setup.

Two credential exposure paths exist:

Path 1 — Server password (PASS command)

src/irc/core/irc-servers.c:223:

cmd = g_strdup_printf("PASS %s", conn->password);
irc_send_cmd_now(server, cmd);

irc_send_cmd_nowirc_send_cmd_fullirc_server_send_datarawlog_output(server->rawlog, str->str) (irc-servers.c:747)

Result: << PASS secretpassword123 appears in the rawlog ring buffer and, if the user has run /rawlog open irc.log, in the log file on disk.

Path 2 — SASL PLAIN credentials (AUTHENTICATE command)

src/irc/core/sasl.csasl_step_complete():

g_string_append(resp, conn->sasl_username);
g_string_append_c(resp, '\0');
g_string_append(resp, conn->sasl_username);
g_string_append_c(resp, '\0');
g_string_append(resp, conn->sasl_password);
sasl_send_response(server, resp);  // → irc_send_cmdv(server, "AUTHENTICATE %.*s", ...)

sasl_send_response calls irc_send_cmdv which calls irc_send_cmd which goes through the same rawlog path.

Result: << AUTHENTICATE dXNlcm5hbWUAdXNlcm5hbWUAcGFzc3dvcmQ= (base64 of username\0username\0password) appears in rawlog. The base64 is trivially decoded: echo dXNlcm5hbWUAdXNlcm5hbWUAcGFzc3dvcmQ= | base64 -d.

Proxy password exposure (bonus)

src/irc/core/irc-servers.c:271:

cmd = g_strdup_printf("PASS %s", conn->proxy_password);
irc_send_cmd_now(server, cmd);

Same path — proxy PASS also logged.

Attack Scenario

  1. User runs /rawlog open ~/irc-debug.log to debug a connection issue.
  2. User reconnects to their IRC server (triggers PASS or SASL PLAIN).
  3. Log file now contains plaintext or trivially-decoded credentials.
  4. File may be exfiltrated via: shared home directories, backup systems, shell history leaks, or a compromised process with file-read access.

Even without /rawlog open, the in-memory ring buffer (200 lines) is accessible to Perl scripts via $server->{rawlog} — a malicious plugin or a plugin with an XSS/eval-equivalent vulnerability could read it.

Fix

Redact PASS and AUTHENTICATE commands before passing to rawlog:

// In rawlog_output() or in the send path, apply a credential denylist:

static char *redact_credential_command(const char *str) {
    if (g_ascii_strncasecmp(str, "PASS ", 5) == 0)
        return g_strdup("PASS ***");
    if (g_ascii_strncasecmp(str, "AUTHENTICATE ", 13) == 0)
        return g_strdup("AUTHENTICATE ***");
    return g_strdup(str);
}

Apply in rawlog_output() before storing/writing:

void rawlog_output(RAWLOG_REC *rawlog, const char *str) {
    char *safe = redact_credential_command(str);
    rawlog_add(rawlog, g_strdup_printf("<< %s", safe));
    g_free(safe);
}

Alternatively, pass a sensitive flag through the send path and suppress rawlog entirely for those commands (precedent: OpenSSH does not log USERAUTH_REQUEST payloads in its protocol trace).

Impact

  • Credentials written to disk if rawlog file is active (user opt-in but common during debugging)
  • Credentials in memory ring buffer accessible to all Perl plugins
  • SASL PLAIN especially dangerous: base64 is not encryption; any reader of the rawlog immediately has the plaintext password

References