Clam Antivirus (AV) ist ein populäres freies Antivirenprogramm. Da es unter Linux praktische keine verbreiteten Viren oder Trojaner gibt, wird es meist eingesetzt, um Samba-Shares oder den Mailverkehr zu untersuchen. Eine inzwischen behobene Sicherheitslücke machte das Programm für Denial-of-Service-Attacken anfällig.
Die Schwachstelle wurde das erste Mal Mitte Mai 2011 gemeldet und trat auf einem Produktionssystem beim Abarbeiten von Mails auf. Dort kam Solaris zum Einsatz, Linux ist von diesem Problem jedoch ebenfalls betroffen. Entdeckt wurde der Fehler durch einen plötzlichen Absturz des Daemons “clamd”, wobei der GNU-Debugger GDB folgendes Backtrace lieferte:
#0 0xfee6515b in hm_cmp (itm=0xf8aedeb0 "G#\006Md#######j####01\002", ref=0xf9f16000 <Address 0xf9f16000 out of bounds>, keylen=16) at matcher-hash.c:130 #1 0xfee653a9 in cli_hm_scan (digest=0xf8aedeb0 "G#\006Md#######j####01\002", size=264, virname=0xf8aeea48, root=0xfe602a40, type=CLI_HASH_MD5) at matcher-hash.c:224
Dieser Backtrace-Auszug zeigt, dass der Absturz in der Funktion “hm_cmp()” auftritt, welche zuvor von “cli_hm_scan()” aufgerufen wird. Beide Routinen befinden sich in der Datei “matcher-hash.c”, die Funktionen zum Abgleich von Hashes bereitstellt. Die Funktion “cli_hm_scan()” ist recht kurz:
int cli_hm_scan(const unsigned char *digest, uint32_t size, const char **virname, const struct cli_matcher *root, enum CLI_HASH_TYPE type) { const struct cli_htu32_element *item; unsigned int keylen; struct cli_sz_hash *szh; size_t l, r; if(!digest || !size || size == 0xffffffff || !root || !root->hm.sizehashes[type].capacity) return CL_CLEAN; item = cli_htu32_find(&root->hm.sizehashes[type], size); if(!item) return CL_CLEAN; szh = (struct cli_sz_hash *)item->data.as_ptr; keylen = hashlen[type]; l = 0; r = szh->items; while(l <= r) { size_t c = (l + r) / 2; int res = hm_cmp(digest, &szh->hash_array[keylen * c], keylen); if(res < 0) { if(!c) break; r = c - 1; } else if(res > 0) l = c + 1; else { if(virname) *virname = szh->virusnames[c]; return CL_VIRUS; } } return CL_CLEAN; }
Dennoch hat sich hier ein typischer Off-by-One-Fehler (“Um Eins daneben”) eingeschlichen: Der Code setzt die Variable “r” vor der While-Schleife auf “szh->items”, und die Schleife bricht ab, sobald “l>r” eintritt. In der Schleife selbst wird dann die aus dem GDB-Backtrace bekannte Funktion “hm_cmp()” aufgerufen, wobei ein auf “l” und “r” basierter Index in “hash_array[]” verwendet wird. Der “clamd”-Absturz kommt nun zustande, weil die Variable “r” eigentlich auf “szh->items-1” gesetzt werden sollte. Anderenfalls läuft der Index zu weit, und es kommt zum Overflow. Der Patch für diese Sicherheitslücke besteht also einfach darin, “r = szh->items-1” zu setzen.
Betroffen ist die Clam AV 0.97. Der Fehler ist in der aktuellen Version 0.97.2 korrigiert.

