2025年2月15日 星期六

RDP using Physical SmartCard from Remote Server Side

 Update for hacking using RDP on Windows RDP remote servers.

If you are trying to access a physical smart card on a Windows server or desktop computer, be aware that this feature is known to be restricted, as mentioned in the MSDN documentation:


Doc:  https://learn.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardestablishcontext


If the client attempts a smart card operation in a remote session, such as a client session running on a terminal server, and the operating system in use does not support smart card redirection, this function returns ERROR_BROKEN_PIPE.


To address this issue, we have confirmed that this behavior still occurs. By analyzing the DLL at the assembly level, we can bypass this function by applying an NOP (no-operation) instruction or forcing the EBX register to return a fixed value, effectively overriding the default behavior.


For reference, see this blog post:

http://lifayk.blogspot.com/2012/07/windows-smart-card-subsystem-and-remote.html


However, note that the post was written in 2012 and is now outdated. After further investigation using WinDbg to list functions from the SCard service by:

x WinSCard!*
or 

x *!*

Referring to the blog post mentioned earlier, we found that several functions—InTSMethodWithContext, InTSRedirectModeWithContext, and WinStationIsSessionRemoteable, it was no longer exist. This means we need to reanalyze and trace the feature again.

After listing functions and setting breakpoints to analyze their behavior, I found the following:

bm WinSCard!SCardEstablishContext  
bm WinSCard!SCardListReadersA
bm WinSCard!RedirectionContextIsLocal* 

When comparing breakpoints between an RDP session and a VNC session, we observed different behaviors. Unlike RDP, VNC is not a remote session, it directly controls the computer as if you were physically present. Because of this, some functions are not triggered in a VNC session, leading to different execution paths.

By tracing back using "step out," we found that RedirectionContextIsLocal is a crucial function. Some services check it first and return immediately based on its result, which affects the overall behavior of smart card operations in remote environments.

I suspect that this function is part of RDP's redirection mechanism. RDP has a feature that redirects the remote client’s hardware to the target server, allowing the server to list smart cards from the client session. This explains why certain functions behave differently in an RDP session compared to a local or VNC session.

We know this behavior is reasonable. However, consider a special case where you insert your smart card at home or in the office and need local access instead of remote access. In such cases, we may want to force the system to list the smart cards from the remote server's hardware rather than relying on client-side redirection.

Additionally, we can check whether the function "RedirectionContextIsLocal" returns a Long value. Though I suspect it simply returns 0 or 1 to represent true or false, I proceeded with that assumption.

To achieve this, we need to force the function to return 1, regardless of the process flow in the middle. Initially, check directly update the value by:
r ebx=1

but to do this, we have to find the position to execute it, so I attempted to modify the return value in some line by using:
mov ebx, 1  
ret

To verify this, we shouldn't rely solely on the application for testing. Instead, we should write a C# process to validate the behavior.


Before that, we directly modified the DLL by adding the following command in somewhere:

mov ebx, 1  
ret

However, it seems to have failed due to signature validation or another integrity check. So, it's better to find another way to modify it.

The first modification was to change the return value in al.



directly modify the al to be 1 by:
eb 7a876245 B0 01




Now we can see that EAX has been modified to 1 and returned.


To modify it, the quickest way is to search for the identity value based on its relative position and modify it. Here, we see that (7a876245) is an address, but it's random, so there's not much we can do with it. The original command (8ac7 = al, bh) is quite common, so it could yield multiple results.


So, we focus on the last line where the command is:

62496240 e87b11ffff   call    WinSCard!RedirectionContextRelease (624873c0)

This serves as the identifier, and e87b11ffff is complex enough that it probably won’t have any duplicate results. Therefore, let's search for this.



We can see that 8AC7 is the next command after e87b11ffff. Carefully replace it with B001.



Now we can check if is work, we can do this by using DllImport in C# to call the function directly and observe its response. Here's an example of how you can access the function using P/Invoke in C#:
namespace WinSCardTest
{
    class Program
    {
        [DllImport("winscard.dll")]
        private static extern int SCardTransmit(IntPtr hCard,
                                       IntPtr pioSendPci,
                                       byte[] pbSendBuffer,
                                       int cbSendLength,
                                       IntPtr pioRecvPci,
                                       byte[] pbRecvBuffer,
                                       ref int pcbRecvLength);

        // Scope values for the SCardEstablishContext function
        private const uint SCARD_SCOPE_USER = 0;   // User scope
        private const uint SCARD_SCOPE_TERMINAL = 1;   // Terminal scope
        private const uint SCARD_SCOPE_SYSTEM = 2;   // System scope

        // Return codes for the function (commonly used ones)
        private const int SCARD_S_SUCCESS = 0x00000000;  // Success

        // Importing the SCardEstablishContext function
        [DllImport("winscard.dll")]
        private static extern int SCardEstablishContext(
            uint dwScope,              // Scope of the resource manager context
            IntPtr pvReserved1,        // Reserved, must be null
            IntPtr pvReserved2,        // Reserved, must be null
            out IntPtr phContext       // Handle to the established resource manager context
        );

        [DllImport("winscard.dll")]
        private static extern int SCardListReaders(
        IntPtr hContext,
        string mszGroups,
        byte[] mszReaders,
        ref int pcchReaders);

        [DllImport("winscard.dll")]
        private static extern int SCardReleaseContext(IntPtr hContext);

        [DllImport("winscard.dll", CharSet = CharSet.Auto)]
        public static extern int SCardConnect(
            IntPtr hContext,
            string szReader,
            uint dwShareMode,
            uint dwPreferredProtocols,
            ref IntPtr phCard,
            ref uint pdwActiveProtocol
        );

