doexec.c (12081B)
1 // for license see license.txt 2 3 // Modified 12/27/2004 by Chris Wysopal <weld@vulnwatch.com> 4 // fixed vulnerability found by hat-squad 5 6 // portions Copyright (C) 1994 Nathaniel W. Mishkin 7 // code taken from rlogind.exe 8 9 #include <stdlib.h> 10 #include <winsock2.h> 11 #include <winbase.h> 12 13 #ifdef GAPING_SECURITY_HOLE 14 15 16 #define BUFFER_SIZE 200 17 18 extern char * pr00gie; 19 void holler(char * str, char * p1, char * p2, char * p3, char * p4, char * p5, char * p6); 20 char smbuff[20]; 21 // 22 // Structure used to describe each session 23 // 24 typedef struct { 25 26 // 27 // These fields are filled in at session creation time 28 // 29 HANDLE ReadPipeHandle; // Handle to shell stdout pipe 30 HANDLE WritePipeHandle; // Handle to shell stdin pipe 31 HANDLE ProcessHandle; // Handle to shell process 32 33 // 34 // 35 // These fields are filled in at session connect time and are only 36 // valid when the session is connected 37 // 38 SOCKET ClientSocket; 39 HANDLE ReadShellThreadHandle; // Handle to session shell-read thread 40 HANDLE WriteShellThreadHandle; // Handle to session shell-read thread 41 42 } SESSION_DATA, *PSESSION_DATA; 43 44 45 // 46 // Private prototypes 47 // 48 49 static HANDLE 50 StartShell( 51 HANDLE StdinPipeHandle, 52 HANDLE StdoutPipeHandle 53 ); 54 55 static VOID 56 SessionReadShellThreadFn( 57 LPVOID Parameter 58 ); 59 60 static VOID 61 SessionWriteShellThreadFn( 62 LPVOID Parameter 63 ); 64 65 66 67 // ********************************************************************** 68 // 69 // CreateSession 70 // 71 // Creates a new session. Involves creating the shell process and establishing 72 // pipes for communication with it. 73 // 74 // Returns a handle to the session or NULL on failure. 75 // 76 77 static PSESSION_DATA 78 CreateSession( 79 VOID 80 ) 81 { 82 PSESSION_DATA Session = NULL; 83 BOOL Result; 84 SECURITY_ATTRIBUTES SecurityAttributes; 85 HANDLE ShellStdinPipe = NULL; 86 HANDLE ShellStdoutPipe = NULL; 87 88 // 89 // Allocate space for the session data 90 // 91 Session = (PSESSION_DATA) malloc(sizeof(SESSION_DATA)); 92 if (Session == NULL) { 93 return(NULL); 94 } 95 96 // 97 // Reset fields in preparation for failure 98 // 99 Session->ReadPipeHandle = NULL; 100 Session->WritePipeHandle = NULL; 101 102 103 // 104 // Create the I/O pipes for the shell 105 // 106 SecurityAttributes.nLength = sizeof(SecurityAttributes); 107 SecurityAttributes.lpSecurityDescriptor = NULL; // Use default ACL 108 SecurityAttributes.bInheritHandle = TRUE; // Shell will inherit handles 109 110 Result = CreatePipe(&Session->ReadPipeHandle, &ShellStdoutPipe, 111 &SecurityAttributes, 0); 112 if (!Result) { 113 holler("Failed to create shell stdout pipe, error = %s", 114 itoa(GetLastError(), smbuff, 10), NULL, NULL, NULL, NULL, NULL); 115 goto Failure; 116 } 117 Result = CreatePipe(&ShellStdinPipe, &Session->WritePipeHandle, 118 &SecurityAttributes, 0); 119 120 if (!Result) { 121 holler("Failed to create shell stdin pipe, error = %s", 122 itoa(GetLastError(), smbuff, 10), NULL, NULL, NULL, NULL, NULL); 123 goto Failure; 124 } 125 // 126 // Start the shell 127 // 128 Session->ProcessHandle = StartShell(ShellStdinPipe, ShellStdoutPipe); 129 130 // 131 // We're finished with our copy of the shell pipe handles 132 // Closing the runtime handles will close the pipe handles for us. 133 // 134 CloseHandle(ShellStdinPipe); 135 CloseHandle(ShellStdoutPipe); 136 137 // 138 // Check result of shell start 139 // 140 if (Session->ProcessHandle == NULL) { 141 holler("Failed to execute shell", NULL, 142 NULL, NULL, NULL, NULL, NULL); 143 144 goto Failure; 145 } 146 147 // 148 // The session is not connected, initialize variables to indicate that 149 // 150 Session->ClientSocket = INVALID_SOCKET; 151 152 // 153 // Success, return the session pointer as a handle 154 // 155 return(Session); 156 157 Failure: 158 159 // 160 // We get here for any failure case. 161 // Free up any resources and exit 162 // 163 164 if (ShellStdinPipe != NULL) 165 CloseHandle(ShellStdinPipe); 166 if (ShellStdoutPipe != NULL) 167 CloseHandle(ShellStdoutPipe); 168 if (Session->ReadPipeHandle != NULL) 169 CloseHandle(Session->ReadPipeHandle); 170 if (Session->WritePipeHandle != NULL) 171 CloseHandle(Session->WritePipeHandle); 172 173 free(Session); 174 175 return(NULL); 176 } 177 178 179 180 BOOL 181 doexec( 182 SOCKET ClientSocket 183 ) 184 { 185 PSESSION_DATA Session = CreateSession(); 186 SECURITY_ATTRIBUTES SecurityAttributes; 187 DWORD ThreadId; 188 HANDLE HandleArray[3]; 189 int i; 190 191 SecurityAttributes.nLength = sizeof(SecurityAttributes); 192 SecurityAttributes.lpSecurityDescriptor = NULL; // Use default ACL 193 SecurityAttributes.bInheritHandle = FALSE; // No inheritance 194 195 // 196 // Store the client socket handle in the session structure so the thread 197 // can get at it. This also signals that the session is connected. 198 // 199 Session->ClientSocket = ClientSocket; 200 201 // 202 // Create the session threads 203 // 204 Session->ReadShellThreadHandle = 205 CreateThread(&SecurityAttributes, 0, 206 (LPTHREAD_START_ROUTINE) SessionReadShellThreadFn, 207 (LPVOID) Session, 0, &ThreadId); 208 209 if (Session->ReadShellThreadHandle == NULL) { 210 holler("Failed to create ReadShell session thread, error = %s", 211 itoa(GetLastError(), smbuff, 10), NULL, NULL, NULL, NULL, NULL); 212 213 // 214 // Reset the client pipe handle to indicate this session is disconnected 215 // 216 Session->ClientSocket = INVALID_SOCKET; 217 return(FALSE); 218 } 219 220 Session->WriteShellThreadHandle = 221 CreateThread(&SecurityAttributes, 0, 222 (LPTHREAD_START_ROUTINE) SessionWriteShellThreadFn, 223 (LPVOID) Session, 0, &ThreadId); 224 225 if (Session->WriteShellThreadHandle == NULL) { 226 holler("Failed to create ReadShell session thread, error = %s", 227 itoa(GetLastError(), smbuff, 10), NULL, NULL, NULL, NULL, NULL); 228 229 // 230 // Reset the client pipe handle to indicate this session is disconnected 231 // 232 Session->ClientSocket = INVALID_SOCKET; 233 234 TerminateThread(Session->WriteShellThreadHandle, 0); 235 return(FALSE); 236 } 237 238 // 239 // Wait for either thread or the shell process to finish 240 // 241 242 HandleArray[0] = Session->ReadShellThreadHandle; 243 HandleArray[1] = Session->WriteShellThreadHandle; 244 HandleArray[2] = Session->ProcessHandle; 245 246 247 i = WaitForMultipleObjects(3, HandleArray, FALSE, 0xffffffff); 248 249 250 switch (i) { 251 case WAIT_OBJECT_0 + 0: 252 TerminateThread(Session->WriteShellThreadHandle, 0); 253 TerminateProcess(Session->ProcessHandle, 1); 254 break; 255 256 case WAIT_OBJECT_0 + 1: 257 TerminateThread(Session->ReadShellThreadHandle, 0); 258 TerminateProcess(Session->ProcessHandle, 1); 259 break; 260 case WAIT_OBJECT_0 + 2: 261 TerminateThread(Session->WriteShellThreadHandle, 0); 262 TerminateThread(Session->ReadShellThreadHandle, 0); 263 break; 264 265 default: 266 holler("WaitForMultipleObjects error: %s", 267 itoa(GetLastError(), smbuff, 10), NULL, NULL, NULL, NULL, NULL); 268 269 break; 270 } 271 272 273 // Close my handles to the threads, the shell process, and the shell pipes 274 shutdown(Session->ClientSocket, SD_BOTH); 275 closesocket(Session->ClientSocket); 276 277 DisconnectNamedPipe(Session->ReadPipeHandle); 278 CloseHandle(Session->ReadPipeHandle); 279 280 DisconnectNamedPipe(Session->WritePipeHandle); 281 CloseHandle(Session->WritePipeHandle); 282 283 284 CloseHandle(Session->ReadShellThreadHandle); 285 CloseHandle(Session->WriteShellThreadHandle); 286 287 CloseHandle(Session->ProcessHandle); 288 289 free(Session); 290 291 return(TRUE); 292 } 293 294 295 // ********************************************************************** 296 // 297 // StartShell 298 // 299 // Execs the shell with the specified handle as stdin, stdout/err 300 // 301 // Returns process handle or NULL on failure 302 // 303 304 static HANDLE 305 StartShell( 306 HANDLE ShellStdinPipeHandle, 307 HANDLE ShellStdoutPipeHandle 308 ) 309 { 310 PROCESS_INFORMATION ProcessInformation; 311 STARTUPINFO si; 312 HANDLE ProcessHandle = NULL; 313 314 // 315 // Initialize process startup info 316 // 317 si.cb = sizeof(STARTUPINFO); 318 si.lpReserved = NULL; 319 si.lpTitle = NULL; 320 si.lpDesktop = NULL; 321 si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0L; 322 si.wShowWindow = SW_HIDE; 323 si.lpReserved2 = NULL; 324 si.cbReserved2 = 0; 325 326 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; 327 328 si.hStdInput = ShellStdinPipeHandle; 329 si.hStdOutput = ShellStdoutPipeHandle; 330 331 DuplicateHandle(GetCurrentProcess(), ShellStdoutPipeHandle, 332 GetCurrentProcess(), &si.hStdError, 333 DUPLICATE_SAME_ACCESS, TRUE, 0); 334 335 if (CreateProcess(NULL, pr00gie, NULL, NULL, TRUE, 0, NULL, NULL, 336 &si, &ProcessInformation)) 337 { 338 ProcessHandle = ProcessInformation.hProcess; 339 CloseHandle(ProcessInformation.hThread); 340 } 341 else 342 holler("Failed to execute shell, error = %s", 343 itoa(GetLastError(), smbuff, 10), NULL, NULL, NULL, NULL, NULL); 344 345 346 return(ProcessHandle); 347 } 348 349 350 // ********************************************************************** 351 // SessionReadShellThreadFn 352 // 353 // The read thread procedure. Reads from the pipe connected to the shell 354 // process, writes to the socket. 355 // 356 357 static VOID 358 SessionReadShellThreadFn( 359 LPVOID Parameter 360 ) 361 { 362 PSESSION_DATA Session = Parameter; 363 BYTE Buffer[BUFFER_SIZE]; 364 BYTE Buffer2[BUFFER_SIZE*2+30]; 365 DWORD BytesRead; 366 367 // this bogus peek is here because win32 won't let me close the pipe if it is 368 // in waiting for input on a read. 369 while (PeekNamedPipe(Session->ReadPipeHandle, Buffer, sizeof(Buffer), 370 &BytesRead, NULL, NULL)) 371 { 372 DWORD BufferCnt, BytesToWrite; 373 BYTE PrevChar = 0; 374 375 if (BytesRead > 0) 376 { 377 ReadFile(Session->ReadPipeHandle, Buffer, sizeof(Buffer), 378 &BytesRead, NULL); 379 } 380 else 381 { 382 Sleep(50); 383 continue; 384 } 385 386 387 388 // 389 // Process the data we got from the shell: replace any naked LF's 390 // with CR-LF pairs. 391 // 392 for (BufferCnt = 0, BytesToWrite = 0; BufferCnt < BytesRead; BufferCnt++) { 393 if (Buffer[BufferCnt] == '\n' && PrevChar != '\r') 394 Buffer2[BytesToWrite++] = '\r'; 395 PrevChar = Buffer2[BytesToWrite++] = Buffer[BufferCnt]; 396 } 397 398 if (send(Session->ClientSocket, Buffer2, BytesToWrite, 0) <= 0) 399 break; 400 } 401 402 if (GetLastError() != ERROR_BROKEN_PIPE) 403 holler("SessionReadShellThreadFn exitted, error = %s", 404 itoa(GetLastError(), smbuff, 10), NULL, NULL, NULL, NULL, NULL); 405 406 ExitThread(0); 407 } 408 409 410 // ********************************************************************** 411 // SessionWriteShellThreadFn 412 // 413 // The write thread procedure. Reads from socket, writes to pipe connected 414 // to shell process. 415 416 417 static VOID 418 SessionWriteShellThreadFn( 419 LPVOID Parameter 420 ) 421 { 422 PSESSION_DATA Session = Parameter; 423 BYTE RecvBuffer[1]; 424 BYTE Buffer[BUFFER_SIZE]; 425 DWORD BytesWritten; 426 DWORD BufferCnt; 427 428 BufferCnt = 0; 429 430 // 431 // Loop, reading one byte at a time from the socket. 432 // 433 while (recv(Session->ClientSocket, RecvBuffer, sizeof(RecvBuffer), 0) != 0) { 434 435 Buffer[BufferCnt++] = RecvBuffer[0]; 436 if (RecvBuffer[0] == '\r') 437 Buffer[BufferCnt++] = '\n'; 438 439 440 // Trap exit as it causes problems 441 if (strnicmp(Buffer, "exit\r\n", 6) == 0) 442 ExitThread(0); 443 444 445 // 446 // If we got a CR, it's time to send what we've buffered up down to the 447 // shell process. 448 // SECURITY FIX: CW 12/27/04 Add BufferCnt size check. If we hit end of buffer, flush it 449 if (RecvBuffer[0] == '\n' || RecvBuffer[0] == '\r' || BufferCnt > BUFFER_SIZE-1) { 450 if (! WriteFile(Session->WritePipeHandle, Buffer, BufferCnt, 451 &BytesWritten, NULL)) 452 { 453 break; 454 } 455 BufferCnt = 0; 456 } 457 } 458 459 ExitThread(0); 460 } 461 462 #endif