-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Canna Security Advisory Canna-2002-1 multiple vulnerabilities in canna server Date Reported: 06 Nov 2002 Release Date: 02 Dec 2002 Vulnerable: Yes Security database references: More information: Vulnerability #1 "hsj" of Shadow Penguin Security discovered a heap overflow vulnerability in the irw_through function in canna server version 3.6 and earlier. A malicious remote user could exploit this vulnerability and potentially gain privileges of running canna server or cause a denial of service attack. Vulnerability #2 AIDA Shinra of Canna project found lack of validations of requests in canna version 3.6 and earlier. A malicious remote user can exploit this vulnerability and peep some information, or cause a denial of service attack. An attacker might take advantage of this information to exploit the first vulnerability. Workaround: 1. Stop canna server by cannakill command. In addition, if your server version is 3.5b2 or earlier, you need to drop setuid/setgid bit or uninstall the server. 2. Create /etc/hosts.canna file and restrict access from untrusted hosts. Note that access control by username does not help. If one user of a host is untrusted, the host should be considered as a totally untrusted host. Additionally, this workaround is effective only to the first vulnerability. You cannot workaround the second by this access control. 3. If your server version is 3.6 and all local users are trusted, remove -inet option when you start the server. Solution: Please upgrade your canna server to version 3.6p1 or apply the following patches. ChangeLog: 02 Dec 2002: Initial revision 02 Dec 2002: * correct Shadow Pengin Society to Shadow Penguin Security. * add release date Canna セキュリティ情報 Canna-2002-1 複数のcanna サーバーの脆弱性 報告日時: 2002年11月6日 公表日: 2002年12月2日 脆弱性 #1 Shadow Penguin Security の hsj 氏は、バージョン 3.6以前の canna サーバー の irw_through 関数に含まれるヒープオーバフロー脆弱性を発見しました。 悪意のあるリモートユーザーはこの脆弱性を使用し、canna サーバーを 走らせているユーザー権限を得られるかもしれません。また、サービス不能 攻撃の原因となるかもしれません。 脆弱性 #2 canna プロジェクトの AIDA Shinra は、バージョン3.6以前の canna サーバー で、リクエストの正当性の検証が不十分であることを発見しました。 悪意のあるリモートユーザーはこの脆弱性を使用し、canna サーバーの 内部の情報を不正に得ることができます。この情報は、第一の脆弱性を悪用 するためにも利用されるかも知れません。 回避策 1. cannakill コマンドで canna サーバーを停止してください。これに加えて、 3.5b2 以前のバージョンでは、setuid/setgidビットを落とすか、サーバーを アンインストールする必要があります。 2. /etc/hosts.cannaを作って、信用できないホストからの接続を拒否するよ う設定してください。この際、ユーザー名による制限は回避策にならず、ホス トによる制限だけが有効です。一人でも信頼できないユーザーが存在するホス トは全く信頼できないホストと考えるべきです。また、この回避策は第一の脆 弱性にのみ有効であり、第二の脆弱性はこの方法では回避できません。 3. サーバーのバージョンが 3.6 で、ローカルのユーザーが信頼できる場合、 canna サーバーの起動オプションから-inetを外してください。 canna サーバーを バージョン3.6p1 以降に更新するか、以下のパッチを適用 してください。 更新履歴: 02 Dec 2002: 最初の発表 02 Dec 2002: * Shadow Penguin Society を Shadow Penguin Security に訂正 * 公開日を追記 Index: server/convert.c =================================================================== RCS file: /cvsroot/canna/canna/server/convert.c,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 convert.c - --- server/convert.c 19 Oct 2002 08:27:53 -0000 1.1.1.1 +++ server/convert.c 1 Dec 2002 10:54:39 -0000 @@ -53,6 +53,8 @@ #define ACK2 2 #define ACK3 3 #define CHECK_ACK_BUF_SIZE (ACK_BUFSIZE + (SIZEOFLONG * 2) ) +#define IR_INT_MAX 32767 +#define IR_INT_INVAL(x) ((unsigned int)x > IR_INT_MAX) extern int errno; @@ -1778,6 +1780,8 @@ return( needsize ) ; req->namelen = (int)L4TOL(buf + SIZE4); + if( IR_INT_INVAL(req->namelen) ) + return( -1 ); ir_debug( Dmsg(10,"req->namelen =%d\n", req->namelen ); ) if( (needsize = SIZE8 + req->namelen - size) > 0 ) @@ -1785,6 +1789,8 @@ if( req->namelen > 0 ){ req->name = buf + SIZE8 ; + if( req->name[req->namelen - 1] != 0 ) + return( -1 ); } ir_debug( Dmsg(10,"req->namelen =%d\n", req->namelen ); ) ir_debug( Dmsg(10,"req->name =%s\n", req->name ); ) Index: server/util.c =================================================================== RCS file: /cvsroot/canna/canna/server/util.c,v retrieving revision 1.3.2.1 diff -u -r1.3.2.1 util.c - --- server/util.c 7 Nov 2002 14:46:02 -0000 1.3.2.1 +++ server/util.c 1 Dec 2002 10:54:39 -0000 @@ -217,6 +217,19 @@ return res; } +const Ushort * +ushortmemchr(ws, ch, len) +const Ushort *ws; +int ch; +size_t len; +{ + const Ushort *p, *end; + for (p = ws, end = ws + len; p < end; ++p) + if (*p == (Ushort)ch) + return p; + return NULL; +} + int ushortstrcpy(wd, ws) Ushort *wd, *ws; Index: server/wconvert.c =================================================================== RCS file: /cvsroot/canna/canna/server/wconvert.c,v retrieving revision 1.2 diff -u -r1.2 wconvert.c - --- server/wconvert.c 20 Oct 2002 04:10:30 -0000 1.2 +++ server/wconvert.c 1 Dec 2002 10:54:40 -0000 @@ -100,6 +100,7 @@ #endif extern void DispDebug() ; +extern const Ushort *ushortmemchr pro((const Ushort *, int, size_t)); extern int canna_server_hi ; extern int canna_server_lo ; #ifdef DEBUG @@ -1333,7 +1334,10 @@ char *dirname, *dirnamelong = (char *)0; int cxnum = Request.type18.context, stat = -1; int requestsize = Request.type18.size, retval; + size_t datasize = Request.type18.datalen - SIZEOFSHORT * 2; + if (datasize == 0 || req->data[datasize - 1] != 0) + goto protoerr; if (validcontext(cxnum, client, wListDictionary)) { if (requestsize <= sizeof(local_buffer) || (dicnames = malloc(requestsize))) { @@ -1370,6 +1374,7 @@ } } +protoerr: retval = SendType6Reply(client, wListDictionary, EXTPROTO, stat, dicnames, namesize(dicnames, stat)); if (dicnames != (char *)local_buffer) free(dicnames); @@ -1481,10 +1486,15 @@ char *dicname, *dirname, *dirnamelong = (char *)0; int cxnum = Request.type18.context, stat = BADCONT; int dirlen, requestsize = Request.type18.size, retval; + size_t datasize = Request.type18.datalen - SIZEOFSHORT * 2; + if (datasize == 0 || req->data[datasize - 1] != 0) + goto protoerr; if (validcontext(cxnum, client, wGetWordTextDictionary)) { dirname = req->data ; dirlen = strlen(dirname) + 1 ; + if (dirlen == datasize) + goto protoerr; dicname = &(req->data[dirlen]) ; if (dirlen > 1) { if (!dirname || dirname[0] != ':' || @@ -1526,6 +1536,7 @@ free(dirnamelong); } } +protoerr: retval = SendType7Reply(client, wGetWordTextDictionary, EXTPROTO, stat, stat > 0 ? stat + 1 : 0, infobuf); if (infobuf != (Ushort *)local_buffer) free((char *)infobuf); @@ -2307,6 +2318,9 @@ { ir_debug( Dmsg(10, "ProcWideReq1 start!!\n") ); + if (Request.type1.datalen != 0) + return( -1 ); + return( 0 ) ; } @@ -2316,6 +2330,8 @@ { ir_debug( Dmsg(10, "ProcWideReq2 start!!\n") ); + if (Request.type2.datalen != SIZEOFSHORT) + return( -1 ); buf += HEADER_SIZE; Request.type2.context = S2TOS(buf); ir_debug( Dmsg(10, "req->context =%d\n", Request.type2.context) ); @@ -2328,6 +2344,8 @@ { ir_debug( Dmsg(10, "ProcWideReq3 start!!\n") ); + if (Request.type3.datalen != SIZEOFSHORT * 2) + return( -1 ); buf += HEADER_SIZE; Request.type3.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type3.buflen = S2TOS(buf); ir_debug( Dmsg(10, "req->context =%d\n", Request.type3.context) ); @@ -2345,12 +2363,18 @@ ir_debug( Dmsg(10, "ProcWideReq4 start!!\n") ); + if (Request.type4.datalen < SIZEOFSHORT * 4) + return( -1 ); buf += HEADER_SIZE; Request.type4.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type4.begin = S2TOS(buf); buf += SIZEOFSHORT; Request.type4.end = S2TOS(buf); buf += SIZEOFSHORT; Request.type4.yomilen = S2TOS(buf); Request.type4.yomi = (Ushort *)(buf += SIZEOFSHORT) ; - - len = Request.type4.datalen - SIZEOFSHORT * 4; + len = Request.type4.yomilen + 1; + if (Request.type4.datalen != SIZEOFSHORT * (4 + len) + || len == 0 + || Request.type4.yomi[len - 1] != 0) + return( -1 ); for (data = Request.type4.yomi, i = 0; i < len; i++, data++) *data = ntohs((unsigned short)*data); /* ちょっとやだなあ */ ir_debug( Dmsg(10, "req->context =%d\n", Request.type4.context) ); @@ -2370,6 +2394,8 @@ { ir_debug( Dmsg(10, "ProcWideReq5 start!!\n") ); + if (Request.type5.datalen != SIZEOFSHORT * 2 + SIZEOFINT) + return( -1 ); buf += HEADER_SIZE; Request.type5.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type5.size = S2TOS(buf); buf += SIZEOFSHORT; Request.type5.mode = L4TOL(buf); @@ -2386,6 +2412,8 @@ { ir_debug( Dmsg(10, "ProcWideReq6 start!!\n") ); + if (Request.type6.datalen != SIZEOFSHORT * 3) + return( -1 ); buf += HEADER_SIZE; Request.type6.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type6.number = S2TOS(buf); buf += SIZEOFSHORT; Request.type6.buflen = S2TOS(buf); @@ -2402,6 +2430,8 @@ { ir_debug( Dmsg(10, "ProcWideReq7 start!!\n") ); + if (Request.type7.datalen != SIZEOFSHORT * 3) + return( -1 ); buf += HEADER_SIZE; Request.type7.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type7.number = S2TOS(buf); buf += SIZEOFSHORT; Request.type7.yomilen = (short)S2TOS(buf); @@ -2418,6 +2448,8 @@ { ir_debug( Dmsg(10, "ProcWideReq8 start!!\n") ); + if (Request.type8.datalen != SIZEOFSHORT * 4) + return( -1 ); buf += HEADER_SIZE; Request.type8.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type8.curbun = S2TOS(buf); buf += SIZEOFSHORT; Request.type8.curkouho = S2TOS(buf); @@ -2436,6 +2468,8 @@ { ir_debug( Dmsg(10, "ProcWideReq9 start!!\n") ); + if (Request.type9.datalen != SIZEOFSHORT * 4) + return( -1 ); buf += HEADER_SIZE; Request.type9.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type9.number = S2TOS(buf); buf += SIZEOFSHORT; Request.type9.kouho = S2TOS(buf); @@ -2453,9 +2487,13 @@ BYTE *buf ; { register int i ; + int rest; ir_debug( Dmsg(10, "ProcWideReq10 start!!\n") ); + rest = Request.type10.datalen - (SIZEOFSHORT * 2 + SIZEOFINT); + if (rest < 0) + return( -1 ); buf += HEADER_SIZE; Request.type10.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type10.number = S2TOS(buf); buf += SIZEOFSHORT; Request.type10.mode = L4TOL(buf); @@ -2463,6 +2501,8 @@ ir_debug( Dmsg(10, "req->number =%d\n", Request.type10.number) ); ir_debug( Dmsg(10, "req->mode =%d\n", Request.type10.mode) ); + if (rest != Request.type10.number * SIZEOFSHORT) + return( -1 ); buf += SIZEOFINT; Request.type10.kouho = (short *)buf; /* short? */ for (i = 0; i < Request.type10.number; i++) { Request.type10.kouho[i] = S2TOS(buf); buf += SIZEOFSHORT; @@ -2479,12 +2519,18 @@ register Ushort *data; int i, len ; - - ir_debug( Dmsg(10, "ProcWideReq10 start!!\n") ); + ir_debug( Dmsg(10, "ProcWideReq11 start!!\n") ); + if (Request.type11.datalen < SIZEOFSHORT * 2) + return( -1 ); buf += HEADER_SIZE; Request.type11.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type11.curbun = S2TOS(buf); buf += SIZEOFSHORT; Request.type11.yomi = (Ushort *)buf; + if (Request.type11.datalen % SIZEOFSHORT != 0) + return( -1 ); len = ((int)Request.type11.datalen - SIZEOFSHORT * 2) / SIZEOFSHORT ; + if (len == 0 || Request.type11.yomi[len - 1] != 0) + return( -1 ); for (data = Request.type11.yomi, i = 0; i < len; i++, data++) *data = ntohs( *data ); /* なんかやだ */ ir_debug( Dmsg(10, "req->context =%d\n", Request.type11.context) ); @@ -2501,16 +2547,26 @@ BYTE *buf ; { register Ushort *data; - - int i, len ; + int i, len, rest; ir_debug( Dmsg(10, "ProcWideReq12 start!!\n") ); + rest = Request.type12.datalen - SIZEOFSHORT; + if (rest < 0) + return( -1 ); buf += HEADER_SIZE; Request.type12.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type12.datainfo = (Ushort *)buf; + if (!ushortmemchr((Ushort *)buf, 0, rest / SIZEOFSHORT)) + return( -1 ); len = ushortstrlen((Ushort *)buf) + 1; + rest -= len * SIZEOFSHORT; + if (rest <= 0) + return( -1 ); for( data = Request.type12.datainfo, i = 0; i < len; i++, data++ ) *data = ntohs( *data ); /* なんかやだ */ buf += len * SIZEOFSHORT; + if (buf[rest - 1] != '\0') + return( -1 ); Request.type12.dicname = (char *)buf; ir_debug( Dmsg(10, "req->context =%d\n", Request.type12.context) ); ir_debug( Dmsg(10, "req->datainfo =%s\n", @@ -2528,24 +2584,37 @@ BYTE *buf ; { register Ushort *data; - - int i ,len ; + int i ,len, rest; ir_debug( Dmsg(10, "ProcWideReq13 start!!\n") ); + rest = Request.type13.datalen - SIZEOFSHORT; + if (rest < 0) + return( -1 ); buf += HEADER_SIZE; Request.type13.context = S2TOS(buf); len = SIZEOFSHORT ; buf += len; Request.type13.dicname = (char *)buf; + if (!memchr(buf, 0, rest)) + return( -1 ); len = strlen( (char *)buf ) + 1; + rest -= len; + if (rest % SIZEOFSHORT + || rest < SIZEOFSHORT * 3) + return( -1 ); buf += len; Request.type13.yomi = (Ushort *)buf; len = ((int)Request.type13.datalen - len - SIZEOFSHORT * 4) / SIZEOFSHORT; + if (ushortmemchr((Ushort *)buf, 0, len) != (Ushort *)buf + len - 1) + return( -1 ); for( data = Request.type13.yomi, i = 0; i < len; i++, data++) *data = ntohs( *data ); - - buf += (ushortstrlen((Ushort *)buf) + 1) * SIZEOFSHORT; + buf += len * SIZEOFSHORT; Request.type13.yomilen = S2TOS(buf); buf += SIZEOFSHORT; Request.type13.kouhosize = S2TOS(buf); buf += SIZEOFSHORT; Request.type13.hinshisize = S2TOS(buf); + if (Request.type13.yomilen != len - 1) + return( -1 ); ir_debug( Dmsg(10, "req->context =%d\n", Request.type13.context) ); ir_debug( Dmsg(10, "req->dicname =%s\n", Request.type13.dicname) ); ir_debug( Dmsg(10, "req->yomi =%s\n", @@ -2567,11 +2636,16 @@ ir_debug( Dmsg(10, "ProcWideReq14 start!!\n") ); + if (Request.type14.datalen <= SIZEOFINT + SIZEOFSHORT + || Request.type14.datalen % SIZEOFSHORT) + return( -1 ); buf += HEADER_SIZE; Request.type14.mode = L4TOL(buf); buf += SIZEOFINT; Request.type14.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type14.yomi = (Ushort *)buf; len = ((int)Request.type14.datalen - SIZEOFSHORT - SIZEOFINT) / SIZEOFSHORT; + if (Request.type14.yomi[len - 1] != 0) + return( -1 ); for (data = Request.type14.yomi, i = 0; i < len; i++, data++) *data = ntohs( *data ); /* なんかやだ */ @@ -2588,11 +2662,17 @@ ProcWideReq15(buf) BYTE *buf ; { + int rest; ir_debug( Dmsg(10, "ProcWideReq15 start!!\n") ); + rest = Request.type15.datalen - (SIZEOFINT + SIZEOFSHORT); + if (rest <= 0) + return( -1 ); buf += HEADER_SIZE; Request.type15.mode = L4TOL(buf); buf += SIZEOFINT; Request.type15.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type15.dicname = (char *)buf; + if (buf[rest - 1] != 0) + return( -1 ); ir_debug( Dmsg(10, "req->mode =%d\n", Request.type15.mode) ); ir_debug( Dmsg(10, "req->context =%d\n", Request.type15.context) ); ir_debug( Dmsg(10, "req->dicname =%s\n", @@ -2608,6 +2688,9 @@ ir_debug( Dmsg(10, "ProcWideReq17 start!!\n") ); buf += HEADER_SIZE; + if (Request.type17.datalen < SIZEOFCHAR * 2 + || buf[Request.type17.datalen - SIZEOFCHAR * 2] != 0) + return( -1 ); Request.type17.dicname = (char *)buf; Request.type17.mode = (char)*(buf + Request.type17.datalen - SIZEOFCHAR) ; ir_debug( Dmsg(10, "req->dicname =%s\n", @@ -2624,6 +2707,8 @@ { ir_debug( Dmsg(10, "ProcWideReq18 start!!\n") ); + if (Request.type18.datalen < SIZEOFSHORT * 2) + return( -1 ); buf += HEADER_SIZE; Request.type18.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type18.data = (char *)buf; buf += Request.type18.datalen - SIZEOFSHORT * 2; @@ -2641,12 +2726,18 @@ ProcWideReq19(buf) BYTE *buf ; { + int rest; ir_debug( Dmsg(10, "ProcWideReq19 start!!\n") ); + rest = Request.type20.datalen - (SIZEOFSHORT + SIZEOFINT * 2); + if (rest < 0) + return( -1 ); buf += HEADER_SIZE; Request.type20.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type20.command = L4TOL(buf); buf += SIZEOFINT; Request.type20.bufsize = L4TOL(buf); buf += SIZEOFINT; Request.type20.buf = (char *)buf; + if (Request.type20.bufsize != rest) + return( -1 ); ir_debug( Dmsg(10, "req->context =%d\n", Request.type20.context) ); ir_debug( Dmsg(10, "req->command =%d\n", Request.type20.command) ); ir_debug( Dmsg(10, "req->bufsize =%d\n", Request.type20.bufsize) ); @@ -2658,15 +2749,25 @@ ProcWideReq20(buf) BYTE *buf ; { + BYTE *bufend; ir_debug( Dmsg(10, "ProcWideReq20 start!!\n") ); + if (Request.type21.datalen < SIZEOFINT + SIZEOFSHORT) + return( -1 ); buf += HEADER_SIZE; Request.type21.mode = L4TOL(buf); + bufend = buf + Request.type21.datalen; buf += SIZEOFINT; Request.type21.context = S2TOS(buf); buf += SIZEOFSHORT; Request.type21.dirname = (char *)buf; + if (!memchr(buf, 0, bufend - buf)) + return( -1 ); buf += strlen((char *)buf) + 1; Request.type21.srcdic = (char *)buf; + if (!memchr(buf, 0, bufend - buf)) + return( -1 ); buf += strlen((char *)buf) + 1; Request.type21.dstdic = (char *)buf; + if (*(bufend - 1) != 0) + return( -1 ); ir_debug( Dmsg(10, "req->mode =%d\n", Request.type21.mode) ); ir_debug( Dmsg(10, "req->context =%d\n", Request.type21.context) ); -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.1 (FreeBSD) iD8DBQE961n64ac+GaESVJARAr8RAJ4oNybDZ/pLssGG3fC2cBvFaDWBCwCfScbw EWujlY/ZRefv7WOuMAUzkQg= =JVwB -----END PGP SIGNATURE-----