How to create a named channel in Windows so that you can read from it and write data to it without any logins and passwords from computers in the local network? Security flaws when using such a channel do not bother me, as this is a training program. I would like to do without complex settings in the OS. It is necessary that it works at least in Windows XP and 7.

Here is an example that works on one computer, but not on several. Server code:

#include <windows.h> #include <iostream> char msg1[]="Message1"; char msg2[]="Message2"; int main(){ std::string c; SECURITY_ATTRIBUTES sa={0}; SECURITY_DESCRIPTOR sd={0}; InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl( &sd, TRUE, NULL, FALSE); sa.bInheritHandle=false; sa.lpSecurityDescriptor=&sd; sa.nLength=sizeof(sa); HANDLE ch1=CreateNamedPipe ( "\\\\.\\pipe\\testpipe", PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE| PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, sizeof msg1, 4, 0, &sa ); if(ch1==INVALID_HANDLE_VALUE){ std::cout<<"ch1 "<<GetLastError()<<"\n"; std::getline(std::cin, c); return 1; } HANDLE ch2=CreateNamedPipe( "\\\\.\\pipe\\testpipe", PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE| PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, sizeof msg1, 4, 0, &sa ); if(ch2==INVALID_HANDLE_VALUE){ std::cout<<"ch2 "<<GetLastError()<<"\n"; std::getline(std::cin, c); return 1; } ConnectNamedPipe(ch1, 0); ConnectNamedPipe(ch2, 0); unsigned long foo; if(TransactNamedPipe(ch1, msg1, sizeof msg1, &foo, sizeof foo, &foo, 0)==0){ std::cout<<"transact1 "<<GetLastError()<<"\n"; std::getline(std::cin, c); return 1; } if(TransactNamedPipe(ch2, msg2, sizeof msg2, &foo, sizeof foo, &foo, 0)==0){ std::cout<<"transact2 "<<GetLastError()<<"\n"; std::getline(std::cin, c); return 1; } std::cout<<"Finished\n"; std::getline(std::cin, c); return 0; } 

Client code:

 #include <windows.h> #include <iostream> char msg1[]="Message1\n"; char msg2[]="Message2\n"; int main(){ std::string c; std::cout<<"Server name:\n"; std::string sname; std::cin>>sname; std::cin.ignore(); NETRESOURCE nr={0}; if(sname!="."){ std::string sname2=std::string("\\\\")+sname; nr.dwType = RESOURCETYPE_ANY; nr.lpRemoteName = &sname2[0]; DWORD ret=WNetAddConnection2( &nr, "", "", 0); if(ret!=NO_ERROR){ std::cout<<"WNetAddConnection2 "<<GetLastError()<<"\n"; std::getline(std::cin, c); return 1; } } std::string pname=std::string("\\\\")+sname+std::string("\\pipe\\testpipe"); HANDLE phandle1=CreateFile ( pname.c_str(), GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0,0 ); if(phandle1==INVALID_HANDLE_VALUE){ std::cout<<"CreateFile1 "<<GetLastError()<<"\n"; std::getline(std::cin, c); return 1; } HANDLE phandle2=CreateFile ( pname.c_str(), GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0,0 ); if(phandle2==INVALID_HANDLE_VALUE){ std::cout<<"CreateFile2 "<<GetLastError()<<"\n"; std::getline(std::cin, c); return 1; } char msg[sizeof(msg1)]; unsigned long s; unsigned long foo=0; if(ReadFile(phandle1, &msg, sizeof msg1, &s, 0)==0){ std::cout<<"ReadFile1 "<<GetLastError()<<"\n"; std::getline(std::cin, c); return 1; } std::cout<<msg<<"\n"; if(WriteFile (phandle1, &foo, sizeof foo, &foo, 0)==0){ std::cout<<"WriteFile1 "<<GetLastError()<<"\n"; std::getline(std::cin, c); return 1; } if(ReadFile(phandle2, &msg, sizeof msg1, &s, 0)==0){ std::cout<<"ReadFile2 "<<GetLastError()<<"\n"; std::getline(std::cin, c); return 1; } std::cout<<msg<<"\n"; if(WriteFile (phandle2, &foo, sizeof foo, &foo, 0)==0){ std::cout<<"WriteFile2 "<<GetLastError()<<"\n"; std::getline(std::cin, c); return 1; } std::cout<<"Finished\n"; std::getline(std::cin, c); return 0; } 

