Intro guide: Using Windows Credentials, Python, Robin

To keep your scripts portable, you’ll need a generic method for credential access.

Here I’ve chosen Windows Credentials, using Python to retrieved them and pass them to Robin, thanks to the (modified) code from code by mrh1997.

The target website is Box.com, which is automation-friendly.

Steps:

  • Make sure you have set up Windows credentials for Box (or whatever site you choose, adapting the code accordingly). Visit the link above if you’re not sure how to do it.
  • Create an appmask like the one below for Robin to work with.
  • Make sure the Python script is referenced by an absolute path. I keep mine in the same directory.
  • Make sure ‘python.exe’ is in your path, or adjust the Robin file if it isn’t.
  • In the Robin file, convert the Python output to a Robin list '[username, password].
  • Follow standard web automation procedures.

Code screenshot:

Code:

import "C:\Users\YourName\Documents\Robin\9.2\wincred\box.appmask"
# This python script will retrieve credentials for box.com
System.RunDOSCommand \
    DOSCommandOrApplication: "python.exe  \
    C:\Users\YourName\Documents\Robin\9.2\wincred\box.py"  \
    WorkingDirectory:'' \
    StandardOutput=> StandardOutput \
    StandardError=> StandardError \
    ExitCode=> ExitCode
# Standard output will be in the form 'username,password', without quotes.
# We need to convert it to a list for Robin
Text.SplitWithDelimiter \
    Text: StandardOutput \
    CustomDelimiter: ',' \
    IsRegEx:False \
    Result=> CredList
# First item from our list is username, second is password
set username to CredList[0]
set passwd to CredList[1]
# These are for your debugging purposes
Console.Write Message: username
Console.Write Message: passwd
# Log in to the Box.com app page
WebAutomation.LaunchFirefox \
    Url: 'https://app.box.com/login' \
    WindowState:WebAutomation.BrowserWindowState.Maximized \
    ClearCache:False \
    ClearCookies:False \
    BrowserInstance=> Browser
# Wait a bit for safety
wait 1
# Box.com is automation-friendly - the first field is pre-focused. Nice.
# Populate the username field with the cred you retrieved earlier.
WebAutomation.FormFilling.PopulateTextFieldCloseDialog \
    BrowserInstance:  Browser \
    Control: box.box.Login.fldEmail \
    Text: username \
    EmulateTyping:True \
    UnfocusAfterPopulate:False

wait 1
# Submit
WebAutomation.Click \
    BrowserInstance: Browser \
    Control: box.box.Login.submitEmail

wait 1
# Again, pre-focused. Nice.
# Populate the password field with the cred you retrieved earlier.
WebAutomation.FormFilling.PopulateTextField \
    BrowserInstance:  Browser\
    Control:  box.box.Password.fldPassword \
    Text:  passwd \
    EmulateTyping:True \
    UnfocusAfterPopulate:False

wait 1
# Login, and done!
WebAutomation.Click \
    BrowserInstance:  Browser \
    Control: box.box.Password.submitLogin

Appmask screenshot:

And the Python script. You may need to install some dependencies, but probably not.

#!python3
"""
Access windows credentials
For Robin.
Modified from the code at 
https://gist.github.com/mrh1997/717b14f5783b49ca14310419fa7f03f6
Thanks!
"""
from typing import Tuple
import ctypes as CT
import ctypes.wintypes as WT

CRED_TYPE_GENERIC = 0x01

LPBYTE = CT.POINTER(WT.BYTE)
LPWSTR = WT.LPWSTR
LPCWSTR = WT.LPWSTR
class CREDENTIAL_ATTRIBUTE(CT.Structure):
    _fields_ = [
        ('Keyword', LPWSTR),
        ('Flags', WT.DWORD),
        ('ValueSize', WT.DWORD),
        ('Value', LPBYTE)]
PCREDENTIAL_ATTRIBUTE = CT.POINTER(CREDENTIAL_ATTRIBUTE)
class CREDENTIAL(CT.Structure):
    _fields_ = [
        ('Flags', WT.DWORD),
        ('Type', WT.DWORD),
        ('TargetName', LPWSTR),
        ('Comment', LPWSTR),
        ('LastWritten', WT.FILETIME),
        ('CredentialBlobSize', WT.DWORD),
        ('CredentialBlob', LPBYTE),
        ('Persist', WT.DWORD),
        ('AttributeCount', WT.DWORD),
        ('Attributes', PCREDENTIAL_ATTRIBUTE),
        ('TargetAlias', LPWSTR),
        ('UserName', LPWSTR)]
PCREDENTIAL = CT.POINTER(CREDENTIAL)

advapi32 = CT.WinDLL('Advapi32.dll')
advapi32.CredReadA.restype = WT.BOOL
advapi32.CredReadA.argtypes = [LPCWSTR, WT.DWORD, WT.DWORD, CT.POINTER(PCREDENTIAL)]


def GetGenericCredential(name:str) -> Tuple[str, str]:
    """
    Returns a Tuple of Name and Password of a Generic Windows Credential
    Uses bytes in Py3 and str in Py2 for url, name and password.
    """
    cred_ptr = PCREDENTIAL()
    if advapi32.CredReadW(name, CRED_TYPE_GENERIC, 0, CT.byref(cred_ptr)):
        username = cred_ptr.contents.UserName
        cred_blob = cred_ptr.contents.CredentialBlob
        cred_blob_size = cred_ptr.contents.CredentialBlobSize
        password_as_list = [int.from_bytes(cred_blob[pos:pos+2], 'little')
                           for pos in range(0, cred_blob_size, 2)]
        password = ''.join(map(chr, password_as_list))
        advapi32.CredFree(cred_ptr)
        return username, password
    else:
        raise IOError("Failure reading credential")


def main():
    name, pwd = GetGenericCredential('https://app.box.com/login')
    print(name + ',' + pwd)



if __name__ == '__main__':
    main()

overandout