Download a file from Remote Server(https) using powershell with credentials - windows

I'm trying to download a file from Linux server with Apache web server to Windows Server 2012 R2 using Windows PowerShell
Note: the URL is HTTPS
$source = "https://uri"
$destination = "C:\path\file.txt"
$username = "admin"
$password = "#dfkl!f" | ConvertTo-SecureString -asPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential($username,$password)
Invoke-WebRequest $source -OutFile $destination -Credential $cred
Invoke-WebRequest : Authorization Required
This server could not verify that you are authorized to access the document requested. Either you supplied the wrong
credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required.
Apache/2.2.15 (CentOS) Server at url Port 443
When I type the credentials through browser i m able to download but through powershell it show bad credentials

I just tried this against one of my Apache/Linux boxes on a SSL page that uses Basic auth, and it seemed to work... Your mileage might vary...
$source = "https://Uri"
$destination = "C:\Foo\Bar"
$username = 'mylogin'
$password = 'reallgoodpassword'
$auth = 'Basic ' + [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($username+':'+$password ))
Invoke-WebRequest -Headers #{ "Accept" = "*/*"; "Authorization" = $auth } -Uri $source -Method Get

try to create the passwordstring with
$password = ConvertTo-SecureString "#dfkl!f" -AsPlainText -Force

Related

PowerShell to download Zip file from GitHub API

I would like to write a PowerShell script to download the GitHub repo in ZIP format by following this instruction:
https://docs.github.com/en/rest/reference/repos#contents
$Token = 'MyUserName:MyPAT'
$Base64Token = [System.Convert]::ToBase64String([char[]]$Token)
$Headers = #{
"Authorization" = 'Basic {0}' -f $Base64Token;
"accept" = "application/vnd.github.v3+json"
}
$Uri = "https://api.github.com/repos/{owner}/{repo}/zipball"
$r = Invoke-WebRequest -Headers $Headers -Uri $Uri -Method Get | Out-File "D:\MyRepo.zip"
The code did download the zip file but I got this error message when I tried to open the zip file:
D:\MyRepo.zip
The archive is either in unknown format or damaged
I am very new to PowerShell, any help is appreciated!
You may need to look more closely at download-a-repository-archive-zip instructions. It says the response will have a 302 redirect to the URL for downloading. Invoke-WebRequest will not automatically redirect, but it will provide the response headers.
If you change your last line to be:
$response = Invoke-WebRequest -Headers $Headers -Uri $Uri -Method Get
you can review the $response object's Headers and issue another Invoke-WebRequest with the same headers and the 302 Uri:
$RedirectedResponse = Invoke-WebRequest -Headers $Headers -Uri $RedirectedURI -Method Get
$RedirectedResponse.Content will have the encoded file contents that you can decode and write to your local filesystem.
EDIT: I got to a system where I had GitHub access and tested the script. I found that the first response had a byte array with the zip file contents. This functionality is too useful not to share! Here's a script that works to download a repo:
$user = 'bjorkstromm'
$repo = 'depends'
$uri = "https://api.github.com/repos/$user/$repo/zipball/"
if(!$cred){$cred = Get-Credential -Message 'Provide GitHub credentials' -UserName $user}
$headers = #{
"Authorization" = "Basic " + [convert]::ToBase64String([char[]] ($cred.GetNetworkCredential().UserName + ':' + $cred.GetNetworkCredential().Password))
"Accept" = "application/vnd.github.v3+json"
}
$response = Invoke-WebRequest -Method Get -Headers $headers -Uri $uri
$filename = $response.headers['content-disposition'].Split('=')[1]
Set-Content -Path (join-path "$HOME\Desktop" $filename) -Encoding byte -Value $response.Content

appcmd.exe set config doesn't check if username or password is invalid and sets it anyways

