Monday, November 28, 2005 6:34 PM
rsamona
Spot the Bug - November 28, 2005
Some people commented that the last bug was too easy, and it was, but buffer overruns are still common enough that I wanted to send the point home. This one is a bit more challenging.
Courtesy of Neelay Shah, Consultant, Foundstone
void Socket_Setup(void)
{
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD( 2, 2 );
::WSAStartup(wVersionRequested, &wsaData);
SOCKET sTCPServer = ::socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in saTCPServAddr;
saTCPServAddr.sin_family = AF_INET;
saTCPServAddr.sin_addr.S_un.S_addr = ::htonl(INADDR_ANY);
saTCPServAddr.sin_port = ::htons(5678);
int len = sizeof(saTCPServAddr);
int iFail =::bind(sTCPServer, (struct sockaddr*)&saTCPServAddr, len);
DWORD dwErr;
if(0 != iFail)
{
dwErr = ::WSAGetLastError();
printf("\n\t Error occured.\n");
return;
}
iFail = ::listen(sTCPServer, 2);
struct sockaddr_in saClient;
int iClsize = sizeof(saClient);
SOCKET sClient = ::accept(sTCPServer, (struct sockaddr*)&saClient ,&iClsize);
char strData[1024];
::recv(sClient, strData, 1024, 0);
printf("\n\nRealServer--Data from client --- %s ---", strData);
::shutdown(sTCPServer, SD_BOTH);
::WSACleanup();
return;
}
Solution
Wow, we had a lot of great posts to this one!
Assuming nothing was missed, here is the solution:
void Socket_Setup(void)
{
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD( 2, 2 );
::WSAStartup(wVersionRequested, &wsaData);
SOCKET sTCPServer = ::socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in saTCPServAddr;
saTCPServAddr.sin_family = AF_INET;
saTCPServAddr.sin_addr.S_un.S_addr = ::htonl(INADDR_ANY);
saTCPServAddr.sin_port = ::htons(5678);
int len = sizeof(saTCPServAddr);
//Approach 1
//Enable exclusive use so that another process cannot bind to the same socket
//BOOL bExclusiveUse = TRUE;
//int iValLen = sizeof(BOOL);
//::setsockopt(sTCPServer, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
//(char*)&bExclusiveUse, iValLen);
int iFail =::bind(sTCPServer, (struct sockaddr*)&saTCPServAddr, len);
DWORD dwErr;
if(0 != iFail)
{
dwErr = ::WSAGetLastError();
printf("\n\t Error occured.\n");
return;
}
//Approach 2:
//Strong ACL the socket only local admin’s and local service can bind
//Starting Win 2003 Server SP1
PSECURITY_DESCRIPTOR psD = NULL;
if(!ConvertStringSecurityDescriptorToSecurityDescriptor(
"D:(A;;GA;;;LS)(A;;GA;;;BA)",
SECURITY_DESCRIPTOR_REVISION,
&psD, NULL))
{
printf("Convert Failed \n");
}
if(!SetKernelObjectSecurity(
(HANDLE)sTCPServer,
DACL_SECURITY_INFORMATION, psD))
{
printf ("SetKernelObjectSecurity failed %d",GetLastError());
}
iFail = ::listen(sTCPServer, 2);
struct sockaddr_in saClient;
int iClsize = sizeof(saClient);
SOCKET sClient = ::accept(sTCPServer, (struct sockaddr*)&saClient ,&iClsize);
char strData[1024];
::recv(sClient, strData, 1024, 0);
printf("\n\nRealServer--Data from client --- %s ---", strData);
::shutdown(sTCPServer, SD_BOTH);
::WSACleanup();
return;
}
Description:
Creating sockets and binding them to any interface can be tricky. This can enable a malicious attacker to write a malicious server program which binds to same port but to a specific port which results in the malicious server getting the client connections along with their data. This is referred as “socket hijacking”. One approach is to strong ACL the socket (starting Win 2003 server and above). The above example shows the socket to be strong ACLed to allow only the local administrators and the Local Service account. Another approach is to set the “SO_EXCLUSIVEADDRUSE” socket option which disallows another process to bind to the same port on all available interfaces.