Link to home
Start Free TrialLog in
Avatar of ozymandias
ozymandiasFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Impersonation & Cloaking

I am looking for a very simple set of example code that uses impersonation cloaking.

1) A server app that listens on a tcp/ip port. When it receives a request from a client it will authenticate that client and then impersonate the client by trying to read a file using the client's credentials. If it can read the file it returns "yes" or some equivalent. If it can't it returns "no" or some equivalent.

2) A client app that will send requests to the server app defined above.

Both can be very basic and command-line based.
I just want a working example that illustrates the techniques involved.
Avatar of Kocil
Kocil

Beg you pardon, this is a new terminology for me:
  impersonation cloaking.
  impersonate the client
  client's credentials

What the **** is that ?
Avatar of Kent Olsen

It certainly appears that you're looking for help building hacker tools.  Before anyone offers any help I hope that they'll insist that you demonstrate a legitimate use.


Kdo
ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of ozymandias

ASKER

This is nothing to do with hacker tools.

I want to create a server application that will authenticate a client using the native security provider on Windows2000.

Basically, I have some files that contain data and the data in the files can only be read by my data engine. My data engine acts as a server and listens on a tcp/ip port for a client request.

When it gets a client request, it authenticates the client and then attaches the clients credentials to a thread which will be used to read the data files. If the user does not have permission to read the files then the thread will not be able to read the files.

This method of getting a thread/process to use a process token that has a client's credentials attached is called impersonation. IN COM and COM+ you can set different impersonation levels and then use cloaking to impersonate a client when accessing resources.

I don't have any experiewnce of doing this but I am familiar with the concepts involved so I want tome example code to start with.
jkr...thanks for the pointer to the ms examples.
I will dowload the file (130MB !) and have a look.

500 points +more + A grade still open to anyone who can provide me with working exmaples of the code detailed in my original post.
I use MFC to save time messing with user interface and to use CString.  The core logic will be obvious and usefule even if you have an aversion to MFC.

Here's the client.  Just ctrate a new dialog-based app and add a button and put all of this in the OnButton1 code:
#include <afxsock.h>

void CDlgSockClientDlg::OnButton1()
{
     AfxSocketInit();   // put this eleshwere ... just call it once

     CString sSocketIP= "127.0.0.1";
     CString sPortNum=  "12345";
     DWORD nLastErr= 0;

     int nPortNum=  atoi( sPortNum );

     CSocket cSocket;

     BOOL fRet= cSocket.Create();
     if ( fRet == 0 ) {
          MessageBox( "Unable to create socket" );
          nLastErr= GetLastError();  // TBD: display err code
          return;
     }

     fRet= cSocket.Connect( sSocketIP, nPortNum );
     if ( fRet == 0 ) {
          MessageBox( "Unable to connect to host" );
          nLastErr= GetLastError();  // TBD: display err code
          return;
     }
:

   CString sPkt;
   CString sPayload= "username:password@.";  
   sPkt.Format("DR%04d%s", sPayload.GetLength(), sPayload );

     CSocketFile file( &cSocket );
     try {
          CArchive    arOut(&file, CArchive::store | CArchive::bNoFlushOnDelete ); // noDelete prevents the outer exception
          arOut.Write( (LPCSTR)sPkt, sPkt.GetLength() );
          arOut.Flush();
     }
     catch( CException* pEx ) {
          MessageBox( "Error writing to socket" );
          pEx->Delete();
          return;
     }
     MessageBox( "successfully sent packet" );
     
     CArchive    arIn( &file, CArchive::load);
     
     int nActual;
     char buf[100];
     CString sResponse;

     try {
          nActual= arIn.Read( buf, 6 ); // get hdr
     }
     catch(...) {
          MessageBox( "Error reading from socket" );
          return;
     }
     if ( nActual != 6 ) {
          sResponse=  "Error! short packet: ";
          sResponse+= CString( buf, nActual );
          MessageBox( sResponse );
          return;
     }
     CString sTmp= CString( &buf[2], 4 );
     int nDataLen= atoi( sTmp );
     // LogPrintf( "Pkt Hdr: Len= %d", (int) nAceMsgLen );

     //----------------------------------------------- read the rest of the msg
     nActual= arIn.Read( buf, nDataLen );  
     buf[nDataLen]= 0;
     MessageBox( buf,"response from host" );

}
host coming...
I created the host from an existing host simulator.  It is also a simple dialog-based MFC app.  It is slightly more comples, in that there si a multi-line edit box used to display a log of actions.  Just draw an edit box, giving it a n ID of IDED_Log.  Use the Classwizard to associate it with a Control variable, m_ctlEditLog.  You'll end up with this in the DoDataExchange

      DDX_Control(pDX, IDED_Log, m_ctlEditLog);

