Can CLR work with secure strings?

Started by stanl, April 04, 2014, 11:24:34 AM

Previous topic - Next topic

stanl

Thought I'd ask.  Saw some code that uses

System.Security.SecureString  to encrypt information, then two methods from

system.runtime.interopservices.marshal to interpret:

Marshal.SecureStringToCoTaskMemUnicode

and

Marshal.PtrToStringUni



stanl

This will probably end up as another one of those classes the CLR cannot handle, but I gave it a shot.  Attached are two files, (1) the text "Hello, I am safe." as a secure string, (2) a WB script that attempts to interpret it.

The script will fail by (1) inability to create the secretdata.txt as a secure string, and inability to recognize SecureStringToGlobalAllocUnicode as a method.

I realize there are existing API's to secure passwords or data, but this looks interesting.

stanl

It does work through powershell. The attached script and .ps1 file will interpret the previously posted secretdata.txt file.

stanl

and... if anyone is interested, here is the front end, a script to build a secure string. Had to run WB through PS and beginning to wonder if this secure string stuff needs .NET 4.5

Deana

Here is a WIL code Example to create a securestring:

Code (winbatch) Select
;***************************************************************************
;**   Create a SecureString
;**
;** Purpose:  Create a dotNet SecureString
;** Inputs:  String
;** Outputs: Displays the length of the secureString
;** Reference:
;**       REQUIRES WinBatch 2013A or newer
;**
;** Developer: Deana Falk 2014.04.07
;***************************************************************************
If Version( )< '2013A'
   Pause('Notice', 'Need 2013A or Newer Version of WinBatch')
   Exit
