Kaseya Community

Authenticating with the REST API in Powershell

  • Hi all,

    I'm hoping someone will be able to point me in the right direction here. I have been checking out the API with PowerShell. After some trial and error, and the help of an older post here I am able to use the Swagger UI to get my token for subsequent queries, but I am unable to get this with PowerShell.

    So far I have

    • A script to do the hashing and encoding of the authorization string
    • Scripts that query the api using the bearer token I received from swagger.

    What I am missing is the correct way to use the authorization string with PowerShell to get the bearer token.

    When I run Invoke-restmethod -uri MyVSAAuthUrl -Method Get -headers @{'Authorization'='encodedauthstring'}


    I get back a response that says "Header missing user param"

    If add 'User'='me@mydomain' to the header I get the same response. If I send an empty header the response is something like "Header missing authorization"

    Can anyone help me out?

    Thanks

    Liam

  • If you use a different tool (like Postman or another REST API client) does your authentication string work? I would recommend that you verify that your hashing process is accurate first.

    If you are generating the authorization string correctly, you could also use Wireshark or Fiddler to intercept the network packets from Powershell and compare them to a successful authorization to see what is different between what PowerShell does and what a successful authorization looks like.

  • Hi can you confirm that you are using the correct method to create the Auth header's payload. Please refer to this link for more information - help.kaseya.com/.../9040000

  • Hi Liam,

    I figured out where you went wrong here. You're missing the text "Basic " in front of your base64 encoded string. So your PowerShell should look like this:

    Invoke-restmethod -uri MyVSAAuthUrl -Method Get -headers @{'Authorization'='Basic encodedauthstring'}

    I tested this and I got the REST response error { "ResponseCode": 4000002, "Status": "Failed", "Error": "None" } at first, but after adding "Basic " as detailed above, it worked.

    Hope this helps.

    Nate

  • - Thank you! Something so simple I had overlooked.

    I now get the response 4010001 OK Username or password incorrect.

    I know they are both correct when I pass them into the hash function, so I must has some issues in the function. I am using the objects SHA256Managed and SHA1Managed to complete the hashing.

    Thanks for your assistance everyone.

    Liam

  • Hi Liam,

    I'd recommend using this post: community.kaseya.com/.../101201.aspx

    Use the same dummy username and password as well as the same random number as the post, then echo out your variables at various points in the script. You can then compare your output with the outputs in the post and wherever yours deviates, that's where you need to figure out where your mistake is.

    Please note, domain credentials are not supported by Kaseya, so if you're trying to use a domain user account, it won't work at all. Your account must be a local VSA username and password.

    You could also post your code (minus any identifying information) and I could see if there are any obvious mistakes.

    Nate

  • Hi Nate,

    Sorry for the late response, I appreciate your assistance.

    The code I've written is

    function Hash1($textToHash){

       $hasher = new-object System.Security.Cryptography.SHA1Managed

       $toHash = [System.Text.Encoding]::UTF8.GetBytes($textToHash)

       $hashByteArray = $hasher.ComputeHash($toHash)

       foreach($byte in $hashByteArray){

           $res +=$byte.ToString()

       }

       Return $res;

    }

    function Hash256($textToHash){

       $hasher = new-object System.Security.Cryptography.SHA256Managed

       $toHash = [System.Text.Encoding]::UTF8.GetBytes($textToHash)

       $hashByteArray = $hasher.ComputeHash($toHash)

       foreach($byte in $hashByteArray){

           $res +=$byte.ToString()

       }

       Return $res;

    }

    $username = 'test@mydomain.com.au'

    $password = 'supersecret'

    $random = Get-Random

    $RawSHA256Hash = Hash256($password)

    $CoveredSHA256HashTemp = Hash256($password -join $username)

    $CoveredSHA256Hash = Hash256($CoveredSHA256HashTemp -join $Random)

    $RawSHA1Hash = Hash1($password)

    $CoveredSHA1HashTemp = Hash1($password -join $username)

    $CoveredSHA1Hash =($CoveredSHA1HashTemp -join $random)

    $auth = "user=lpkemp@techmed.com.au,pass2=$CoveredSHA256Hash,pass1=$CoveredSHA1Hash,rand2=$random,rpass2=$RawSHA256Hash,rpass1=$RawSHA1Hash,twofapass=:undefined"

    $b = [System.Text.Encoding]::UTF8.GetBytes($auth)

    $token = [System.Convert]::ToBase64String($b)

    Invoke-RestMethod -Method Get -Uri 'saas12.kaseya.net/.../auth& -Headers @{'Authorization' = "Basic $token"}

    I've take the value of $token and put it through postman and get the same username or password is incorrect response, so i'm fairly certain i'm doing something wrong with the hashes. possibly using the wrong assemblies.

    Thanks

    Liam



    Typo in object name
    [edited by: liamkemp at 3:59 AM (GMT -8) on Jan 7, 2017]
  • I'll walk through this in the same steps as this post (http://community.kaseya.com/xsp/f/114/p/22103/101201.aspx#101201) and hopefully we'll see where you went wrong.

    STEP 1 - Get a username, password, and random number:

    Random: 998877
    User: testuser
    Pass: SomethingMoreSecureThanThis
    twofapass=:undefined

    STEP 2 - Hash Pass+User with SH256 and SHA1:

    Target Hashes (from the post):
    SHA1 Hash   = 84d015ab5dad524a428ddbd1d96b27e35bef07b9
    SHA256 Hash = 9968fbf6aa432a498e1cc159f5a0cfe0501ba2bc14e51a29bead555194595c38

    PowerShell:
    $username = 'testuser'
    $password = 'SomethingMoreSecureThanThis'
    $random   = '998877'
    $CoveredSHA1Hash   = Hash1("$password$username")
    $CoveredSHA256Hash = Hash256("$password$username")

    PowerShell Hashes:
    SHA1 Hash   = 132208211719317382746614121920921710739227912397185
    SHA256 Hash = 153104251246170674273142281938924516020722480271621882022926411901738581148899256

    Observations:

    First, for your covered hashes, you are doing things like this:
    $password -join $username
    If I'm reading the docs right, that's joining $password to nothing using $username as the delimiter -- obviously NOT what you want. You're better off just doing "$password$username" like I did above.

    Second, after running the password+username through the hash functions, the output (as seen above) is not even in the same format as normal SHA1 and SHA256 hashes. I found this article (https://learn-powershell.net/2013/03/25/use-powershell-to-calculate-the-hash-of-a-file/), which uses the following method to hash data:
    [string]$hash = -join ([Security.Cryptography.HashAlgorithm]::Create($algorithm).ComputeHash($data) | ForEach { "{0:x2}" -f $_ })

    So I did this instead:
    $data = [system.Text.Encoding]::UTF8.GetBytes("$password$username")
    [string]$SHA1Hash   = -join ([Security.Cryptography.HashAlgorithm]::Create("SHA1").ComputeHash($data) | ForEach { "{0:x2}" -f $_ })
    [string]$SHA256Hash = -join ([Security.Cryptography.HashAlgorithm]::Create("SHA256").ComputeHash($data) | ForEach { "{0:x2}" -f $_ })

    Results:
    SHA1 Hash   = 84d015ab5dad524a428ddbd1d96b27e35bef07b9
    SHA256 Hash = 9968fbf6aa432a498e1cc159f5a0cfe0501ba2bc14e51a29bead555194595c38

    MUCH BETTER!

    STEP 3 - Hash prior hash+random number with SHA256 and SHA1:

    Target Hashes (from the post):
    SHA1 Hash   = a94e5f7c12d1599e538cffb61bc15ab8c7ab7ced
    SHA256 Hash = 687e5a021386639f86ba48aecfadbc162de3f28128e6e6772807d80cdf38615b

    My Hashes:
    SHA1 Hash   = a94e5f7c12d1599e538cffb61bc15ab8c7ab7ced
    SHA256 Hash = 687e5a021386639f86ba48aecfadbc162de3f28128e6e6772807d80cdf38615b

    YAY!

    STEP 4 - Build the authorization string:

    user=testuser,pass2=687e5a021386639f86ba48aecfadbc162de3f28128e6e6772807d80cdf38615b,pass1=a94e5f7c12d1599e538cffb61bc15ab8c7ab7ced,rand2=998877,rpass2=687e5a021386639f86ba48aecfadbc162de3f28128e6e6772807d80cdf38615b,rpass1=a94e5f7c12d1599e538cffb61bc15ab8c7ab7ced,twofapass=:undefined

    Matches the forum post.

    STEP 5 - Encode the authorization string with Base64:

    dXNlcj10ZXN0dXNlcixwYXNzMj02ODdlNWEwMjEzODY2MzlmODZiYTQ4YWVjZmFkYmMxNjJkZTNmMjgxMjhlNmU2NzcyODA3ZDgwY2RmMzg2MTViLHBhc3MxPWE5NGU1ZjdjMTJkMTU5OWU1MzhjZmZiNjFiYzE1YWI4YzdhYjdjZWQscmFuZDI9OTk4ODc3LHJwYXNzMj02ODdlNWEwMjEzODY2MzlmODZiYTQ4YWVjZmFkYmMxNjJkZTNmMjgxMjhlNmU2NzcyODA3ZDgwY2RmMzg2MTViLHJwYXNzMT1hOTRlNWY3YzEyZDE1OTllNTM4Y2ZmYjYxYmMxNWFiOGM3YWI3Y2VkLHR3b2ZhcGFzcz06dW5kZWZpbmVk

    Matches the forum post.

    STEP 6 - HTTP GET with basic authorization header:

    Obviously I don't have access to your Kaseya system, and it look like yours is hosted, where mine is an on-prem instance. However, I tried this on my system and after putting in my own username and password I did get a token.

    Here is my final PowerShell script:

    function hash($algorithm, $text) {
        $data = [system.Text.Encoding]::UTF8.GetBytes($text)
        [string]$hash = -join ([Security.Cryptography.HashAlgorithm]::Create($algorithm).ComputeHash($data) | ForEach { "{0:x2}" -f $_ })
        return $hash
    }

    $vsa_url = 'https://yourvsa.com/api/v1.0/auth'
    $username = 'testuser'
    $password = 'SuperSecretPassword'
    $random = Get-Random

    $SHA1Hash   = hash "SHA1"   "$password$username"
    $SHA256Hash = hash "SHA256" "$password$username"
    $SHA1Hash   = hash "SHA1"   "$SHA1Hash$random"
    $SHA256Hash = hash "SHA256" "$SHA256Hash$random"

    $auth = "user=$username,pass2=$SHA256Hash,pass1=$SHA1Hash,rand2=$random,rpass2=$SHA256Hash,rpass1=$SHA1Hash,twofapass=:undefined"
    $base64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($auth))

    Invoke-RestMethod -Method Get -Uri $vsa_url -Headers @{'Authorization' = "Basic $base64"}

    Hope this helps.

    Nate

  • Hi Nate,

    Now that you mention it, I don't know why I was trying to use -join like that at all!

    In trying to make this readable to a scripting-averse colleague I have apparently lost my mind.

    The only way -join would work is $password,$username -join ""  which is clearly not intuitive or readable.

    I'm missing WMF 5 on this computer, so I cant use invoke-restmethod. I'll try that hashing method out on another computer when I can.

    Thanks for sticking with me through this.

    Liam

  • Nate,

    Thank you so much, you are fantastic.

    I'm curious to know why the output from the hashing function I found are so different to yours, but that is for another day.

    Cheers

    Liam

  • - looking at this again, your authorisation string is in a different order than the documentation, and is using the SHA256 and SHA1 hashes twice, instead of the covered and the raw of each - This process is working, so is the documentation incorrect? Or have I misunderstood.

    Liam

  • Liam,

    If you read through the thread I sent you before, you'll note that Kaseya has built in documentation on how to build the authorization string here: http:///api/v1.0/swagger/ui/ext/Kaseya-API-Core-Swagger-SwaggerUITweaks-js

    I have also documented the whole thing in detail in my post and I'm using it just this way in the systems we have built.

    I agree though. It does seem to conflict with the documentation you see in the manual.

    Nate
  • The order of the parameters in the auth string is irrelevant. They are broken down into a dictionary during authentication so it really doesn't care what order is used, as long as they're all there.

    As to the raw hashes, I believe they are only relevant when the usernames are complex ones such as email addresses. I think they are only evaluated if the raw hashes don't pass the auth tests.

  • That would make sense. It explains why my hashes work even though they deviate from the documentation. They aren't being used!