I'm using winexe from my backend api to run commands on Windows Domain Server. I want to set IIS App Pool Identity as an Account from Active Directory. The problem is that while using this command :
%windir%\system32\inetsrv\appcmd.exe set config /section:applicationPools ^
/[name='POOLNAME'].processModel.identityType:SpecificUser ^
/[name='POOLNAME'].processModel.userName:DOMAIN\USER ^
/[name='POOLNAME'].processModel.password:PASSWORD
It runs successfully everytime even if the username and password is incorrect. Even the pool gets Started with wrong password. However setting wrong password through GUI fails.
I want to identify when the password or username is being set wrongly.
PS: I even tried using Set-ItemProperty on powershell and the result was the same.
You can't test your credentials with AppPool, but you can definitely test them.
# Service Principal credentials
$username = 'Username'
$password = 'Password' | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object -TypeName 'System.Management.Automation.PSCredential' -ArgumentList $username, $password
if (Test-Credential -Credential $credential) {
Write-Verbose "Credentials for $($credential.UserName) are valid..."
# do the appcmd stuff
}
else {
Write-Warning 'Credentials are not valid or some other logic'
}
Just add Test-Credential function definition at the top of your script
function Test-Credential {
[CmdletBinding()]
Param
(
# Specifies the user account credentials to use when performing this task.
[Parameter()]
[ValidateNotNull()]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty
)
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$DS = $null
$Username = $Credential.UserName
$SplitUser = $Username.Split('\')
if ($SplitUser.Count -eq 2 ) {$Username = $SplitUser[1]}
if ($SplitUser.Count -eq 1 -or $SplitUser[0] -eq $env:COMPUTERNAME ) {
$DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('machine', $env:COMPUTERNAME)
}
else {
try {
$DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('domain')
}
catch {
return $false
}
}
$DS.ValidateCredentials($Username, $Credential.GetNetworkCredential().Password)
}
(PS: Code is valid even though prettifier break with backslash quote syntax)
amazingly i puzzled out that you can do it like this - but it still doesn't validate
appcmd set apppool junkapp /processmodel.password:junkpassword

Download file using bash script issue [duplicate]

With cURL, we can pass a username with an HTTP web request as follows:
$ curl -u <your_username> https://api.github.com/user
The -u flag accepts a username for authentication, and then cURL will request the password. The cURL example is for Basic authentication with the GitHub Api.
How do we similarly pass a username and password along with Invoke-WebRequest? The ultimate goal is to user PowerShell with Basic authentication in the GitHub API.
I am assuming Basic authentication here.
$cred = Get-Credential
Invoke-WebRequest -Uri 'https://whatever' -Credential $cred
You can get your credential through other means (Import-Clixml, etc.), but it does have to be a [PSCredential] object.
Edit based on comments:
GitHub is breaking RFC as they explain in the link you provided:
The API supports Basic Authentication as defined in RFC2617 with a few
slight differences. The main difference is that the RFC requires
unauthenticated requests to be answered with 401 Unauthorized
responses. In many places, this would disclose the existence of user
data. Instead, the GitHub API responds with 404 Not Found. This may
cause problems for HTTP libraries that assume a 401 Unauthorized
response. The solution is to manually craft the Authorization header.
Powershell's Invoke-WebRequest does to my knowledge wait for a 401 response before sending the credentials, and since GitHub never provides one, your credentials will never be sent.
Manually build the headers
Instead you'll have to create the basic auth headers yourself.
Basic authentication takes a string that consists of the username and password separated by a colon user:pass and then sends the Base64 encoded result of that.
Code like this should work:
$user = 'user'
$pass = 'pass'
$pair = "$($user):$($pass)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
$basicAuthValue = "Basic $encodedCreds"
$Headers = #{
Authorization = $basicAuthValue
}
Invoke-WebRequest -Uri 'https://whatever' -Headers $Headers
You could combine some of the string concatenation but I wanted to break it out to make it clearer.
Use this:
$root = 'REST_SERVICE_URL'
$user = "user"
$pass= "password"
$secpasswd = ConvertTo-SecureString $pass -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd)
$result = Invoke-RestMethod $root -Credential $credential
If someone would need a one liner:
iwr -Uri 'https://api.github.com/user' -Headers #{ Authorization = "Basic "+ [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("user:pass")) }
Invoke-WebRequest follows the RFC2617 as #briantist noted, however there are some systems (e.g. JFrog Artifactory) that allow anonymous usage if the Authorization header is absent, but will respond with 401 Forbidden if the header contains invalid credentials.
This can be used to trigger the 401 Forbidden response and get -Credentials to work.
$login = Get-Credential -Message "Enter Credentials for Artifactory"
#Basic foo:bar
$headers = #{ Authorization = "Basic Zm9vOmJhcg==" }
Invoke-WebRequest -Credential $login -Headers $headers -Uri "..."
This will send the invalid header the first time, which will be replaced with the valid credentials in the second request since -Credentials overrides the Authorization header.
Tested with Powershell 5.1
I had to do this to get it to work:
$pair = "$($user):$($pass)"
$encodedCredentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($Pair))
$headers = #{ Authorization = "Basic $encodedCredentials" }
Invoke-WebRequest -Uri $url -Method Get -Headers $headers -OutFile Config.html
Here is another way using WebRequest, I hope it will work for you
$user = 'whatever'
$pass = 'whatever'
$secpasswd = ConvertTo-SecureString $pass -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd)
$headers = #{ Authorization = "Basic Zm9vOmJhcg==" }
Invoke-WebRequest -Credential $credential -Headers $headers -Uri "https://dc01.test.local/"
This is what worked for our particular situation.
Notes are from Wikipedia on Basic Auth from the Client Side. Thank you to #briantist's answer for the help!
Combine the username and password into a single string username:password
$user = "shaunluttin"
$pass = "super-strong-alpha-numeric-symbolic-long-password"
$pair = "${user}:${pass}"
Encode the string to the RFC2045-MIME variant of Base64, except not limited to 76 char/line.
$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
$base64 = [System.Convert]::ToBase64String($bytes)
Create the Auth value as the method, a space, and then the encoded pair Method Base64String
$basicAuthValue = "Basic $base64"
Create the header Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
$headers = #{ Authorization = $basicAuthValue }
Invoke the web-request
Invoke-WebRequest -uri "https://api.github.com/user" -Headers $headers
The PowerShell version of this is more verbose than the cURL version is. Why is that? #briantist pointed out that GitHub is breaking the RFC and PowerShell is sticking to it. Does that mean that cURL is also breaking with the standard?
another way is to use certutil.exe
save your username and password in a file e.g. in.txt as username:password
certutil -encode in.txt out.txt
Now you should be able to use auth value from out.txt
$headers = #{ Authorization = "Basic $((get-content out.txt)[1])" }
Invoke-WebRequest -Uri 'https://whatever' -Headers $Headers
I know this is a little off the OPs original request but I came across this while looking for a way to use Invoke-WebRequest against a site requiring basic authentication.
The difference is, I did not want to record the password in the script. Instead, I wanted to prompt the script runner for credentials for the site.
Here's how I handled it
$creds = Get-Credential
$basicCreds = [pscredential]::new($Creds.UserName,$Creds.Password)
Invoke-WebRequest -Uri $URL -Credential $basicCreds
The result is the script runner is prompted with a login dialog for the U/P then, Invoke-WebRequest is able to access the site with those credentials. This works because $Creds.Password is already an encrypted string.
I hope this helps someone looking for a similar solution to the above question but without saving the username or PW in the script

