[4285] in BarnOwl Developers
Re: BarnOwl 1.10: a zephyr's CC: line is used as a log file name
daemon@ATHENA.MIT.EDU (Daniel Clark)
Sun Nov 1 01:17:42 2026
In-Reply-To: <CAKObCapg0Lqb6Hw9k6Hdo++p04Z9ewr0KGyTixoPknSb9VbPCA@mail.gmail.com>
From: Daniel Clark <djbclark@gmail.com>
Date: Sun, 1 Nov 2026 01:15:22 -0400
To: Jason Gross <jgross@mit.edu>
Cc: barnowl-dev@mit.edu
--000000000000676cb1065f301eb9
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
Looks like my update email and your email crossed the wires in transit.
Re: using barnowl, I haven't been around much since back then, so if there
is some new Zephyr client to use, LMK. I tried getting. some of the others
I saw mentioned set up - Zabbix I think? - but that one has 404s on all the
auth pages. Some other one I forget the name of now faired little better.
On Sun, Nov 1, 2026 at 1:10=E2=80=AFAM Jason Gross <jgross@mit.edu> wrote:
> I believe this issue is not present in the Perl-based logging code
> <https://github.com/barnowl/barnowl/pull/54>, which was merged 6 months
> after the 1.10 release, 9 years ago. If anyone still uses BarnOwl, I gues=
s
> it may be worth doing another release. (There has only been one commit in
> the past seven years.)
>
> On Sat, Oct 31, 2026 at 9:50=E2=80=AFPM Daniel Clark <djbclark@gmail.com>=
wrote:
>
>> Hello BarnOwl developers,
>>
>> I found a security bug in BarnOwl 1.10 logging: a zephyr's CC: line is
>> used as a log file name without the checks that the sender's own name
>> gets. Any zephyr sender can make the receiving client append a message
>> to any file the user can write, or create it. I have also reported it
>> to security@debian.org (2026-11-01). I am sending it to you so that it
>> is fixed upstream and so that you can decide how to tell BarnOwl users.
>>
>> WHAT HAPPENS
>>
>> owl_log_incoming (logging.c) checks the sender's name before using it as
>> a file name under logpath: 1 to 35 printable ASCII characters, first
>> character alphanumeric, no '/', not "." or "..". For a personal zephyr
>> it then also logs the message to every name on its CC list
>> (owl_message_get_cc_without_recipient: the first body line, if it starts
>> with "CC:"). Those names come from the sender and get none of those
>> checks: g_build_filename(logpath, short_zuser(name), NULL), then
>> fopen(..., "a") on the logger thread. owl_log_outgoing has the same
>> loop, for the user's own CC lines.
>>
>> IMPACT
>>
>> A personal zephyr whose body starts with the line "CC:
>> ../../.ssh/authorized_keys", followed by a line holding an SSH public
>> key, makes the receiving client append the whole logged entry (header
>> lines, then the body) to ~/.ssh/authorized_keys. sshd ignores the header
>> lines and accepts the key line. Appending to ~/.bashrc or creating
>> ~/.ssh/rc gives code execution the same way. It needs personal logging
>> on ("set logging on", loggingdirection "in" or "both", the documented
>> setup), no authentication and no action by the user.
>>
>> AFFECTED
>>
>> BarnOwl 1.10 (tag barnowl-1.10), tested as Debian barnowl 1.10-2+b9 on
>> Debian 13. I did not check other versions; the code looks old, so
>> earlier ones are probably affected too.
>>
>> REPRODUCTION (scratch directory; nothing outside it is touched)
>>
>> Use a scratch HOME with the config dir /tmp/T/home/.owl and logpath
>> /tmp/T/zlog/people. Put this in /tmp/T/home/.owl/startup:
>>
>> set logging on
>> set loggingdirection both
>> set logpath /tmp/T/zlog/people
>> perl BarnOwl::queue_message(BarnOwl::Message->new(
>> type=3D>'zephyr', direction=3D>'in', class=3D>'message',
>> instance=3D>'personal', sender=3D>'eve@ATHENA.MIT.EDU',
>> recipient=3D>'user@ATHENA.MIT.EDU', private=3D>1,
>> isprivate=3D>'true', realm=3D>'ATHENA.MIT.EDU', opcode=3D>'',
>> auth=3D>'NO', host=3D>'h', hostname=3D>'h', zsig=3D>'z',
>> body=3D>"CC: ../../victim\nssh-ed25519 AAAAFAKEKEY attacker\n",
>> time=3D>scalar(localtime), unix_time=3D>time()))
>> perl our $t =3D BarnOwl::Timer->new({after=3D>4,
>> cb=3D>sub{BarnOwl::command("quit")}})
>>
>> Then run: HOME=3D/tmp/T/home script -qfc 'barnowl -n -s /tmp/T/home/.owl=
'
>> /dev/null. With 1.10-2+b9, /tmp/T/victim exists (outside logpath) and
>> holds the logged entry. With the patch below it is not created, and a CC
>> to an ordinary user name is still logged. A real zephyr goes through the
>> same function.
>>
>> TEMPORARY WORKAROUND FOR USERS
>>
>> Until a fixed BarnOwl is installed, users can stop the bug by turning
>> personal-zephyr logging off:
>>
>> - Put "set logging off" in ~/.owl/startup (replacing "set logging on")
>> and run ":set logging off" in a running client. Personal zephyrs are
>> then not logged, in either direction. Class logging ("set
>> classlogging on") is unaffected, because class names get the sender-
>> name checks and have no CC handling.
>> - "set loggingdirection out" should also stop incoming personals from
>> being logged while keeping a log of what you send. I have not tested
>> that variant; the "logging off" one is based on reading the code and
>> on how the logging variables gate the logger, and I did not run it
>> against the exploit.
>> - Weaker, partial measures: make ~/.ssh/authorized_keys, ~/.bashrc and
>> ~/.profile read-only (chmod 400, or chattr +i as root). This does
>> not help against files that do not exist yet, such as ~/.ssh/rc or
>> ~/.bash_login, and any file the user can write stays reachable.
>> - Run the client in a sandbox (bwrap, firejail, a separate
>> unprivileged account) that can write only ~/.owl and the log
>> directory. I have not tried this.
>>
>> FIX
>>
>> Apply the sender-name rules to every CC name, in both loops. This patch
>> against 1.10 does that (it also works as a Debian quilt patch,
>> debian/patches/0001-log-validate-cc-names.patch). I tested the name
>> function on its own, and the behaviour above with a Debian 1.10-2 build
>> plus this patch. I did not review the jabber and aim branches of the
>> logger, which also build file names from message data.
>>
>> --- a/logging.c
>> +++ b/logging.c
>> @@ -205,6 +205,26 @@
>> g_free(buffer);
>> }
>>
>> +/* A name taken from a message is used as a file name under the log
>> directory only if
>> + * it could be a user name: 1-35 printable ASCII characters, the
>> first alphanumeric,
>> + * no '/', and not "." or "..". The CC: line of a zephyr is written
>> by its sender, so
>> + * without this a CC of "../../.ssh/authorized_keys" makes us append
>> the message to
>> + * that file (CVE pending). */
>> +static int owl_log_name_is_safe(const char *name)
>> +{
>> + size_t i, len;
>> +
>> + if (!name) return 0;
>> + len =3D strlen(name);
>> + if (len < 1 || len > 35) return 0;
>> + if (!g_ascii_isalnum(name[0])) return 0;
>> + for (i =3D 0; i < len; i++) {
>> + if (name[i] < '!' || name[i] >=3D '~' || name[i] =3D=3D '/') return=
0;
>> + }
>> + if (!strcmp(name, ".") || !strcmp(name, "..")) return 0;
>> + return 1;
>> +}
>> +
>> void owl_log_outgoing(const owl_message *m)
>> {
>> char *filename, *logpath;
>> @@ -220,10 +240,12 @@
>> cc =3D owl_message_get_cc_without_recipient(m);
>> while (cc !=3D NULL) {
>> temp =3D short_zuser(cc->data);
>> - filename =3D g_build_filename(logpath, temp, NULL);
>> - owl_log_append(m, filename);
>> + if (owl_log_name_is_safe(temp)) {
>> + filename =3D g_build_filename(logpath, temp, NULL);
>> + owl_log_append(m, filename);
>> + g_free(filename);
>> + }
>>
>> - g_free(filename);
>> g_free(temp);
>> g_free(cc->data);
>> cc =3D g_list_delete_link(cc, cc);
>> @@ -410,7 +432,7 @@
>> cc =3D owl_message_get_cc_without_recipient(m);
>> while (cc !=3D NULL) {
>> temp =3D short_zuser(cc->data);
>> - if (strcasecmp(temp, frombuff) !=3D 0) {
>> + if (strcasecmp(temp, frombuff) !=3D 0 && owl_log_name_is_safe(tem=
p))
>> {
>> filename =3D g_build_filename(logpath, temp, NULL);
>> owl_log_append(m, filename);
>> g_free(filename);
>>
>> I can send a pull request if you prefer that, or answer questions. A CVE
>> has not been requested; Debian security may assign one, and you may want
>> to coordinate with them on the timing of any announcement. If you need
>> more detail, reply to me directly.
>>
>> Daniel J. B. Clark
>> djbclark@gmail.com
>> djbclark@mit.edu
>>
>
--000000000000676cb1065f301eb9
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div class=3D"gmail_default" style=3D"font-family:arial,sa=
ns-serif">Looks like my update email and your email crossed the wires in tr=
ansit.</div><div class=3D"gmail_default" style=3D"font-family:arial,sans-se=
rif"><br></div><div class=3D"gmail_default" style=3D"font-family:arial,sans=
-serif">Re: using barnowl, I haven't been around much since back then, =
so if there is some new Zephyr client to use, LMK. I tried getting. some of=
the others I saw mentioned set up - Zabbix I think? - but that one has 404=
s on all the auth pages. Some other one I forget the name of now faired lit=
tle better.</div></div><br><div class=3D"gmail_quote gmail_quote_container"=
><div dir=3D"ltr" class=3D"gmail_attr">On Sun, Nov 1, 2026 at 1:10=E2=80=AF=
AM Jason Gross <<a href=3D"mailto:jgross@mit.edu">jgross@mit.edu</a>>=
wrote:<br></div><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px =
0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir=
=3D"ltr">I believe this issue is not present in the <a href=3D"https://gith=
ub.com/barnowl/barnowl/pull/54" target=3D"_blank">Perl-based logging code</=
a>, which was merged 6 months after the 1.10 release, 9 years ago. If anyon=
e still uses BarnOwl, I guess it may be worth doing another release. (There=
has only been one commit in the past seven years.)</div><br><div class=3D"=
gmail_quote"><div dir=3D"ltr" class=3D"gmail_attr">On Sat, Oct 31, 2026 at =
9:50=E2=80=AFPM Daniel Clark <<a href=3D"mailto:djbclark@gmail.com" targ=
et=3D"_blank">djbclark@gmail.com</a>> wrote:<br></div><blockquote class=
=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rg=
b(204,204,204);padding-left:1ex">Hello BarnOwl developers,<br>
<br>
I found a security bug in BarnOwl 1.10 logging: a zephyr's CC: line is<=
br>
used as a log file name without the checks that the sender's own name<b=
r>
gets. Any zephyr sender can make the receiving client append a message<br>
to any file the user can write, or create it. I have also reported it<br>
to <a href=3D"mailto:security@debian.org" target=3D"_blank">security@debian=
.org</a> (2026-11-01). I am sending it to you so that it<br>
is fixed upstream and so that you can decide how to tell BarnOwl users.<br>
<br>
WHAT HAPPENS<br>
<br>
owl_log_incoming (logging.c) checks the sender's name before using it a=
s<br>
a file name under logpath: 1 to 35 printable ASCII characters, first<br>
character alphanumeric, no '/', not "." or ".."=
. For a personal zephyr<br>
it then also logs the message to every name on its CC list<br>
(owl_message_get_cc_without_recipient: the first body line, if it starts<br=
>
with "CC:"). Those names come from the sender and get none of tho=
se<br>
checks: g_build_filename(logpath, short_zuser(name), NULL), then<br>
fopen(..., "a") on the logger thread. owl_log_outgoing has the sa=
me<br>
loop, for the user's own CC lines.<br>
<br>
IMPACT<br>
<br>
A personal zephyr whose body starts with the line "CC:<br>
../../.ssh/authorized_keys", followed by a line holding an SSH public<=
br>
key, makes the receiving client append the whole logged entry (header<br>
lines, then the body) to ~/.ssh/authorized_keys. sshd ignores the header<br=
>
lines and accepts the key line. Appending to ~/.bashrc or creating<br>
~/.ssh/rc gives code execution the same way. It needs personal logging<br>
on ("set logging on", loggingdirection "in" or "bo=
th", the documented<br>
setup), no authentication and no action by the user.<br>
<br>
AFFECTED<br>
<br>
BarnOwl 1.10 (tag barnowl-1.10), tested as Debian barnowl 1.10-2+b9 on<br>
Debian 13. I did not check other versions; the code looks old, so<br>
earlier ones are probably affected too.<br>
<br>
REPRODUCTION (scratch directory; nothing outside it is touched)<br>
<br>
Use a scratch HOME with the config dir /tmp/T/home/.owl and logpath<br>
/tmp/T/zlog/people. Put this in /tmp/T/home/.owl/startup:<br>
<br>
=C2=A0 =C2=A0 set logging on<br>
=C2=A0 =C2=A0 set loggingdirection both<br>
=C2=A0 =C2=A0 set logpath /tmp/T/zlog/people<br>
=C2=A0 =C2=A0 perl BarnOwl::queue_message(BarnOwl::Message->new(<br>
=C2=A0 =C2=A0 =C2=A0 type=3D>'zephyr', direction=3D>'in&#=
39;, class=3D>'message',<br>
=C2=A0 =C2=A0 =C2=A0 instance=3D>'personal', sender=3D>'<=
a href=3D"mailto:eve@ATHENA.MIT.EDU" target=3D"_blank">eve@ATHENA.MIT.EDU</=
a>',<br>
=C2=A0 =C2=A0 =C2=A0 recipient=3D>'<a href=3D"mailto:user@ATHENA.MIT=
.EDU" target=3D"_blank">user@ATHENA.MIT.EDU</a>', private=3D>1,<br>
=C2=A0 =C2=A0 =C2=A0 isprivate=3D>'true', realm=3D>'<a hr=
ef=3D"http://ATHENA.MIT.EDU" rel=3D"noreferrer" target=3D"_blank">ATHENA.MI=
T.EDU</a>', opcode=3D>'',<br>
=C2=A0 =C2=A0 =C2=A0 auth=3D>'NO', host=3D>'h', hostn=
ame=3D>'h', zsig=3D>'z',<br>
=C2=A0 =C2=A0 =C2=A0 body=3D>"CC: ../../victim\nssh-ed25519 AAAAFAK=
EKEY attacker\n",<br>
=C2=A0 =C2=A0 =C2=A0 time=3D>scalar(localtime), unix_time=3D>time()))=
<br>
=C2=A0 =C2=A0 perl our $t =3D BarnOwl::Timer->new({after=3D>4,<br>
=C2=A0 =C2=A0 =C2=A0 cb=3D>sub{BarnOwl::command("quit")}})<br>
<br>
Then run: HOME=3D/tmp/T/home script -qfc 'barnowl -n -s /tmp/T/home/.ow=
l'<br>
/dev/null. With 1.10-2+b9, /tmp/T/victim exists (outside logpath) and<br>
holds the logged entry. With the patch below it is not created, and a CC<br=
>
to an ordinary user name is still logged. A real zephyr goes through the<br=
>
same function.<br>
<br>
TEMPORARY WORKAROUND FOR USERS<br>
<br>
Until a fixed BarnOwl is installed, users can stop the bug by turning<br>
personal-zephyr logging off:<br>
<br>
=C2=A0 - Put "set logging off" in ~/.owl/startup (replacing "=
;set logging on")<br>
=C2=A0 =C2=A0 and run ":set logging off" in a running client. Per=
sonal zephyrs are<br>
=C2=A0 =C2=A0 then not logged, in either direction. Class logging ("se=
t<br>
=C2=A0 =C2=A0 classlogging on") is unaffected, because class names get=
the sender-<br>
=C2=A0 =C2=A0 name checks and have no CC handling.<br>
=C2=A0 - "set loggingdirection out" should also stop incoming per=
sonals from<br>
=C2=A0 =C2=A0 being logged while keeping a log of what you send. I have not=
tested<br>
=C2=A0 =C2=A0 that variant; the "logging off" one is based on rea=
ding the code and<br>
=C2=A0 =C2=A0 on how the logging variables gate the logger, and I did not r=
un it<br>
=C2=A0 =C2=A0 against the exploit.<br>
=C2=A0 - Weaker, partial measures: make ~/.ssh/authorized_keys, ~/.bashrc a=
nd<br>
=C2=A0 =C2=A0 ~/.profile read-only (chmod 400, or chattr +i as root). This =
does<br>
=C2=A0 =C2=A0 not help against files that do not exist yet, such as ~/.ssh/=
rc or<br>
=C2=A0 =C2=A0 ~/.bash_login, and any file the user can write stays reachabl=
e.<br>
=C2=A0 - Run the client in a sandbox (bwrap, firejail, a separate<br>
=C2=A0 =C2=A0 unprivileged account) that can write only ~/.owl and the log<=
br>
=C2=A0 =C2=A0 directory. I have not tried this.<br>
<br>
FIX<br>
<br>
Apply the sender-name rules to every CC name, in both loops. This patch<br>
against 1.10 does that (it also works as a Debian quilt patch,<br>
debian/patches/0001-log-validate-cc-names.patch). I tested the name<br>
function on its own, and the behaviour above with a Debian 1.10-2 build<br>
plus this patch. I did not review the jabber and aim branches of the<br>
logger, which also build file names from message data.<br>
<br>
--- a/logging.c<br>
+++ b/logging.c<br>
@@ -205,6 +205,26 @@<br>
=C2=A0 =C2=A0g_free(buffer);<br>
=C2=A0}<br>
<br>
+/* A name taken from a message is used as a file name under the log<br>
directory only if<br>
+ * it could be a user name: 1-35 printable ASCII characters, the<br>
first alphanumeric,<br>
+ * no '/', and not "." or "..". The CC: line o=
f a zephyr is written<br>
by its sender, so<br>
+ * without this a CC of "../../.ssh/authorized_keys" makes us ap=
pend<br>
the message to<br>
+ * that file (CVE pending). */<br>
+static int owl_log_name_is_safe(const char *name)<br>
+{<br>
+=C2=A0 size_t i, len;<br>
+<br>
+=C2=A0 if (!name) return 0;<br>
+=C2=A0 len =3D strlen(name);<br>
+=C2=A0 if (len < 1 || len > 35) return 0;<br>
+=C2=A0 if (!g_ascii_isalnum(name[0])) return 0;<br>
+=C2=A0 for (i =3D 0; i < len; i++) {<br>
+=C2=A0 =C2=A0 if (name[i] < '!' || name[i] >=3D '~' =
|| name[i] =3D=3D '/') return 0;<br>
+=C2=A0 }<br>
+=C2=A0 if (!strcmp(name, ".") || !strcmp(name, "..")) =
return 0;<br>
+=C2=A0 return 1;<br>
+}<br>
+<br>
=C2=A0void owl_log_outgoing(const owl_message *m)<br>
=C2=A0{<br>
=C2=A0 =C2=A0char *filename, *logpath;<br>
@@ -220,10 +240,12 @@<br>
=C2=A0 =C2=A0 =C2=A0cc =3D owl_message_get_cc_without_recipient(m);<br>
=C2=A0 =C2=A0 =C2=A0while (cc !=3D NULL) {<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0temp =3D short_zuser(cc->data);<br>
-=C2=A0 =C2=A0 =C2=A0 filename =3D g_build_filename(logpath, temp, NULL);<b=
r>
-=C2=A0 =C2=A0 =C2=A0 owl_log_append(m, filename);<br>
+=C2=A0 =C2=A0 =C2=A0 if (owl_log_name_is_safe(temp)) {<br>
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 filename =3D g_build_filename(logpath, temp, N=
ULL);<br>
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 owl_log_append(m, filename);<br>
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_free(filename);<br>
+=C2=A0 =C2=A0 =C2=A0 }<br>
<br>
-=C2=A0 =C2=A0 =C2=A0 g_free(filename);<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0g_free(temp);<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0g_free(cc->data);<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0cc =3D g_list_delete_link(cc, cc);<br>
@@ -410,7 +432,7 @@<br>
=C2=A0 =C2=A0 =C2=A0cc =3D owl_message_get_cc_without_recipient(m);<br>
=C2=A0 =C2=A0 =C2=A0while (cc !=3D NULL) {<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0temp =3D short_zuser(cc->data);<br>
-=C2=A0 =C2=A0 =C2=A0 if (strcasecmp(temp, frombuff) !=3D 0) {<br>
+=C2=A0 =C2=A0 =C2=A0 if (strcasecmp(temp, frombuff) !=3D 0 && owl_=
log_name_is_safe(temp)) {<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 filename =3D g_build_filename(logpath, temp, NU=
LL);<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0owl_log_append(m, filename);<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_free(filename);<br>
<br>
I can send a pull request if you prefer that, or answer questions. A CVE<br=
>
has not been requested; Debian security may assign one, and you may want<br=
>
to coordinate with them on the timing of any announcement. If you need<br>
more detail, reply to me directly.<br>
<br>
Daniel J. B. Clark<br>
<a href=3D"mailto:djbclark@gmail.com" target=3D"_blank">djbclark@gmail.com<=
/a><br>
<a href=3D"mailto:djbclark@mit.edu" target=3D"_blank">djbclark@mit.edu</a><=
br>
</blockquote></div>
</blockquote></div>
--000000000000676cb1065f301eb9--