I added this near the top:

//----------- a slight embellishemt to CSocket calls my local fn to hand incomming
class CListen : public CSocket {
public:
      CListen() {};
      virtual ~CListen() {};
public:
      virtual void OnAccept(int nErrorCode);
protected:
};
void CListen::OnAccept(int nErrorCode)
{
      AddToLog("On Accept" );
      CSocket::OnAccept(nErrorCode);
      gpDlg->DoOnAccept();
}

CListen*     gpSocket;
CDlgSockDlg* gpDlg;
BOOL         TextFileToStr( LPCSTR sFilename, CString& s );

//---------------------------- message logging
void AddToLog( LPCSTR s ) { gpDlg->AddToLog(s); }
void AddToLog( LPCSTR s, DWORD n ) {
      CString sTmp; sTmp.Format("%s : %x", s, n );
      AddToLog( sTmp );
}
=-=--==-=-=--==-=-=-=-=-=-=-=-=-=-=-=-=-
I added a [Start Listening] button with this handler:

void CDlgSockDlg::OnPbListen()
{
      CString sMsg;
      UpdateData( TRUE ); // get portnum
      gpSocket= new CListen();

      if ( ! gpSocket ) {
            AfxMessageBox ( "Unable to allocate memory for listener socket!" ) ;
            return;
      }
      if ( ! gpSocket->Create ( m_nPortNum ) ) {
            DWORD dwErr = gpSocket->GetLastError() ;
            switch ( dwErr ) {
                  case WSAEADDRINUSE:  // example of expected error handler
                        AfxMessageBox ( "The WWW port is already in use!" ) ;
                  break ;
            default:  // example of generic error handler
                  AfxMessageBox ( "Listener socket Create failed: %x", dwErr ) ;
            }
            return;
      }
      BOOL fRet = gpSocket->Listen() ;
      if ( ! fRet ) {
            DWORD dwErr = gpSocket->GetLastError() ;
            sMsg.Format ( "Listener socket Listen failed: %x\n", dwErr );
            AfxMessageBox ( sMsg ) ;
      }
      sMsg.Format("Listening on port: %d", m_nPortNum );
      AddToLog( sMsg );
}
=-=--==-=-=--==-=-=-=-=-=-=-=-=-=-=-=-=-
Here's the code that interprets the data coming from the client:

//-------------------------------------- client is pressing our buttons
// look for a 2-letter signature, a 4-digit packet length, then the user:password@domain
//
void CDlgSockDlg::DoOnAccept()
{
      CString sMsg;
      CString sHdr, sData;
      int     nRespLen;
      char    buf[400];          
      
      AddToLog("Connection requested");

      CSocket sockRecv;
      gpSocket->Accept( sockRecv );

      CSocketFile file( &sockRecv );
      CArchive    arIn( &file, CArchive::load);
      CArchive   arOut( &file, CArchive::store);

      int nActual= arIn.Read( buf, 6 ); // length for subsequent response data (not including this)

      if ( nActual != 6 ) {
            AddToLog("Rx: Unexpected/Invalid packet" );
            return;
      }
      sHdr= CString( buf, 6 );
      if ( sHdr.Left(2) != "DR" ) {
            AddToLog("Rx: Unexpected/Invalid packet" );
      }
      nRespLen= atoi( sHdr.Mid(2) );

      sMsg.Format( "Data Pkt: Len= %d ", nRespLen );
      AddToLog( sMsg );

      //----- read the packet data "username:password"
      nActual= arIn.Read( buf, nRespLen );  
      sData= CString( buf, nRespLen );

      sMsg.Format("Rx: Data: '%s'", (LPCSTR)sData );
      AddToLog( sMsg );

      int nOffset= sData.Find(":");
      if (nOffset == -1 ) {
            AddToLog("Rx: Yukky (malformed) data in packet" );
            return;
      }

      //------------------------------- take the nibey shot
      CString sUser=   sData.Left( nOffset);
      CString sPswd=   sData.Mid( nOffset+1);
      CString sDomain= "";
      nOffset= sPswd.Find("@");
      if ( nOffset != -1 ) {
            sDomain= sPswd.Mid( nOffset+1 );
            sPswd = sPswd.Left( nOffset );
      }

      bool fRet= VerifyCredentials( sUser, sPswd, sDomain );

      //------------------------------- respond to the request
      CString sPacket=   "DR0003";
      sPacket += fRet ? "yes" : "no ";

      arOut.Write( (LPCSTR)sPacket, sPacket.GetLength() );  
      arOut.Flush();

      sMsg.Format("Wrote %d Bytes of data: %s...", sPacket.GetLength(), (LPCSTR)sPacket );
      AddToLog( sMsg );
      
      return;
}

