CVE-2016-7553: Permission Issues (CWE-275)
value: The buf.pl script before 2.20 in Irssi before 0.8.20 uses weak permissions for the scrollbuffer dump file created between upgrades, which might allow local users to obtain sensitive information from private chat conversations by reading the file.
CWE description: Weaknesses in this category are related to improper assignment or handling of permissions.
CVSS v3 base score: 3.3
Published: 2017-02-27T22:59Z
Repository: https://github.com/irssi/scripts.irssi.org
Language: Perl
Files changed: scripts/buf.pl
Commit date: 2016-09-22 04:13:17 +0200
Commit message: Fix disclosure via filesystem

buf.pl restores the scrollbuffer between "/upgrade"s by writing the
contents to a file, and reading that after the new process was spawned.
Through that file, the contents of (private) chat conversations may leak to
other users.

Careful users with a limited umask (e.g. 077) are not affected by this bug.
However, most Linux systems default to a umask of 022, meaning that files
written without further restricting the permissions, are readable by any
user.

This patch sets a safer umask of 077 for the scrollbuffer dump, and will
remove the temporary file after use to further reduce the attack surface.
Additionally, it will remove any remaining temporary scrollbuffer file left
in place, like those written by previous versions of the script.

Diff: diff --git a/scripts/buf.pl b/scripts/buf.pl
index da50e82..6d907f1 100644
--- a/scripts/buf.pl
+++ b/scripts/buf.pl
@@ -3,22 +3,20 @@ use vars qw($VERSION %IRSSI);
 
 use Irssi qw(command signal_add signal_add_first active_win
              settings_get_str settings_get_bool channels windows
 	     settings_add_str settings_add_bool get_irssi_dir
 	     window_find_refnum signal_stop);
-$VERSION = '2.13';
+$VERSION = '2.20';
 %IRSSI = (
     authors	=> 'Juerd',
     contact	=> 'juerd@juerd.nl',
     name	=> 'Scroll buffer restorer',
     description	=> 'Saves the buffer for /upgrade, so that no information is lost',
     license	=> 'Public Domain',
     url		=> 'http://juerd.nl/irssi/',
-    changed	=> 'Mon May 13 19:41 CET 2002',
-    changes	=> 'Severe formatting bug removed * oops, I ' .
-                   'exposed Irssi to ircII foolishness * sorry ' .
-		   '** removed logging stuff (this is a fix)',
+    changed	=> 'Thu Sep 22 01:37 CEST 2016',
+    changes	=> 'Fixed file permissions (leaked everything via filesystem)',
     note1	=> 'This script HAS TO BE in your scripts/autorun!',
     note2	=> 'Perl support must be static or in startup',
 );
 
 # Q: How can I get a very smooth and clean upgrade?
@@ -37,13 +35,19 @@ $VERSION = '2.13';
 use Irssi::TextUI;
 use Data::Dumper;
 
 my %suppress;
 
+sub _filename { sprintf '%s/scrollbuffer', get_irssi_dir }
+
 sub upgrade {
-    open BUF, q{>}, sprintf('%s/scrollbuffer', get_irssi_dir) or die $!;
-    print BUF join("\0", map $_->{server}->{address} . $_->{name}, channels), "\n";
+    my $fn = _filename;
+    my $old_umask = umask 0077;
+    open my $fh, q{>}, $fn or die "open $fn: $!";
+    umask $old_umask;
+
+    print $fh join("\0", map $_->{server}->{address} . $_->{name}, channels), "\n";
     for my $window (windows) {
 	next unless defined $window;
 	next if $window->{name} eq 'status';
 	my $view = $window->view;
 	my $line = $view->get_lines;
@@ -55,40 +59,43 @@ sub upgrade {
 		$line = $line->next;
 		$lines++;
 		redo if defined $line;
 	    }
 	}
-	printf BUF "%s:%s\n%s", $window->{refnum}, $lines, $buf;
+	printf $fh "%s:%s\n%s", $window->{refnum}, $lines, $buf;
     }
