Reproducing file upload to web-server through script

72 views Asked by At

I have a bunch of devices and I want to script an action to perform firmware upgrades on them, using powershell. I have captured the execution of the firmware upgrade with Chrome inspector and exported as PowerShell. This is the resulting code


$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$session.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.67"
Invoke-WebRequest -UseBasicParsing -Uri "https://192.168.1.10/do_upgrade.page" `
-Method "POST" `
-WebSession $session `
-Headers @{
"Accept"="text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
  "Accept-Encoding"="gzip, deflate, br"
  "Accept-Language"="en-US,en;q=0.9"
  "Cache-Control"="max-age=0"
  "Origin"="https://192.168.1.10"
  "Referer"="https://192.168.1.10/home.page"
  "Sec-Fetch-Dest"="iframe"
  "Sec-Fetch-Mode"="navigate"
  "Sec-Fetch-Site"="same-origin"
  "Sec-Fetch-User"="?1"
  "Upgrade-Insecure-Requests"="1"
  "sec-ch-ua"="`"Not.A/Brand`";v=`"8`", `"Chromium`";v=`"114`", `"Microsoft Edge`";v=`"114`""
  "sec-ch-ua-mobile"="?0"
  "sec-ch-ua-platform"="`"Windows`""
} `
-ContentType "multipart/form-data; boundary=----WebKitFormBoundaryYDA0ny9olNOKQs7B" `
-Body ([System.Text.Encoding]::UTF8.GetBytes("------WebKitFormBoundaryYDA0ny9olNOKQs7B$([char]13)$([char]10)Content-Disposition: form-data; name=`"firmware_upg`"; filename=`"XYZ.tar.gz`"$([char]13)$([char]10)Content-Type: application/x-gzip$([char]13)$([char]10)$([char]13)$([char]10)$([char]13)$([char]10)------WebKitFormBoundaryYDA0ny9olNOKQs7B$([char]13)$([char]10)Content-Disposition: form-data; name=`"session_id`"$([char]13)$([char]10)$([char]13)$([char]10)3e97239d-3cda-460f-963c-838f6ac794a7$([char]13)$([char]10)------WebKitFormBoundaryYDA0ny9olNOKQs7B$([char]13)$([char]10)Content-Disposition: form-data; name=`"session_username`"$([char]13)$([char]10)$([char]13)$([char]10)admin$([char]13)$([char]10)------WebKitFormBoundaryYDA0ny9olNOKQs7B--$([char]13)$([char]10)"))

If I try to reproduce this upload (of the same file) with PowerShell, the upload completes successfully, but the webserver rejects the upload stating the file is corrupt.

Is there something specific I'm missing? How can I encode the uploaded file in the same exact way as Chrome does it interactively from the browser? The uploaded file is close to 100MB, and the device takes about 4 minutes to accept the upload, so this is a very frustrating trial and error process.

Below is the powershell code I'm using to simulate this programatically for a single device


$firmwarefile = "C:\Users\administrator\Desktop\Firmware\XYZ.tar.gz"
$IPadd = "192.168.1.10"
$user = "admin"
$secret = "password"

$auth = Invoke-RestMethod -Uri "https://$ipadd/auth.page?session_username=$user&secret=$secret"
# auth.message returns the session ID which will be re-used later. Sessions are valid for 20 minutes
$sessionID = $auth.message


$uri = "https://$IPAdd/do_upgrade.page"
#$uri = "https://$IPAdd/do_upgrade.page?session_username=$user&session_id=$sessionID"

$boundary = "----WebKitFormBoundaryYDA0ny9olNOKQs7B"
$fileContent = Get-Content -Path $firmwarefile -Raw

$body = ("--$boundary$([char]13)$([char]10)Content-Disposition: form-data; name=`"firmware_upg`"; filename=`"XYZ.tar.gz`"$([char]13)$([char]10)Content-Type: application/x-gzip$([char]13)$([char]10)$([char]13)$([char]10)$fileContent$([char]13)$([char]10)--$boundary$([char]13)$([char]10)Content-Disposition: form-data; name=`"session_id`"$([char]13)$([char]10)$([char]13)$([char]10)$sessionID$([char]13)$([char]10)--$boundary$([char]13)$([char]10)Content-Disposition: form-data; name=`"session_username`"$([char]13)$([char]10)$([char]13)$([char]10)$user$([char]13)$([char]10)--$boundary--$([char]13)$([char]10)")
<#
$body = @"
--$boundary
Content-Disposition: form-data; name="firmware_upg"; filename="XYZ.tar.gz"
Content-Type: application/x-gzip

$fileContent
--$boundary
Content-Disposition: form-data; name="session_id"

$sessionID
--$boundary
Content-Disposition: form-data; name="session_username"

$user
--$boundary--
"@
#>

$actualContentLength = $body.Length
$actualContentLength

$invokeWebRequestParams = @{
    Uri              = $uri
    Method           = "Post"
    Body             = $body
    ContentType      = "multipart/form-data; boundary=$boundary"
    UseBasicParsing  = $true
    Headers          = @{
        "Content-Length"            = $actualContentLength
        "Content-Type"              = "multipart/form-data; boundary=$boundary"
        "Host"                      = "$IPadd"
        "User-Agent"                = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.67"
        "Accept"                    = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
        "Accept-Encoding"           = "gzip, deflate, br"
        "Accept-Language"           = "en-US,en;q=0.9"
        "Cache-Control"             = "max-age=0"
        "Origin"                    = "https://$ipadd"
        "Referer"                   = "https://$ipadd/home.page"
        "Sec-Fetch-Dest"            = "iframe"
        "Sec-Fetch-Mode"            = "navigate"
        "Sec-Fetch-Site"            = "same-origin"
        "Sec-Fetch-User"            = "?1"
        "Upgrade-Insecure-Requests" = "0"
        "sec-ch-ua-mobile"          = "?0"
        "sec-ch-ua-platform"        = "`"Windows`""  
        "sec-ch-ua"                 = "`"Chromium`";v=`"110`", `"Not A(Brand`";v=`"24`", `"Microsoft Edge`";v=`"110`""      
    }
}
Invoke-WebRequest @invokeWebRequestParams


I have tried several methods of encoding the uploaded file inside the multipart sections such as:

$fileContent = Get-Content -Path $firmwarefile -Raw
$fileContent = [System.IO.File]::ReadAllBytes($firmwarefile)
$fileContent = [System.Text.Encoding]::UTF8.GetString([System.IO.File]::ReadAllBytes($firmwarefile))

and of course I tried with the -InFile method

Also note that I have tried reconstructing the $body with two approaches (one of which is commented out at present)

to no avail....

All other interactions with the webserver through powershell code works perfectly using the above approach, such as reading settings from pages or changing settings in the interface and submitting, downloading files from the interface. It's just the upload that I cannot get to work

Please support me with some guidance on how to correctly encode the tar.gz file or any other obvious mistakes when I interact with a multipart form, to make the webserver accept the upload.

0

There are 0 answers