Use of the API without .NET, JAVA, or gacmd. A proof-of-concept in Powershell.

Post any question you may have in regards to GoAnywhere MFT and let our talented support staff and other users assist you.
If you need a quicker response, please create a support ticket via the customer portal my.goanywhere.com or contact our support team by email at [email protected].
1 post Page 1 of 1

Cignul9

Posts: 1
Joined: Tue Jan 10, 2017 6:06 pm

Post by Cignul9 » Tue Jan 10, 2017 6:28 pm
Here's a Powershell script that doesn't drive gacmd, use the .NET api or the Java api. I wiresharked the web calls gacmd was making. Powershell pretends to be that client to the server. The good news is this is sessionless; one web call gives one result. The data in motion security is totally dependent upon the fact that this web call should happen over a secure connection, ie: https. Going through a reverse proxy should work just fine.

Now, I didn't completely reverse engineer gacmd. I just wanted to show that it can be done and encourage the GoAnywhere devs to release the web call details, maybe give us a wsdl or a wadl (and for god sake please provide support for json bodies or something more reasonable). I only support three commands. Hack this up and add more. While your at it here are some improvements you could throw in:
  • * ParameterSets for each command
    * Parameter descriptors, maybe take input from the pipeline, that kind of thing
    * Better exception handling
    * Better output format. I'm just dumping xml. I'm lazy.
    * More commands supported (lookin at you for that wsdl, devs)
    * Some kind of GPL license up in there. I don't care just take it.
This is useful if you want to integrate with CRM, for example, and your CRM people want web calls not Java or .NET API's. This way you can demonstrate how to do that. If anyone's interested I can post decoded HTTP so you can get a raw look at a request and response.
Code: Select all
[CmdletBinding()]
param(
  [string]$url = [ your server url here, including the port ex: https://mft.mycompany.com:8000 ],
  [string]$user = [ the account name use to manage web users ],
  [string]$password [ the account password you use to manage web users ],
  [string]$command,
  [string]$webUserTemplate,
  [string]$webUserName,
  [string]$webUserPassword,
  [string]$webUserFirstName,
  [string]$webUserLastName,
  [string]$webUserDescription,
  [string]$webUserEmail,
  [switch]$generatePassword,
  [switch]$forcePasswordChange,
  [switch]$emailPassword
)
function Create-MultiPartBody{
  param(
    [object]$parts,
    [string]$boundary
  )
  $body = ''
  foreach($part in $Parts.GetEnumerator()){
    if($part.Name -eq 'commandAction'){
      $disposition = "form-data; name=`"file`"; filename=`"$($part.Name)`""
      $type = 'application/octet-stream; charset=ISO-8859-1'
      $encoding = 'binary'
    }
    else{
      $disposition = "form-data; name=`"$($part.Name)`""
      $type = 'text/plain; charset=UTF-8'
      $encoding = '8bit'
    }
    $bodySegment = @"
--$($boundary)
Content-Disposition: $($disposition)
Content-Type: $($type)
Content-Transfer-Encoding: $($encoding)

$($part.Value)

"@
    $body += $bodySegment
  }
  $body += "--$($boundary)--`n"
  $body.Replace("`n","`r`n")
  return $body
}
function Create-CommandXml{
  param(
    [object]$command
  )
  [xml]$doc = ''
  $rootNode = $doc.CreateElement('command')
  [void]$doc.AppendChild($rootNode)
  $command.GetEnumerator() | ?{$_.Value -ne $null} | %{
    $parameter = $doc.CreateElement($_.Name)
    if($_.Value -is [boolean] -or $_.Value -is [switch]){
      $value = $_.Value.ToString().ToLower()
    }else{
      $value = $_.Value
    }
    $parameter.InnerText = $value
    [void]$rootNode.AppendChild($parameter)
  }
  return $doc.OuterXml.Trim()
}
function Send-WebRequest{
  param(
    [string]$url,
    [object]$contentParts
  )
  $apiURL = "$($url)/goanywhere/servlets/commandCenter"
  Write-Verbose "URL: `n$($apiURL)"
  $randomString = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes([System.Guid]::NewGuid().ToString()))
  $boundary = $randomString.Substring($randomString.Length-39)
  $userAgent = 'Jakarta Commons-HttpClient/3.1'
  $headers = [ordered]@{
    'Transfer-Encoding' = 'chunked';
    'Content-Type' =  "multipart/form-data; boundary=$($boundary)";
  }
  $body = Create-MultiPartBody $contentParts $boundary
  Write-Verbose "Body: `n$($body)"
  try{
    $response = Invoke-WebRequest `
      -Uri $apiURL `
      -DisableKeepAlive `
      -UserAgent $userAgent `
      -Headers $headers `
      -Method POST `
      -Body $body
  }
  catch{
    Write-Verbose $_.Exception.Message
  }
  return $response
}
$commandActions = @{
  deleteWebUser = [ordered]@{
    userName              = $webUserName;
  }
  addWebUser = [ordered]@{
    webUserTemplate       = $webUserTemplate;
    userName              = $webUserName;
    userPassword          = $webUserPassword;
    firstName             = $webUserFirstName;
    lastName              = $webUserLastName;
    description           = $webUserDescription;
    email                 = $webUserEmail;
  }
  resetWebUserPassword = [ordered]@{
    userName              = $webUserName;
    generatePassword      = $generatePassword;
    userPassword          = $webUserPassword;
    forcePasswordChange   = $forcePasswordChange;
    displayPassword       = $true;
    emailPassword         = $emailPassword;
  }
}
$contentParts = [ordered]@{
  command = $command
  user = $user;
  password = $password;
  passwordIsEncrypted = 'false';
  commandAction = Create-CommandXml $commandActions.$Command
}
$response = Send-WebRequest $url $contentParts
$response.Content

#.\Set-MFTWebUser.ps1 -webUserName 'TestUser' -command 'addWebUser' -webUserDescription 'Delete this user' -webUserEmail '[email protected]'
#.\Set-MFTWebUser.ps1 -webUserName 'TestUser' -command 'resetWebUserPassword' -generatePassword -emailPassword
#.\Set-MFTWebUser.ps1 -webUserName 'TestUser' -command 'deleteWebUser'
1 post Page 1 of 1