Specific error: 5 (ERROR_ACCESS_DENIED) during the first call to CreateFile in the client program.

Update

I figured out a bit with named pipes. It is also necessary to add on the machine where the server is running, in the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\lanmanserver\parameters NullSessionPipes in NullSessionPipes the channel name. Then the above code works if the server is Win XP and the client is Win 7. But on the contrary, for some reason it works only for reading ( GENERIC_READ instead of GENERIC_READ|GENERIC_WRITE in CreateFile ), otherwise ERROR_ACCESS_DENIED.

I also found an article about anonymous channels on MSDN. There is a much more complicated example, but it provides only read access, and I did not succeed in changing the code for the recording to work. I installed PIPE_ACCESS_DUPLEX instead of PIPE_ACCESS_OUTBOUND in CreateNamedPipe , GENERIC_READ | GENERIC_WRITE GENERIC_READ | GENERIC_WRITE instead of FILE_GENERIC_READ in AddAccessAllowedAce , GENERIC_READ | GENERIC_WRITE GENERIC_READ | GENERIC_WRITE instead of GENERIC_READ in CreateFile , but error 5 also occurs if the server is Win 7.

That's what's in the registry of Windows 7 (maybe somewhere there is an error?) The NullSessionPipes value contains the strings "testpipe" and "AnonymousPipe".

 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\LanmanServer\Parameters] "ServiceDll"=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,\ 00,74,00,25,00,5c,00,73,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,\ 73,00,72,00,76,00,73,00,76,00,63,00,2e,00,64,00,6c,00,6c,00,00,00 "ServiceDllUnloadOnStop"=dword:00000001 "EnableAuthenticateUserSharing"=dword:00000001 "NullSessionPipes"=hex(7):74,00,65,00,73,00,74,00,70,00,69,00,70,00,65,00,00,\ 00,41,00,6e,00,6f,00,6e,00,79,00,6d,00,6f,00,75,00,73,00,50,00,69,00,70,00,\ 65,00,00,00,00,00 "autodisconnect"=dword:0000000f "enableforcedlogoff"=dword:00000001 "enablesecuritysignature"=dword:00000000 "requiresecuritysignature"=dword:00000000 "restrictnullsessaccess"=dword:00000000 "Lmannounce"=dword:00000000 "Size"=dword:00000001 "AdjustedNullSessionPipes"=dword:00000003 "Guid"=hex:eb,c5,75,fd,46,51,ce,4c,8f,64,a9,70,12,f6,05,6e 

Tell me how to properly create an anonymous named channel?

Update 2

If you enable the “Network access: allow the use of“ For all ”permissions to anonymous users” setting or add the “Imitation of client after authentication” permission for the ANONYMOUS LOGON group, then my example and MSDN example work without errors. It is just not clear why - after all, impersonation functions are not called anywhere.

Update 3

Finally, I found articles explaining what was happening: https://blogs.technet.microsoft.com/nettracer/2010/07/23/why-does-anonymous-pipe-access-fail-on-windows-vista- 2008-windows-7-or-windows-2008-r2 / , http://blog.m-ri.de/index.php/2009/12/08/windows-integrity-control-schreibzugriff-auf-eine-named -pipe-eines-services-ueber-anonymen-zugriff-auf-vista-windows-2008-server-und-windows-7 / (in German, can be read in translation into English) The example given in the last article works without giving an error without the above settings! (You just need to add the channel name to NullSessionPipes )