=-=-=-=-=-=-=-=-=-=-=-=-
Conttinued...
Heres' the code that validates the credentials:

#define CSTR_TestFileName "c:\\temp\\test.txt"

bool VerifyCredentials( LPCSTR sUser, LPCSTR sPswd, LPCSTR sDomain )
{
     HANDLE hToken;
     if (sDomain == "" ) {
          sDomain= 0;
     }
     BOOL fRet= ::LogonUser( (char*)sUser,(char*)sDomain,(char*)sPswd,
          LOGON32_LOGON_BATCH,
          LOGON32_PROVIDER_DEFAULT,
          &hToken
     );
     if ( !fRet) {
          AddToLog("Unable to logon with user/pswd/domain credentials",GetLastError() );
          DWORD dwErr= GetLastError();
          return( false );
     }

     fRet= ::ImpersonateLoggedOnUser( hToken );
     if ( !fRet) {
          AddToLog("ImpersonateLoggedOnUser() failed",GetLastError() );
          return( false );
     }

     CFile cf;
     fRet= cf.Open( CSTR_TestFileName, CFile::modeReadWrite );
     if ( !fRet ) {
          AddToLog( "OpenFile failed",GetLastError() );
          ::RevertToSelf();
          return( false );
     }
     //---------------------- unnecessary read test to satisfy the spec
     char buf[10];
     int nActual= cf.Read( buf, 10 );
     if (nActual != 10 ) {
          AddToLog( "Read File failed",GetLastError() );
          ::RevertToSelf();
          cf.Close();
          return( false );
     }

     return( true );
}

=-=-=-=-=-=-=-=-=-=-=-=-=-
And there you habe it!

In all socket operations, I always send and recieve packets.  Here I used a 2-letter signature, followed by a four-digit length (all in text).  That way, I always know exactly how many bytes to expect and I don't need to hang around for a time out.  

-- Dan
oops, here's something missing:
void CDlgSockDlg::AddToLog( LPCSTR s )
{
     m_ctlEditLog.SetSel(-1,-1);
     m_ctlEditLog.ReplaceSel(s);
     m_ctlEditLog.ReplaceSel("\r\n" );
}

And a hnddy utility (not used in the program, consider ti a bonus :)

//-------------------------------
// read an entire text file into a string
//
BOOL TextFileToStr( LPCSTR sFilename, CString& s )
{
     char sBuf[513];
     HFILE hFile= _lopen(sFilename, OF_READ );

     if (hFile == HFILE_ERROR ) {
          return( FALSE );
     }
     int nFileLen= _llseek( hFile, 0L, SEEK_END );
     _llseek( hFile, 0L, SEEK_SET );
     for (int j=0; j<nFileLen; j += 512) {
          int nActual= _lread( hFile, sBuf, 512 );
          sBuf[nActual]= 0;
          s += sBuf;
     }
     _lclose( hFile );
     //s.Replace("\r",NULL);    
     return( TRUE );
}
wow dan...you've been busy.

Thanks.

I'll have a look at all that and get back to you.
Hi, Dan.
Please forgive my ignorance but from reading your code it looks like "username:password@domain" is passed in plain text to the server.

a) This is not a good idea
b) it requires the user of the client to enter this information.

I should have made this clearer but I wanted the client to pass the credentials (token) of the user currently logged on the workstation.

Anyway, from the code in AuthSocket provided by jkr courtesy of MSDN and the stuff you've posted I think I have enough to be going on with.

I will award points to jkr and post extra points for Dan in this topic area.

Cheers.
Thanks again.