EndIf

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Load assemblies into the WinBatch process.
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; mscorlib assembly is automatically loaded by WinBatch when the CLR is loaded.
; ObjectClrOption ('use','mscorlib, version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
ObjectClrOption("use","System.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Prompt for input
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
initString = 'TestString'

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Create a class implemented by a managed assembly.
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;Create a SecureString
testString = ObjectClrNew( 'System.Security.SecureString')
length = StrCharCount( initString )
for i = 0 to length-1
    ochar = ObjectClrNew( 'System.Char', Char2Num(StrSub( initString, i, 1 )) )
    testString.AppendChar(ochar)
next
length = testString.Length
testString.MakeReadOnly
Pause(testString.ToString(), Length)

Exit
Deana F.
Technical Support
Wilson WindowWare Inc.

Deana

Here is another code sample that tries to convert the input string back using System.Runtime.InteropServices.Marshal. It appears most of the methods exposed return a pointer of type VT_INT, which seems to convert to a WIL data type VT_I4. I then pass the pointer to a UDF that attempts to read the memory from that pointer using IntControl 32. However it seems to truncate the data att he last character and I am not quite sure how to resolve this.....yet.


Code (winbatch) Select
;***************************************************************************
;**   Create a SecureString and attempt to convert it back to a string.
;**
;** Purpose:  Create a dotNet SecureString
;** Inputs:  String
;** Outputs: Displays the converted string.
;** Reference:
;**       REQUIRES WinBatch 2013A or newer
;**>>  ISSUE: It appears most of the methods exposed return a pointer of type VT_INT, which seems to convert
;**        to a WIL data type VT_I4. I then pass the pointer to a UDF that attempts to read the memory from that pointer
;**       using IntControl 32. However it seems to truncate the data att he last character and I am not quite sure how to
;**       resolve this.....yet.

;** Developer: Deana Falk 2014.04.07
;***************************************************************************
If Version( )< '2013A'
   Pause('Notice', 'Need 2013A or Newer Version of WinBatch')
   Exit
EndIf

#DefineFunction udfGetStringFromPointer(pointer)
   DebugTrace(22)
   strFromPointer=""
   ;errormode(@off)
   cValue=IntControl(32, pointer, "LONG", 0, 0)
   ;errormode(@on)
   While cValue
      strFromPointer=strFromPointer:Num2Char(cValue)
      pointer=pointer + 1
      cValue=IntControl(32, pointer, "LONG", 0,0)
   EndWhile
   Return(strFromPointer)
#EndFunction


;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Load assemblies into the WinBatch process.
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; mscorlib assembly is automatically loaded by WinBatch when the CLR is loaded.
; ObjectClrOption ('use','mscorlib, version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
ObjectClrOption("use","System.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Prompt for input
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;initString = '0123456789'
initString = 'TestString'

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Create a class implemented by a managed assembly.
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;Create a SecureString
testString = ObjectClrNew( 'System.Security.SecureString')
length = StrCharCount( initString )
for i = 0 to length-1
    ochar = ObjectClrNew( 'System.Char', Char2Num(StrSub( initString, i, 1 )) )
    testString.AppendChar(ochar)
next
length = testString.Length
testString.MakeReadOnly
;Pause(testString.ToString(), Length)

;Note that SecureString has no members that inspect, compare, or convert the value of a SecureString. The absence of such members helps protect the value of the
;instance from accidental or malicious exposure. Use appropriate members of the System.Runtime.InteropServices.Marshal class, such as the SecureStringToBSTR method,
;to manipulate the value of a SecureString object.

;The SecureString class and its members are not visible to COM. For more information, see ComVisibleAttribute.

;When you wish to use the value stored in the SecureString object, you will have to use the Marshal class (which can be found in the System.Runtime.InteropServices namespace).
;Among other things, this class provides methods for allocating unmanaged memory and copying unmanaged memory blocks. This includes methods that will convert the contents of
;your SecureSting object into an object of type BSTR (basic string or binary string) or a block of ANSI or Unicode memory.

Marshal = ObjectClrNew("System.Runtime.InteropServices.Marshal")

; ptr Returned from the following methods is type VT INT which is not currently supported bt WinBatch
ptr = Marshal.SecureStringToGlobalAllocUnicode(testString)
;ptr = Marshal.SecureStringToGlobalAllocAnsi(testString)
;ptr = Marshal.SecureStringToBSTR(testString)
WILptr = ObjectType( 'I4', ptr )
result = udfGetStringFromPointer(WILptr)
Pause(initString, result)

Exit
Deana F.
Technical Support
Wilson WindowWare Inc.

stanl

Interesting.....

This part
ochar = ObjectClrNew( 'System.Char', Char2Num(StrSub( initString, i, 1 )) )

especially.  I assume this defaults to the -plaintext option in PS.

Deana

Quote from: stanl on April 08, 2014, 10:24:42 AM
Interesting.....

This part
ochar = ObjectClrNew( 'System.Char', Char2Num(StrSub( initString, i, 1 )) )

especially.  I assume this defaults to the -plaintext option in PS.

Actually no. That line merely creates variable (of type 'System.Char') that contains a single character. The AppendChar method is what actually creates the secure string.
Deana F.
Technical Support
Wilson WindowWare Inc.

stanl

Quote from: Deana on April 08, 2014, 10:22:43 AM
Here is another code sample that tries to convert the input string back using System.Runtime.InteropServices.Marshal. It appears most of the methods exposed return a pointer of type VT_INT, which seems to convert to a WIL data type VT_I4. I then pass the pointer to a UDF that attempts to read the memory from that pointer using IntControl 32. However it seems to truncate the data att he last character and I am not quite sure how to resolve this.....yet.

Well, at least in my original posts, the .wbt code was on the right track. I tried the PtrToStringUni method in the Marshal class. Could you not use that?

td

A few suggested mods to experiment with
Code (winbatch) Select

; Take advantage of a MSFT's largess
#DefineFunction udfGetStringFromPointer(pointer, nLen)
hBin = BinaryAlloc((nLen+1) *2)
DllCall("kernel32.dll",long:"lstrcpyW", lpbinary:hBin, long:pointer)
BinaryEodSet(hBin, (nLen+1)*2)
strResult = BinaryPeekStrW(hBin, 0, nLen*2 )
BinaryFree(hBin)
   Return(strResult)
#EndFunction


And here
Code (winbatch) Select

length = StrCharCount( initString )
for i = 1 to length  ; 1 instead of 0
    ;ochar = ObjectClrNew( 'System.Char', Char2Num(StrSub( initString, i, 1 )) )
    ochar = Char2Num(StrSub( initString, i, 1 ))
    testString.AppendChar(ui2:ochar)
next
testString.AppendChar(ui2:0)  ; Null terminate to be safe.    

"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

Deana

Thanks Tony! Here is the code sample that I came up with the help of your suggestions:

Code (winbatch) Select
;***************************************************************************
;**   Create a SecureString and attempt to convert it back to a WIL string.
;**
;** Purpose:  Create then Read a dotNet SecureString
;** Inputs:  String
;** Outputs: Displays the converted string.
;** Reference:
;**       REQUIRES WinBatch 2013A or newer
;**       
;**       
;** Developer: Deana Falk, Tony Deets 2014.04.08
;***************************************************************************
If Version( )< '2013A'
   Pause('Notice', 'Need 2013A or Newer Version of WinBatch')
   Exit
EndIf

;Take advantage of a MSFT's largess
; It appears most of the methods exposed return a pointer of type VT_INT, which seems to convert
; to a WIL data type VT_I4. Pass the pointer to a UDF that attempts to read the memory from that pointer
#DefineFunction udfGetStringFromPointer(pointer, nLen)
        hBin = BinaryAlloc((nLen+1) *2)
        DllCall("kernel32.dll",long:"lstrcpyW", lpbinary:hBin, long:pointer)
        BinaryEodSet(hBin, (nLen+1)*2)
        strResult = BinaryPeekStrW(hBin, 0, nLen*2 )
        BinaryFree(hBin)
   Return(strResult)
#EndFunction

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Load assemblies into the WinBatch process.
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; mscorlib assembly is automatically loaded by WinBatch when the CLR is loaded.
; ObjectClrOption ('use','mscorlib, version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
ObjectClrOption("use","System.Security, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Prompt for input
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
initString = 'TestString'
initString = '01234567890'
initString = '!@#$^&*()_+'

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Create a class implemented by a managed assembly.
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;Create a SecureString
testString = ObjectClrNew( 'System.Security.SecureString')
length = StrCharCount( initString )
for i = 1 to length  ; 1 instead of 0
    ;ochar = ObjectClrNew( 'System.Char', Char2Num(StrSub( initString, i, 1 )) )
    ochar = Char2Num(StrSub( initString, i, 1 ))
    testString.AppendChar(ui2:ochar)
next
testString.AppendChar(ui2:0)  ; Null terminate to be safe.   

;length = testString.Length
testString.MakeReadOnly

;Note that SecureString has no members that inspect, compare, or convert the value of a SecureString. The absence of such members helps protect the value of the
;instance from accidental or malicious exposure. Use appropriate members of the System.Runtime.InteropServices.Marshal class, such as the SecureStringToBSTR method,
;to manipulate the value of a SecureString object.

;The SecureString class and its members are not visible to COM. For more information, see ComVisibleAttribute.

;When you wish to use the value stored in the SecureString object, you will have to use the Marshal class (which can be found in the System.Runtime.InteropServices namespace).
;Among other things, this class provides methods for allocating unmanaged memory and copying unmanaged memory blocks. This includes methods that will convert the contents of
;your SecureSting object into an object of type BSTR (basic string or binary string) or a block of ANSI or Unicode memory.

Marshal = ObjectClrNew("System.Runtime.InteropServices.Marshal")

; ptr Returned from the following methods is type VT INT which is not currently supported bt WinBatch
ptr = Marshal.SecureStringToBSTR(testString)
WILptr = ObjectType( 'I4', ptr )
result = udfGetStringFromPointer(WILptr,length)
Pause(initString, result)

Exit

Deana F.
Technical Support
Wilson WindowWare Inc.

stanl

If I might summarize: again, a good learning experience. In other example scripts using the CLR a property that accepted a string value could just be assigned it. In this case the securestring property needed a few hoops. Also, the method in the Marshal class to obtain the string from the memory pointer [ PtrToStringUni ] requires a WB udf.

I had a need (helping a friend) for a compiled WB script that used secure strings. I ended up with a script that used the powershell class (combining the two I posted). Today, I tested it against Deana's code with no real differences... the good part being both are WB code.  Obviously PS cmdlets hide a lot of the 'innards', and so I appreciate both Tony and Deana totally winbatching this thread....

Deana

Quote from: stanl on April 10, 2014, 01:44:28 PM
If I might summarize: again, a good learning experience. In other example scripts using the CLR a property that accepted a string value could just be assigned it. In this case the securestring property needed a few hoops.
Actually SecureString is a "Class".  The SecureString Class expects a subarray of System.Char objects, which is NOT the same as a BSTR/WIL string. The code I posted calls the AppendChar method which appends a character to the end of the current secure string.
Reference: http://msdn.microsoft.com/en-us/library/system.security.securestring%28v=vs.110%29.aspx

Quote from: stanl on April 10, 2014, 01:44:28 PM
Also, the method in the Marshal class to obtain the string from the memory pointer [ PtrToStringUni ] requires a WB udf.
Not necessarily. That is just the solution I came up with. The Marshal Class also seems to provide a ReadByte method that you might be able to use.
Reference: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal(v=vs.100).ASPX

Deana F.
Technical Support
Wilson WindowWare Inc.

stanl

Quote from: Deana on April 10, 2014, 02:05:01 PM
Actually SecureString is a "Class".


Yes, I confused property with class.