Since I still announced a contest and the topic was practically not covered anywhere, it would be nice if someone wrote a detailed answer based on the articles listed.

  • "Here it is, Mikhalych!" (c) @Im ieee, good find! Perfectly earned and without access to IPC $ (only on XP the firewall had to be stopped; still, they write, you can allow port 445) You need to write the answer yourself, since this is your find (excellent, and very useful find!); I personally consider it unethical to use someone else’s answer (but to thank for it is a matter of honor). - SeNS
  • @SeNS the fact is that I can’t mark my own answer with a “reward”. But if no one answers, then, of course, I will. And with access to IPC $ - as I understand it, it includes the same setting, which increases the client Integrity Level. - Im ieee
  • >> But if no one answers, then, of course, I will. You have the fullest and inalienable right, because you yourself have found the answer! Regarding access to IPC $: no, the correct code (which led the German) only adds the name of the channel to NullSessionPipes in the registry. It does not give access to IPC $. Those. The gitkhabovsky code that I gave works only if anonymous access to IPC $ is allowed (which is a potential “hole” in security), the German code works without it (basically what you wanted). Maybe someday I will use this trick. - SeNS
  • That is, if you fix server.cpp in the gitkhabovsk code like this: //Define SDDL for the security descriptor PCWSTR szSDDL = L"D:" //Discretionary ACL // L"(A;OICI;GA;;;AN)" // L"(A;OICI;GA;;;AU)" //allow read/write to authenticated users // L"(A;OICI;GA;;;BA)" //allow full control to administrators // ; L"("(A;;GA;;;"))" L"(A;;GWGR;;;AN)" L"(A;;GWGR;;;WD)"; //Define SDDL for the security descriptor PCWSTR szSDDL = L"D:" //Discretionary ACL // L"(A;OICI;GA;;;AN)" // L"(A;OICI;GA;;;AU)" //allow read/write to authenticated users // L"(A;OICI;GA;;;BA)" //allow full control to administrators // ; L"("(A;;GA;;;"))" L"(A;;GWGR;;;AN)" L"(A;;GWGR;;;WD)"; then it will work. PS Sorry, I can’t format the code in stupid comments! - SeNS

1 answer 1

Here is a working example of named pipes. There is no need to subtilize with the registry; only, on the client in the source code you need to replace (to work on two different PCs on the network, and not on the same machine) #define SERVER_NAME L"YOUR_REAL_SERVER_NAME" (that is, the real windows name of the computer where the server will be running), You can leave #define SERVER_NAME L"." on the server #define SERVER_NAME L"."

[UPDATE] According to the answer found by @Im ieee, in the GitHub example, you need to replace lines 199-201 with:

 L"(A;OICI;GA;;;AN)" // allow full control to anonymous L"(A;OICI;GRGW;;;AU)" // allow read/write to authenticated users L"(A;OICI;GA;;;BA)" // allow full control to administrators ; 

and on the client add such lines before attempting to open an anonymous named pipe:

 NETRESOURCE nr; ZeroMemory( &nr, sizeof(nr) ); nr.dwType = RESOURCETYPE_ANY; nr.lpRemoteName = FULL_SERVER_NAME; dwError = WNetAddConnection2( &nr, L"", L"", 0); if( dwError != ERROR_SUCCESS ) { wprintf_s(L"WNetAddConnection2 fails"); goto Cleanup; } 