        static void Main(string[] args)
        {
            IntPtr hContext = IntPtr.Zero;
            int result = SCardEstablishContext(SCARD_SCOPE_SYSTEM, IntPtr.Zero, IntPtr.Zero, out hContext);

            if (result != SCARD_S_SUCCESS)
            {
                Console.WriteLine($"SCardEstablishContext failed with error: 0x{result:X}");
                Console.WriteLine("Test - 3");
                Console.ReadLine();
                return;
            }

            Console.WriteLine("SCardEstablishContext succeeded.");

            // Get the size of the buffer for the reader names
            int readerListSize = 0;
            result = SCardListReaders(hContext, null, null, ref readerListSize);

            if (result != SCARD_S_SUCCESS)
            {
                Console.WriteLine($"SCardListReaders failed to get size with error: 0x{result:X}");
                SCardReleaseContext(hContext);
                Console.WriteLine("Test - 2");
                Console.ReadLine();
                return;
            }

            // Allocate buffer to hold the reader names
            byte[] readerList = new byte[readerListSize];
            result = SCardListReaders(hContext, null, readerList, ref readerListSize);

            if (result != SCARD_S_SUCCESS)
            {
                Console.WriteLine($"SCardListReaders failed to get readers with error: 0x{result:X}");
                SCardReleaseContext(hContext);
                Console.WriteLine("Test - 1");
                Console.ReadLine();
                return;
            }

            // Convert the null-separated list to a string array
            string readerNames = Encoding.ASCII.GetString(readerList).TrimEnd('\0');
            string[] readers = readerNames.Split('\0');

            Console.WriteLine("Available Readers:");
            foreach (string reader in readers)
            {
                Console.WriteLine($"- {reader}");
            }

            SCardReleaseContext(hContext);

            Console.WriteLine("Test");
            Console.ReadLine();


            ConnectToSmartCard("InfoThink USB Reader 0");

            Console.WriteLine("Test");
            Console.ReadLine();
        }

        public static IntPtr ConnectToSmartCard(string readerName)
        {
            IntPtr hContext = IntPtr.Zero;
            uint dwScope = 0; // SCARD_SCOPE_USER (0) or SCARD_SCOPE_SYSTEM (1)
            int result = SCardEstablishContext(0, IntPtr.Zero, IntPtr.Zero, out hContext);

            if (result != 0)  // SCARD_S_SUCCESS = 0
            {
                throw new Exception("SCardEstablishContext failed with error code " + result);
            }

            IntPtr hCard = IntPtr.Zero;
            uint pdwActiveProtocol = 0;
            uint dwShareMode = 2; // SCARD_SHARE_SHARED
            uint dwPreferredProtocols = 3; // SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1

            // 嘗試連接到指定的讀卡器
            result = SCardConnect(hContext, readerName, dwShareMode, dwPreferredProtocols, ref hCard, ref pdwActiveProtocol);

            if (result != 0)
            {
                throw new Exception("SCardConnect failed with error code " + result);
            }

            // 釋放上下文資源
            SCardReleaseContext(hContext);

            return hCard;
        }

    }
}
The behavior of this C# script will first list the cards and then connect to a specific card by its name.



The first AL, 1 behavior allows it to list all the physical cards on the remote server, but it cannot connect to them. 



After investigating, I decided to mute the exception pop in WinSCard!SCardListReadersA.

6685cd91 e8353dffff     call    WinSCard!__CxxThrowException@8 (66850acb)


 
by using:
eb 6249cd91 90 90 90 90



We used a lot of 90 because I tried modifying e8353dffff to 90. When the command length is shorter than the original, it causes issues and fails. So, I replaced it all with NOP (90).

The command "e8353dffff" for (6685cd91 e8353dffff call WinSCard!__CxxThrowException@8 (66850acb)) is complex enough that we can simply search for it in the DLL and modify it.


replace to



Now it works:



Common Error Troubleshooting


-2146434967
Insert your card.

-2146435043 or 8010001D, 5, 1058, 1068

1. open "services.msc"
2. go to Smart Card
3. open detail
4. choose This Account and write the Account Name "Local Service", and the password is your password
5. Restart service


net stop SCardSvr
net start SCardSvr



TL; DR

  1. find e8353dffff in DLL and modify to 9090909090
  2. find e87b11ffff, move to next commend you will see "8ac7", modify 8ac7 to B001

Tools:
  1. WinDBG
  2. 0101 Editor (for dll)
  3. Tiger VNC

About Debug WinSCard:
I select my Bank desktop app as a debug target, it will show all the dll been used, and add breakpoint to check, using Visual Studio debug app and attach to windbg is bit difficult, so better choose the .exe from build folder and don't execute from Visual Studio, directly attach from windbg.



Reference:
https://lifayk.blogspot.com/2012/07/windows-smart-card-subsystem-and-remote.html
https://lifayk.blogspot.com/2012/07/windows-smart-card-subsystem-and-remote.html
https://www.reddit.com/r/WindowsServer/comments/raw4sr/disable_smart_card_redirection_on_rdp_server/
https://github.com/LudovicRousseau/PCSC/issues/161
https://www.cnblogs.com/yilang/p/11451549.html
https://learn.microsoft.com/zh-tw/windows-hardware/drivers/debuggercmds/bp--bu--bm--set-breakpoint-
https://learn.microsoft.com/zh-tw/windows/win32/api/winscard/nf-winscard-scardestablishcontext
RedirectedSCardGetDeviceTypeId

沒有留言:

張貼留言

© Mac Taylor, 歡迎自由轉貼。
Background Email Pattern by Toby Elliott
Since 2014