[Secure-testing-team] Re: ekg: CAN-2005-1916 Bug#317027 and #318059 FIXED

Marcin Owsiany porridge at debian.org
Fri Jul 15 16:04:59 UTC 2005


The following vulnerabilities:

* CAN-2005-1916 (insecure tempfile creation and arbitrary command
  execution in contrib/scripts/linki.py found by Eric Romang) (local
  vuln.)

* other vulnerabilities in user-contributed example scripts:
 - insecure tempfile creation in contrib/ekgh, contrib/ekgnv.sh,
   contrib/getekg.sh (local vuln.)
 - potential shell command injection in contrib/scripts/ekgbot-pre1.py
   (if indeed exploitable, a remote vuln.)
 found by Marcin Owsiany and Wojtek Kaniewski

Are fixed in:
 - upstream version 1.6rc2
 - sid package 1:1.5+20050712+1.6rc2-1 (uploaded)
 - sarge package 1:1.5+20050411-4 (available at
   deb http://people.debian.org/~porridge/ekg-sarge/ ./
   interdiff attached

All previous versions are vulnerable (except for some very old ones,
which do not contain the scripts).
Woody does not contain ekg package.
ekg2 (in experimental) does not contain the vulnerable scripts.

please issue a DSA

Marcin
-- 
Marcin Owsiany <porridge at debian.org>             http://marcin.owsiany.pl/
GnuPG: 1024D/60F41216  FE67 DA2D 0ACA FC5E 3F75  D6F6 3A0D 8AA0 60F4 1216
-------------- next part --------------
interdiff debian/ekg_1.5+20050411-4.diff.0 debian/ekg_1.5+20050411-4.diff
diff -u ekg-1.5+20050411/debian/changelog ekg-1.5+20050411/debian/changelog
--- ekg-1.5+20050411/debian/changelog	2005-05-24 19:14:30.000000000 +0200
+++ ekg-1.5+20050411/debian/changelog	2005-07-13 21:30:21.829781920 +0300
@@ -1,3 +1,12 @@
+ekg (1:1.5+20050411-4) stable-security; urgency=medium
+
+  * Security upload
+  * Fixes symlink creation and potential shell injection vulnerabilities in
+    user-contributed example scripts. (CAN-2005-1916) (CAN-2005-XXXX -
+    optionally if we decide to get new one). Closes: #318059
+
+ -- Marcin Owsiany <porridge at kufelek>  Wed, 13 Jul 2005 21:25:04 +0300
+
 ekg (1:1.5+20050411-3) unstable; urgency=high
 
   * Applied patches selected from upstream CVS, to fix the following important
only in patch2:
unchanged:
--- ekg-1.5+20050411.orig/contrib/ekgh	2002-10-30 01:00:00.000000000 +0200
+++ ekg-1.5+20050411/contrib/ekgh	2005-07-13 21:33:26.810660552 +0300
@@ -44,15 +44,8 @@
 
 #?eby si? cudzys?owy, przecinki, oraz \r \n dobrze wy?wietla?y...
 
-tail -n $ILELINII $HISTFILE | sed -e 's/\\r/\\\\r/g; s/\\n/\\\\n/g; s/,"/,/; s/"$//; s/*/"*"/g' > /usr/tmp/gg_history 
-				 
-				 # czemu * wy?wietla zaw. katalogu?
-				 # *uj j? tam wie :>
-							
-
-#No to sru
-
-cat /usr/tmp/gg_history | while read linia; do
+tail -n $ILELINII $HISTFILE | sed -e 's/\\r/\\\\r/g; s/\\n/\\\\n/g; s/,"/,/; s/"$//; s/*/"*"/g' | \
+while read linia; do
 
 DATA_S=$(echo $linia | cut -d "," -f4 )
 DATA=$(date -d "1970-01-01 `echo $DATA_S` sec UTC" +"%Y-%m-%d %T")
only in patch2:
unchanged:
--- ekg-1.5+20050411.orig/contrib/ekgnv.sh	2002-03-30 01:00:00.000000000 +0200
+++ ekg-1.5+20050411/contrib/ekgnv.sh	2005-07-13 21:33:26.847654928 +0300
@@ -8,7 +8,7 @@
 #
 # Zmienne dla ~/.ekgnv :
 # WGET= ?cie?ka do wget'a
-# EKGTMP= gdzie ma wrzuca? pliki tymaczasowe
+# EKGTMP= gdzie zostanie utworzony podkatalog na pliki tymaczasowe
 # EKGWWW= adres strony ekg.
 # EKGCONF= co ma podawa? do configure gdy automatycznie budujesz ekg
 # LASTEKG= ostatnia zainstalowana werja ekg
@@ -40,6 +40,15 @@
 # Wczytaj ustawienia.
   . ~/.ekgnv
 
+# wymyslamy bezpieczny podkatalog
+EKGTMPS="$EKGTMP/ekgnv-$$"
+mkdir "$EKGTMPS"
+if [ "$?" != "0" ]; then
+	echo "Proba utworzenia katalogu tymczasowego \"$EKGTMPS\" nie powiodla sie." >&2
+	echo "Posprzataj \"$EKGTMP\" i sprobuj ponownie." >&2
+	exit 1
+fi
+
 # Czy w systemie jest wget?
 function check_wget {
  if [ ! -x "$WGET" ]; then \
@@ -52,13 +61,13 @@
 function get_list {
  check_wget
  echo -n "?ci?gam list? wersji EKG. Poczekaj chwil?. "
-  wget -q -P $EKGTMP $EKGWWW/download.php
+  wget -q -P $EKGTMPS $EKGWWW/download.php
  # to mozna zamienic na odczytywanie pliku ktory bylby automatycznie po
  # twojej stronie generowany, a w ktorym bylby tylko numer najnowszej 
  # werjsji
- LASTES="`grep ekg-20 $EKGTMP/download.php | cut -d\  -f6 | \
+ LASTES="`grep ekg-20 $EKGTMPS/download.php | cut -d\  -f6 | \
   	   cut -d\\" -f2 | tail -1 | sed -e s/.tar.gz//`"
-  rm -f $EKGTMP/download.php*
+  rm -f $EKGTMPS/download.php*
  echo "Gotowe!"
 }
 
@@ -82,13 +91,13 @@
  check_new
   if [ ! -z "$NEW" ]; then \
    echo -n "?ci?gam j?, poczekaj chwil?. "
-   wget -q -P $EKGTMP $EKGWWW/$LASTES.tar.gz
+   wget -q -P $EKGTMPS $EKGWWW/$LASTES.tar.gz
    cat ~/.ekgnv | sed -e s/$LASTEKG/$LASTES/ > ~/.ekgtmp
  # lub jak ktos nie ma seda to grep -v "LASTEKG" ~/.ekgnv > ~/.ekgtmp
  # echo "export LASTEKG=$LASTES" >> ~/.ekgtmp
    mv -f ~/.ekgtmp ~/.ekgnv
    echo "Gotowe.
-Plik znajduje si? w $EKGTMP/$LASTES.tar.gz"
+Plik znajduje si? w $EKGTMPS/$LASTES.tar.gz"
   else 
    echo 
   fi
@@ -99,11 +108,11 @@
  get_new
   if [ ! -z "$NEW" ]; then \
    echo -n "Buduje nowe EKG. Poczekaj chwil?. "
-   ( cd $EKGTMP ;
+   ( cd $EKGTMPS ;
    tar -zxf $LASTES.tar.gz ; cd $LASTES ;
      ./configure $EKGCONF > /dev/null ; # tylko b??dy b?d? na konsoli.
       make ; make install > /dev/null ;
-   rm -rf ../$LASTES* ; )
+      cd .. ; rm -rf $LASTES $LATES.tar.gz ; )
    echo "Sko?czy?em. Masz ju? najnowsz? wersj?."
   fi
 }
only in patch2:
unchanged:
--- ekg-1.5+20050411.orig/contrib/getekg.sh	2002-05-29 01:00:00.000000000 +0300
+++ ekg-1.5+20050411/contrib/getekg.sh	2005-07-13 21:33:26.848654776 +0300
@@ -7,8 +7,13 @@
 	echo "Podaj zmienne (\"--prefix=/usr\" itp.) dla configure'a!"; 
 	exit 1; 
 fi
-echo "Wchodz? do katalogu tymczasowego."
-cd /tmp
+DIR="${TMPDIR:-${TMP:-/tmp}}/getekg-$$"
+if ! mkdir "$DIR"; then
+	echo "Nie udalo sie utworzyc katalogu tymczasowego \"$DIR\"." >&2
+	exit 1
+fi
+echo "Wchodz? do katalogu tymczasowego \"$DIR\"."
+cd "$DIR"
 echo -n "?ci?gam najnowsz? wersj?."
  if [ ! -x "`which wget`" ]; then \
     if [ ! -x "`which curl`" ]; then \
only in patch2:
unchanged:
--- ekg-1.5+20050411.orig/contrib/scripts/ekgbot-pre1.py	2003-06-29 21:19:00.000000000 +0300
+++ ekg-1.5+20050411/contrib/scripts/ekgbot-pre1.py	2005-07-13 21:33:26.888648696 +0300
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+# -*- coding: ISO-8859-2 -*-
 # ekg-bot 0.1-pre1
 # Copyright (C) 2003 Andrzej Lindna?
 #
@@ -25,6 +26,8 @@
 from urllib import *					# Obs?uga www
 from time import *					# Operacje czasowe
 from random import Random				# Random
+from os import *					# do implementacji popen
+#import sys						# debugowanie implementacji popen
 
 # Konfiguracja
 owner = twojnumer					# Numer gg ownera
@@ -80,7 +83,7 @@
 
 	if text[0] == "@":
 		if uin != owner:
-			ekg.command("msg %d Czy Ty, aby na pewno jeste? w?a?cicielem tego bota?;)" % uin)
+			ekg.command("msg %d Czy Ty aby na pewno jeste? w?a?cicielem tego bota?;)" % uin)
 			return
 		tablica = ownerz
 	elif text[0] == "!": tablica = userz
@@ -99,14 +102,17 @@
 	try: funkcja, ilosc = tablica[kom]
 	except:	return
 
+	# jesli komenda wymaga argumentow
 	if ilosc > 0:
 		splitarg = arg.split()
 		args = splitarg[:ilosc]
 		if len(splitarg) > ilosc:
 			args.append(string.join(splitarg[ilosc:]))
+	# jesli komenda nie wymaga argumentow
 	elif ilosc == 0:
 		if len(arg) > 0: args = [arg]
 		else: args = []
+	# bledny wpis w liscie komend
 	else: return
 
 	try: funkcja(uin, *args)
@@ -307,17 +313,15 @@
 	try: 
 		ekg.command("add %d %s" % (int(duin), nazwa))
 		ekg.command("msg %d Doda?em %s (%s) do listy kontakt?w." % (uin, duin, nazwa))
-	except:	ekg.command("msg %d Pierwszy parametro to UIN, a drugi NAZWA." % uin)
+	except:	ekg.command("msg %d Pierwszy parametr to UIN, a drugi NAZWA." % uin)
 
 def freestats(uin):
 	free = os.popen("%s -m" % path_free).read()
 	ekg.command("msg %d %s" % (uin, free))
-	os.popen("%s -m" % path_free).close()
 
 def hddstats(uin):
 	df = os.popen("%s -h" % path_df).read()
 	ekg.command("msg %d %s" % (uin, df))
-	os.popen("%s -h" % path_df).close()
 
 def refstatus(uin):
 	status_ref = strftime("%a, %d %b %Y %H:%M:%S %Z")
@@ -332,9 +336,8 @@
 	ekg.command("msg %d %s" % (uin, checkurl.info().getheader('Server')))
 
 def ciekhost(uin, host):
-	qmer = os.popen("%s %s" % (path_host, re.escape(host))).read()
+	qmer = safepopen([path_host, host])
 	ekg.command("msg %d %s" % (uin, qmer))
-	os.popen("%s %s" % (path_host, re.escape(host))).close()
 
 def fetchmail(uin):
 	os.popen("%s" % path_fetchmail)
@@ -351,6 +354,7 @@
 		ekg.command("block %d" % ktos)
 		ekg.command("msg %d %d zosta? zablokowany" % (uin, ktos))
 	except: ekg.command("msg %d Musisz poda? parametr jako UIN do zablokowania" % uin)
+
 def unblock(uin, numer):
 	try:
 		numer = int(numer)
@@ -393,16 +397,13 @@
 
 def uname(uin):
 	ekg.command("msg %d %s" % (uin, os.popen("%s -mnrs" % path_uname).read()))
-	os.popen("%s -mnrs" % path_uname).close()
 
 def krot(uin, ile, tekst):
 	ile = int(ile)
 	if ile < 2 or ile > 23:	ekg.command("msg %d Jako pierwszy parametr nale?y poda? liczb? z zakresu 2-23." % uin)
 	else:
 		try:
-			cmdline = "%s -ear%d %s" % (path_erecoder, ile, re.escape(tekst))
-			ekg.command("msg %d %s" % (uin, os.popen(cmdline).read()))
-			os.popen(cmdline).close()
+			ekg.command("msg %d %s" % (uin, safepopen([path_erecoder, '-ear'+ile, '--', tekst])))
 		except:	ekg.command("msg %d Jako pierwszy parametr nale?y poda? liczb? z zakresu 2-23." % uin)
 
 def drot(uin, ile, tekst):
@@ -410,9 +411,7 @@
 	if ile < 2 or ile > 23: ekg.command("msg %d Jako pierwszy parametr nale?y poda? liczb? z zakresu 2 - 23." % uin)
 	else:
 		try:
-			cmdline = "%s -dar%d %s" % (path_erecoder, int(ile), re.escape(tekst))
-			ekg.command("msg %d %s" % (uin, os.popen(cmdline).read()))
-			os.popen(cmdline).close()
+			ekg.command("msg %d %s" % (uin, safepopen([path_erecoder, '-dar'+ile, '--', tekst])))
 		except:	ekg.command("msg %d Jako pierwszy parametr nale?y poda? liczb? z zakresu 2-23" % uin)
 
 def kbase(uin, tekst):
@@ -496,6 +495,7 @@
 		ekg.command("msg %d Wylosowane przez komputer liczby do losowa? Zak?ad?w Specjalnych to: %s" % (uin, lotto(42,5)))
 	else:
 		ekg.command("msg %d Nieznany parametr! Parametry:\r\nmulti, duzy, express, zaklady" % uin)
+
 def sin(uin, liczba):
 	try: ekg.command("msg %d %s" % (uin, trygonometria('sin', liczba)))
 	except:	ekg.command("msg %d B??dne wywo?anie." % uin)
@@ -523,14 +523,10 @@
 		?polecenie. Na przyk?ad: ?status.\r\nAutorem tego bota jest Andrzej Lindna?""" % uin)
 
 def kmorse(uin, tekst):
-	cmdline = "%s -eam -- %s" % (path_erecoder, re.escape(tekst))
-	ekg.command("msg %d %s" % (uin, os.popen(cmdline).read()))
-	os.popen(cmdline).close()
+	ekg.command("msg %d %s" % (uin, safepopen([path_erecoder, '-eam', '--', tekst])))
 
 def dmorse(uin, tekst):
-	cmdline = "%s -dam -- %s" % (path_erecoder, re.escape(tekst))
-	ekg.command("msg %d %s" % (uin, os.popen(cmdline).read()))
-	os.popen(cmdline).close()
+	ekg.command("msg %d %s" % (uin, safepopen([path_erecoder, '-dam', '--', tekst])))
 
 def bmi(uin, masa, wzrost):
 	try:
@@ -561,3 +557,27 @@
 def killme(uin):
 	ekg.command("msg %d Wy??czam EKG. Aby mnie ponownie w??czy?, b?dziesz musia? zalogowa? si? na shella niestety;))" % uin)
 	ekg.command("quit")
+
+
+def safepopen(cmd):
+	rfd, wfd = pipe()
+	ret = fork()
+	# child
+	if ret == 0:
+		close(rfd)
+		dup2(wfd, 1)
+		execv(cmd[0], cmd)
+		exit(1)
+	# parent
+	else:
+		close(wfd)
+#		sys.stderr.write('reading..')
+		readstring = read(rfd, 4094)
+#		sys.stderr.write('got it!')
+		while read(rfd, 4096) != '':
+			pass
+#		sys.stderr.write('EOF')
+		waitpid(ret, 0)
+#		sys.stderr.write('reaped')
+		return readstring
+
only in patch2:
unchanged:
--- ekg-1.5+20050411.orig/contrib/scripts/linki.py	2003-06-29 23:29:58.000000000 +0300
+++ ekg-1.5+20050411/contrib/scripts/linki.py	2005-07-13 21:33:26.926642920 +0300
@@ -1,14 +1,21 @@
+# -*- coding: ISO-8859-2 -*-
 # Skrypt ten uruchamia si? z poziomu ekg (polecenie python) a zadanie jego to
 # wy?apywanie adres?w URL w otzymanych wiadomo?ciach. Mozna by w tym miejscu poopowiada? o dzia?aniu skryptu
 # ale my?le ze skypt jest dos? rozmowny pozatym by poczyta? helpa (i nie tylko) wystarczy nacisn?? F8
 # wszelkie pretensje mo?na kierowa? na adres: rmrmg(at)wp(dot)pl
+#
+# poprawki bezpiecze?stwa: wojtekka (2005-07-11)
+
 import re
 import ekg
 import string
 import os
 
+browser="firefox"
 link=re.compile(".*http.*")
 linka=re.compile("http.*")
+linkfile=os.path.expanduser("~/.gg/rmrmg_ekg_url")
+
 def init ():
  ekg.printf("generic", "linkownik")
  return 1
@@ -17,6 +24,17 @@
  ekg.printf("generic", "linkownik poszed?")
  return 1 
 
+def launch(url, tab):
+    url = string.replace(string.replace(url, ",", "%2c"), "'", "%27");
+    
+    if tab:
+	command = "%s -remote 'openURL(%s, new-tab)'" % (browser, url)
+    else:
+	command = "%s '%s'" % (browser, url)
+
+    #ekg.printf("generic", "[%s]" % (command))
+    os.system(command)
+
 def handle_msg(uin, name, msgclass, text, time, secure):
     #ekg.printf("generic", "echo dzia?a")
     if link.match(text):
@@ -26,7 +44,7 @@
 		ekg.printf("generic", "znaleziono link: %s" %(x)) 
 		ekg.printf("generic", "by otworzy? w: nowym oknie wcisnij F7, nowej zak?adce F5, by nie otwierac wci?nijF6.")
 		ekg.printf("generic", "F8 pokazuje liste przechwyconych link?w; F5-F7 dzia?a na pierwszym linku z listy")
-		os.system("echo \"%s\" >> /tmp/rmrmg_ekg_url" %(x))
+		open(linkfile, 'a').write(x + '\n');
 	#ekg.printf("generic","echo tada")
 	return 1
     else:
@@ -42,13 +60,13 @@
 	    dlug=len(nurl)
 	    if dlug == 1:
 		ekg.printf("generic", "otwieram %s w nowej zak?adce" %(nurl[0]))
-		os.system("MozillaFirebird -remote 'openURL(%s, new-tab)'" %(nurl[0]))
-		os.system('rm /tmp/rmrmg_ekg_url')
+		launch(nurl[0], True)
+		os.unlink(linkfile)
 	    else:
 		ekg.printf("generic", "link?w mam %d" %(dlug))
 		wielejest(nurl)
 		ekg.printf("generic", "otwieram %s w nowej zak?adce" %(nurl[0]))
-		os.system("MozillaFirebird -remote 'openURL(%s, new-tab)'" %(nurl[0]))
+		launch(nurl[0], True)
     elif key == 270:
 	ekg.printf("generic", "wcisni?to F6")
 	nurl=czyjest()
@@ -58,7 +76,7 @@
 	    dlug=len(nurl)
 	    if dlug == 1:
 		ekg.printf("generic", "kasuje adres %s" %(nurl[0]))	    
-		os.system('rm /tmp/rmrmg_ekg_url')
+		os.unlink(linkfile)
 	    else:
 		ekg.printf("generic", "jest wiele link?w")
 		wielejest(nurl)
@@ -72,8 +90,8 @@
 	    dlug=len(nurl)
 	    if dlug == 1:
 		ekg.printf("generic", "otwieram %s w nowym oknie" %(nurl[0]))
-		os.system("MozillaFirebird %s" %(nurl[0]))
-		os.system('rm /tmp/rmrmg_ekg_url')		
+		launch(nurl[0], False)
+		os.unlink(linkfile)
 	    else:
 		ekg.printf("generic", "link?w mam %d" %(dlug))
 		wielejest(nurl)
@@ -93,8 +111,8 @@
 ###########################################################
 
 def czyjest ():
-    if os.path.exists('/tmp/rmrmg_ekg_url'):
-	wejsc= open ('/tmp/rmrmg_ekg_url')
+    if os.path.exists(linkfile):
+	wejsc= open (linkfile)
 	file = wejsc.readlines()
 	dlug=len(file)
 	wejsc.close()
@@ -104,9 +122,9 @@
 	return 0
 	
 def wielejest (buff):
-    file=open('/tmp/rmrmg_ekg_url' , 'w')		
+    file=open(linkfile , 'w')		
     #buff= file.readlines()
     #file.truncate()
     #file.writelines
     file.writelines('\n'.join (buff[1:]))
-    file.close()
\ Brak znaku nowej linii na ko?cu pliku
+    file.close()
#-diff -u debian/ekg_1.5+20050411-4.diff.0 debian/ekg_1.5+20050411-4.diff


More information about the Secure-testing-team mailing list