after which this solution will work (taking into account that the name of the test named pipe, SamplePipe, has been added to the registry variable SYSTEM \ CurrentControlSet \ Services \ lanmanserver \ parameters \ NullSessionPipes

Here is the complete solution code: client.cpp

 #pragma region Includes #include <stdio.h> #include <Windows.h> #pragma endregion #define SERVER_NAME L"VIRTUAL-XP" // change to your server name! #define FULL_SERVER_NAME L"\\\\" SERVER_NAME #define PIPE_NAME L"SamplePipe" #define FULL_PIPE_NAME FULL_SERVER_NAME L"\\pipe\\" PIPE_NAME #define BUFFER_SIZE 1024 #define REQUEST_MESSAGE L"Default request from client" int wmain(int argc, wchar_t * argv[]) { HANDLE hPipe = INVALID_HANDLE_VALUE; DWORD dwError = ERROR_SUCCESS; NETRESOURCE nr; ZeroMemory( &nr, sizeof(nr) ); nr.dwType = RESOURCETYPE_ANY; nr.lpRemoteName = FULL_SERVER_NAME; // These lines make client work with any server (including XP) // even if you not in domain or logged to the server // But don't forget to ass named pipe name to the registry on server! dwError = WNetAddConnection2( &nr, L"", L"", 0); if( dwError != ERROR_SUCCESS ) { wprintf_s(L"WNetAddConnection2 fails"); goto Cleanup; } while (TRUE) { hPipe = CreateFile( FULL_PIPE_NAME, GENERIC_READ | GENERIC_WRITE, 0, // no sharing nullptr, //default security attributes OPEN_EXISTING, //open existing pipe SECURITY_ANONYMOUS, //default attributes nullptr // no template file ); if (hPipe != INVALID_HANDLE_VALUE) { wprintf_s(L"The named pipe %s is now connected. \n", FULL_PIPE_NAME); break; } dwError = GetLastError(); if( dwError == ERROR_PIPE_BUSY ) { //all pipe instances are busy so wait for 5 secs if (!WaitNamedPipe(FULL_PIPE_NAME, 5000)) { dwError = GetLastError(); wprintf_s(L"Could not open named pipe. 5 sec timeout expired \n"); goto Cleanup; } } else { wprintf_s(L"Unable to open named pipe with error %08lx \n", dwError); goto Cleanup; } } // Set the read mode and blocking mode of the named pipe. Here , we set data to be read from the pipe as a stream of messages. DWORD dwMode = PIPE_READMODE_MESSAGE; if (!SetNamedPipeHandleState(hPipe, &dwMode, nullptr, nullptr)) { dwError = GetLastError(); wprintf_s(L"SetNamedPipeHandleState failed with error %08lx \n", dwError); goto Cleanup; } //Send request from client to server wchar_t chRequest[] = REQUEST_MESSAGE; DWORD cbRequest, cbWritten; cbRequest = sizeof(chRequest); if (!WriteFile(hPipe, chRequest, cbRequest, &cbWritten, nullptr)) { dwError = GetLastError(); wprintf_s(L"WriteFile failed with error message %08lx \n", dwError); goto Cleanup; } wprintf_s(L"Sent %ld bytes to server: %s \n", cbWritten, chRequest); // Receive a response from the server BOOL fFinishRead = TRUE; do { wchar_t chResponse[BUFFER_SIZE]; DWORD cbResponse, cbRead; cbResponse = sizeof(chResponse); fFinishRead = ReadFile(hPipe, chResponse, cbResponse, &cbRead, nullptr); dwError = GetLastError(); if (!fFinishRead && ERROR_MORE_DATA != dwError) { wprintf_s(L"Readfile from pipe %s failed with error %08lx \n", FULL_PIPE_NAME, dwError); break; } wprintf(L"Receive %ld bytes fom server : %s \n", cbRead, chResponse); } while (!fFinishRead); Cleanup: if (hPipe != INVALID_HANDLE_VALUE) { CloseHandle(hPipe); hPipe = INVALID_HANDLE_VALUE; } return dwError; } 

server.cpp

 /*************************************** PIPE_ACCESS_IMBOUND: Client (Generic_Write) -> Server( Generic_Read) PIPE_ACCESS_OUTBOUND: Server (Generic_Write) -> Client (Generic_Read) PIPE_ACCESS_DUPLEX: Client (Generic_Read or Generic_Write or Both) <--> Server (Generic_Read and Generic_Write) *******************************************/ #pragma region Includes #include <stdio.h> #include <windows.h> #include <sddl.h> //security descriptor description language #pragma endregion #define SERVER_NAME L"." #define PIPE_NAME L"SamplePipe" #define FULL_PIPE_NAME L"\\\\" SERVER_NAME L"\\pipe\\" PIPE_NAME //concatenation done by compiler #define BUFFER_SIZE 1024 #define RESPONSE_MESSAGE L"Default response from server" //Forward declarations of methods so that they can be used in main method. These will be defined later. BOOL CreatePipeSecurity(PSECURITY_ATTRIBUTES *); void FreePipeSecurity(PSECURITY_ATTRIBUTES ); int wmain(int argc, wchar_t * argv[]) { DWORD dwError = ERROR_SUCCESS; //error code definitions in winerr.h for windows apis PSECURITY_ATTRIBUTES pSA = nullptr; HANDLE hNamedPipe = INVALID_HANDLE_VALUE; //handleapi.h // Prepare the security attributes (the lpSecurityAttributes parameter in // CreateNamedPipe) for the pipe. This is optional. If the // lpSecurityAttributes parameter of CreateNamedPipe is NULL, the named // pipe gets a default security descriptor and the handle cannot be // inherited. The ACLs in the default security descriptor of a pipe grant // full control to the LocalSystem account, (elevated) administrators, // and the creator owner. They also give only read access to members of // the Everyone group and the anonymous account. However, if you want to // customize the security permission of the pipe, (eg to allow // Authenticated Users to read from and write to the pipe), you need to // create a SECURITY_ATTRIBUTES structure. if (!CreatePipeSecurity(&pSA)) //passing by ref so that the actual pointer value is updated. { dwError = GetLastError(); //errohandlingapi.h wprintf_s(L"CreatePipeSecurity failed with error 0x%08lx\n", dwError); // printf format %[parameter][flags][width][.precision][length]type goto Cleanup; } //Create Named pipe hNamedPipe = CreateNamedPipe( FULL_PIPE_NAME, PIPE_ACCESS_DUPLEX, //open mode PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, //max intances BUFFER_SIZE, //output buffer size BUFFER_SIZE, // input buffer size NMPWAIT_USE_DEFAULT_WAIT, //time out interval pSA ); if (hNamedPipe == INVALID_HANDLE_VALUE) { dwError = GetLastError(); wprintf_s(L"CreateNamedPipe failed with error %08lx\n", dwError); goto Cleanup; } wprintf_s(L"The named pipe (%s) has been successfully created. \n", FULL_PIPE_NAME); //wait for the client to connect. wprintf_s(L"Waiting for the client to connect. \n"); // Blocking call on server. //ConnectNamedPipe Enables a named pipe server process to wait for a client process to connect to an instance of a named pipe. A client process connects by calling either the CreateFile or CallNamedPipe function. if (!ConnectNamedPipe(hNamedPipe, nullptr)) //namedpipeapi.h { dwError = GetLastError(); if (dwError != ERROR_PIPE_CONNECTED) { wprintf_s(L"ConnectNamedPipe failed with error %08lx \n", dwError); goto Cleanup; } } wprintf(L"Client is connected.\n"); //Receive request from client. BOOL fFinishedRead = FALSE; do { wchar_t chRequest[BUFFER_SIZE]; DWORD cbRequest, cbRead; cbRequest = sizeof(chRequest); //another blocking call. fFinishedRead = ReadFile( //fileapi.h hNamedPipe, //file handle which is the named pipe handle chRequest, //buffer cbRequest, // no. of bytes to read &cbRead, // no. of bytes read , pass by ref so that the callee can update the value. nullptr ); dwError = GetLastError(); if (!fFinishedRead && dwError != ERROR_MORE_DATA) { wprintf_s(L"ReadFile failed with error %08lx \n", dwError); goto Cleanup; } wprintf_s(L"Received %ld bytes from client: %s \n", cbRead, chRequest); } while (!fFinishedRead); //repeat loop if ERROR_MORE_DATA. //send a response from server to client. wchar_t chResponse[] = RESPONSE_MESSAGE; DWORD cbResponse, cbWritten; cbResponse = sizeof(chResponse); //another blocking call. if (!WriteFile( hNamedPipe, chResponse, cbResponse, &cbWritten, nullptr )) { dwError = GetLastError(); wprintf_s(L"WriteFile failed with error %08lx \n", dwError); goto Cleanup; } wprintf_s(L"Sent %ld bytes to client: %s \n", cbWritten, chResponse); //Flush the pipe to allow the client to read the pipe's contents before disconnecting. FlushFileBuffers(hNamedPipe); DisconnectNamedPipe(hNamedPipe); Cleanup: if (!pSA) { FreePipeSecurity(pSA); pSA = nullptr; //always null after releasing. } if (hNamedPipe != INVALID_HANDLE_VALUE) { CloseHandle(hNamedPipe); hNamedPipe = INVALID_HANDLE_VALUE; } return dwError; } // // FUNCTION: CreatePipeSecurity(PSECURITY_ATTRIBUTES *) // // PURPOSE: The CreatePipeSecurity function creates and initializes a new // SECURITY_ATTRIBUTES structure to allow Authenticated Users read and // write access to a pipe, and to allow the Administrators group full // access to the pipe. // // PARAMETERS: // * ppSa - output a pointer to a SECURITY_ATTRIBUTES structure that allows // Authenticated Users read and write access to a pipe, and allows the // Administrators group full access to the pipe. The structure must be // freed by calling FreePipeSecurity. BOOL CreatePipeSecurity(PSECURITY_ATTRIBUTES *ppSA) { BOOL fSucceeded = TRUE; DWORD dwError = ERROR_SUCCESS; PSECURITY_DESCRIPTOR pSD = nullptr; PSECURITY_ATTRIBUTES pSA = nullptr; //Define SDDL for the security descriptor PCWSTR szSDDL = L"D:" //Discretionary ACL L"(A;OICI;GA;;;AN)" //allow full control to anonymous L"(A;OICI;GRGW;;;AU)" //allow read/write to authenticated users L"(A;OICI;GA;;;BA)" //allow full control to administrators ; if (!ConvertStringSecurityDescriptorToSecurityDescriptor(szSDDL, SDDL_REVISION_1, &pSD, nullptr)) { fSucceeded = FALSE; dwError = GetLastError(); goto Cleanup; } //allocate memory for security attributes pSA = (PSECURITY_ATTRIBUTES)LocalAlloc(LPTR, sizeof(*pSA)); //minwinbase.h for LPTR if (pSA == nullptr) { fSucceeded = FALSE; dwError = GetLastError(); goto Cleanup; } pSA->nLength = sizeof(*pSA); //sizeof(SECURITY_ATTRIBUTES) pSA->lpSecurityDescriptor = pSD; pSA->bInheritHandle = FALSE; *ppSA = pSA; Cleanup: if (!fSucceeded) { if (pSD) { LocalFree(pSD); //winbase.h pSD = nullptr; } if (pSA) { LocalFree(pSA); pSA = nullptr; } SetLastError(dwError); } return fSucceeded; } void FreePipeSecurity(PSECURITY_ATTRIBUTES pSA) { if (pSA) { if (pSA->lpSecurityDescriptor) { LocalFree(pSA->lpSecurityDescriptor); //clear its contents first. } LocalFree(pSA); //then clear itself. } } 

PS If you are going to build configurations in Release , do not forget to specify the _UNICODE; UNICODE; preprocessor options;

  • Yes, I checked on Win10 + Win7. - SeNS
  • one
    I also checked it between XP and 10, everything works. Important note: the client must have access to the IPC $ server (at least some), otherwise the client will crash with the error 0x0000052e, which means: "Logon failure: unknown user name or bad password. Access to IPC $ is denied. (Not the If you’re looking at the IPC resource itself.) - SeNS
  • Thank you for helping again! Unfortunately, this code also gives me error 5 in CreateFile on the client, and when server 7, and XP client, and when vice versa. I think it’s because permissions are given to the Authenticated group, and it doesn’t include anonymous users. In general, I have almost figured out (see Update 2 in the question), it is just not clear why such a strange resolution should be given. - Im ieee
  • I tried once more (server on XP, client on 10 ke), everything works provided that access to IPC $ is allowed. Unfortunately, I can not check on the network with the domain (only peer-to-peer). As I understand it, without the possibility of anonymous access to the server, there will be no access to the named pipes. Those. The problem is not in the code (yours, with MSDN or with github), but in the Windows settings. - SeNS
  • By the way, such an error as you (0x00000005, access denied) I have never met. Another interesting question: why do you use XP in work / training? Is this a real necessity, or just "got it under your arm"? XP is already outdated, and is very different (in internal design) from the modern "OS". - SeNS