Ruby and SOAP creating a web service proxy and namespace

I am SOAP novice, but I have been struggling with this for days and can't figure out where I'm going wrong. I'm trying to use Ruby to talk to VMware Site Recovery Manager
I have a powershell script that can successfully use to login. I want to take this powershell script and re-write it in ruby.
Here are the wsdl files:
https://srm-vcenter-a:8095/srm?wsdl
http://pastebin.com/xJ6AwLaC
https://srm-vcenter-a:8095/srm-Service?wsdl
http://pastebin.com/nmH5mzdH
The powershell code
$Server = "srm-vcenter-a"
$UserName = "administrator"
$Password = "mypw"
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
Write-Host "Connecting to SRM"
$webSvc = New-WebServiceProxy ("https://" + $Server + ":8095/srm-Service?wsdl") -Namespace SRM
$srm = New-Object SRM.SrmService
$srm.Url = "Https://" + $Server + ":9007"
$srm.Timeout = 600000
$srm.CookieContainer = New-Object System.Net.CookieContainer
$srmSvcRef = New-Object SRM.ManagedObjectReference
$srmSvcRef.Type = "SrmServiceInstance"
$srmSvcRef.Value = $srmSvcRef.Type
$srmSvcContent = $srm.RetrieveContent($srmSvcRef)
$srm.SrmLoginLocale($srmSvcRef, $UserName, $Password, $null)
$srmObject = New-Object System.Object
$srmObject | Add-Member -Type NoteProperty -value $Server -Name SRMServer
$srmObject | Add-Member -Type NoteProperty -value $srm -Name SRMService
$srmObject | Add-Member -Type NoteProperty -value $srmSvcContent -Name SRMContent
...
I have tried using Savon, soap4r, and handsoap and I don't know what I'm missing.
Here is the Savon code that doesn't work.
require 'savon'
require 'rubygems'
client = Savon.client do
wsdl "https://srm-vcenter-a:8095/srm?wsdl"
#endpoint "https://srm-vcenter-a:8095/srm-Service?wsdl"
endpoint "http://srm5-vcenter-a:9007"
namespace "https://srm-vcenter-a/sdk/srm"
#proxy "https://srm-vcenter-a:8095/srm-Service?wsdl"
ssl_version :TLSv1
ssl_verify_mode :none
convert_request_keys_to :lower_camelcase
end
message = { username: 'administrator', password: 'mypw' }
response = client.call(:srm_login_locale, message: message)
Thanks in advance for all help
Having taken a brief look at your code it appears that in PowerShell you have
$srm.SrmLoginLocale($srmSvcRef, $UserName, $Password, $null)
While in Ruby you have
message = { username: 'administrator', password: 'mypw' }
response = client.call(:srm_login_locale, message: message)
In the PowerShell code there are 4 values passed to the SrmLoginLocale method:
$srmSvcRef
$UserName
$Password
$null
In the ruby code you are missing the first parameter and the fourth parameter in your call. Try creating the equivalent of $srmSvcRef in ruby. I am not a ruby programmer but I think the code would look something like:
srm_svc_ref = { value: 'SrmServiceInstance', type: 'SrmServiceInstance'}
message = { _this: srm_svc_ref, username: 'administrator', password: 'mypw', locale: nil}
response = client.call(:srm_login_locale, message: message)

How to implement a password change check in Powershell?

I've created a set of virtual machines (Windows Server) with a specific admin password; these VMs have been assigned to users, and may be in use. I want to know if the user changed the admin password, and do the check so the user doesn't notice. What are good solutions in powershell?
You could create a PSCredential, then attempt to get a WmiObject from the host. Something like:
$computerNames = "host1", "host2"
$pw = ConvertTo-SecureString "adminpw" -AsPlainText -Force
foreach($computerName in $computerNames)
{
$cred = New-Object System.Management.Automation.PSCredential("$computerName\Administrator", $pw)
try
{
Get-WmiObject win32_bios -ComputerName $computerName -Credential $cred
Write-Host "$computerName = Password not changed."
}
catch [System.UnauthorizedAccessException]
{
Write-Host "$computerName = Password changed."
}
}

Resources