-    close BUF;
+    close $fh;
     unlink sprintf("%s/sessionconfig", get_irssi_dir);
     command 'layout save';
     command 'save';
 }
 
 sub restore {
-    open BUF, q{<}, sprintf('%s/scrollbuffer', get_irssi_dir) or die $!;
-    my @suppress = split /\0/, <BUF>;
+    my $fn = _filename;
+    open my $fh, q{<}, $fn or die "open $fn: $!";
+    unlink $fn or warn "unlink $fn: $!";
+
+    my @suppress = split /\0/, readline $fh;
     if (settings_get_bool 'upgrade_suppress_join') {
 	chomp $suppress[-1];
 	@suppress{@suppress} = (2) x @suppress;
     }
     active_win->command('^window scroll off');
-    while (my $bla = <BUF>){
+    while (my $bla = readline $fh){
 	chomp $bla;
 	my ($refnum, $lines) = split /:/, $bla;
 	next unless $lines;
 	my $window = window_find_refnum $refnum;
 	unless (defined $window){
-	    <BUF> for 1..$lines;
+	    readline $fh for 1..$lines;
 	    next;
 	}
 	my $view = $window->view;
 	$view->remove_all_lines();
 	$view->redraw();
 	my $buf = '';
-	$buf .= <BUF> for 1..$lines;
+	$buf .= readline $fh for 1..$lines;
 	my $sep = settings_get_str 'upgrade_separator';
 	$sep .= "\n" if $sep ne '';
 	$window->gui_printtext_after(undef, MSGLEVEL_CLIENTNOTICE, "$buf\cO$sep");
 	$view->redraw();
     }
@@ -117,5 +124,12 @@ signal_add       'event 366'       => 'suppress';
 signal_add       'event join'      => 'suppress';
 
 unless (-f sprintf('%s/scripts/autorun/buf.pl', get_irssi_dir)) {
     Irssi::print('PUT THIS SCRIPT IN ~/.irssi/scripts/autorun/ BEFORE /UPGRADING!!');
 }
+
+# Remove any left-over file. If 'session' doesn't exist (created by irssi
+# during /UPGRADE), neither should our file.
+unless (-e sprintf('%s/session', get_irssi_dir)) {
+    my $fn = _filename;
+    unlink $fn or warn "unlink $fn: $!" if -e $fn;
+}

Vulnerable code: $VERSION = '2.13';
    changed	=> 'Mon May 13 19:41 CET 2002',
    changes	=> 'Severe formatting bug removed * oops, I ' .
                   'exposed Irssi to ircII foolishness * sorry ' .
		   '** removed logging stuff (this is a fix)',
    open BUF, q{>}, sprintf('%s/scrollbuffer', get_irssi_dir) or die $!;
    print BUF join("\0", map $_->{server}->{address} . $_->{name}, channels), "\n";
	printf BUF "%s:%s\n%s", $window->{refnum}, $lines, $buf;
    close BUF;
    open BUF, q{<}, sprintf('%s/scrollbuffer', get_irssi_dir) or die $!;
    my @suppress = split /\0/, <BUF>;
    while (my $bla = <BUF>){
	    <BUF> for 1..$lines;
	$buf .= <BUF> for 1..$lines;
Fixed code: $VERSION = '2.20';
    changed	=> 'Thu Sep 22 01:37 CEST 2016',
    changes	=> 'Fixed file permissions (leaked everything via filesystem)',
sub _filename { sprintf '%s/scrollbuffer', get_irssi_dir }

    my $fn = _filename;
    my $old_umask = umask 0077;
    open my $fh, q{>}, $fn or die "open $fn: $!";
    umask $old_umask;

    print $fh join("\0", map $_->{server}->{address} . $_->{name}, channels), "\n";
	printf $fh "%s:%s\n%s", $window->{refnum}, $lines, $buf;
    close $fh;
    my $fn = _filename;
    open my $fh, q{<}, $fn or die "open $fn: $!";
    unlink $fn or warn "unlink $fn: $!";

    my @suppress = split /\0/, readline $fh;
    while (my $bla = readline $fh){
	    readline $fh for 1..$lines;
	$buf .= readline $fh for 1..$lines;

# Remove any left-over file. If 'session' doesn't exist (created by irssi
# during /UPGRADE), neither should our file.
unless (-e sprintf('%s/session', get_irssi_dir)) {
    my $fn = _filename;
    unlink $fn or warn "unlink $fn: $!" if -e $fn;
}