News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Upload Data via POST

Started by colinr, September 04, 2023, 10:39:09 PM

Previous topic - Next topic

colinr

Hi Guys,

Back again with more asm woes!

I'm trying to upload data by means of HTTP POST, I found a decent looking C++ example and have attempted to convert it to asm. It seems to sort of work, as I can detect that it's calling out as the handler on the web server is triggering and writing a test text file, however, no form data is present as nothing is returned when I read the form fields 'name' and 'filename'.

I've mimicked the 'file data' with the 'ABCD...' string.

Could it be something to do wit the boundaries?

Here is a link to the original C++ sample https://stackoverflow.com/questions/47878566/wininet-upload-file

Thanks in advance

.data

UserAgent    db    "Custom User Agent", 0h

lpszServerName    db    "www.<redacted>.com", 0h
PostRequest        db    "POST", 0h
PostURL            db    "/testabcd", 0h

szHeaders        db    "Content-Type: multipart/form-data; boundary=----974767299852498929531610575", 0h
szContent         db    "------974767299852498929531610575", 13,10, "Content-Disposition: form-data; name=", 22h, "file", 22h, "; filename=", 22h, "main.cpp", 22h, 13,10, "Content-Type: application/octet-stream", 13,10,13,10
MyData            db    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
szEndData         db     13,10, "------974767299852498929531610575--", 13,10,0h

.data?
hInstance        HINSTANCE ?

InternetOpenHandle    dd    ?
InternetConnectHandle    dd    ?
HttpOpenRequestHandle    dd    ?

.code

start:
    invoke     GetModuleHandle, NULL
    mov        hInstance,eax

    invoke    InitCommonControls
   
    invoke    InternetOpen, addr UserAgent, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0h
    mov        dword ptr[InternetOpenHandle], eax
   
    invoke    InternetConnect, InternetOpenHandle, addr lpszServerName, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0
    mov        dword ptr[InternetConnectHandle], eax
   
    invoke    HttpOpenRequest, InternetConnectHandle, addr PostRequest, addr PostURL, NULL, NULL, NULL, 0, 0
    mov        dword ptr[HttpOpenRequestHandle], eax
   
    invoke    HttpSendRequest, HttpOpenRequestHandle, addr szHeaders, -1, addr szContent, 208
   
Terminate:
   
    invoke    ExitProcess,0h
   
End start

And here is the simple page that is posted to.

<%@LANGUAGE="VBScript"%>
<%

For Each Item In Request.Form
    fieldName = Item
    fieldValue = Request.Form(Item)

    FData = FData & fieldName & " " & fieldValue   
Next

Set fso = Server.CreateObject("Scripting.FileSystemObject")
Set fs = fso.OpenTextFile(Server.MapPath("../testabcd.txt"), 8, true)
fs.Write(FData)
fs.Close
Set fso = Nothing

%>

A zero byte file is written.



BugCatcher

When you declare InternetOpenHandle, it is given an address by the assembler.
Using dword ptr[InternetOpenHandle],is a pointer to a pointer. Just use InternetOpenHandle.

zedd151

colinr, are you getting any error messages?
If yes, what exactly is the message?

colinr

Quote from: zedd151 on September 05, 2023, 02:39:37 AMcolinr, are you getting any error messages?
If yes, what exactly is the message?

Hi, the asm code seems to be working fine, no errors but i'll check again.

It's communicating with the server as I write a test file when the communication occurs. Just seems not to be passing the form data.

colinr


zedd151

Server side errors? Have you checked the logs? (Assuming you have access to the server.)
Possibly a permission, or security issue?

If the code works as you suggest, probably something wrong on the server side of things. That is way beyond my knowledge or skillset, sorry.

colinr

This is getting kind of frustrating.

There was an error server side, basically I was redirecting port 80 to 443 automatically, so if a request came in on 80 (HTTP), it uses HTTP 301 to permanently redirect to the HTTPS (443) site.

This causes the POST data to be dropped and the POST was converted to a GET.

I've managed to fix this issue as the requests are now appearing in the logs as POST and directly on port 443.

However, my test text file that is created when the URL in invoked is still void of data.

Are there any alternatives to WinInet that are straightforward to use as this is becoming quite tedious now?

jj2007

Quote from: colinr on September 04, 2023, 10:39:09 PMHere is a link to the original C++ sample https://stackoverflow.com/questions/47878566/wininet-upload-file

Have you checked the answer starting with "Your MIME boundary lines in szContent and szEndData are wrong"? There are quite a number of pitfalls listed.

colinr

Quote from: jj2007 on September 05, 2023, 06:40:12 AMHave you checked the answer starting with "Your MIME boundary lines in szContent and szEndData are wrong"? There are quite a number of pitfalls listed.

Yes, I think so.

I worked off the sample posted by the knowledgeable one, the boundaries *seem correct*.

I don't need to worry about the file being present as 'MyData' mimicks the file, which is hard coded for test purposes, the length of 208 bytes is also correct and there are no NULL bytes in the entire string apart from the terminator which is in the correct position.

I've stepped through the code with OllyDbg and handles are returned rather than errors.

To enable POST to appear in the logs rather than GET, I changed INTERNET_DEFAULT_HTTP_PORT to INTERNET_DEFAULT_HTTPS_PORT in the InternetConnect and added INTERNET_FLAG_SECURE to the dwFlags of HttpOpenRequest.

I also added some code to count the number of bytes for the form data rather than manually counting when I made a change.

colinr

Right, finally got this working in a round-about way. It turned out I did not need the multipart rubbish, so I got rid of it. That challenge is for another day, which is hopefully never.

I've added the working code should anyone require something similar.

.data

UserAgent    db    "CustomUserAgent", 0h

lpszServerName    db    "www.<redacted>.com", 0h
PostRequest        db    "POST", 0h
PostURL            db    "/testabcd", 0h

szHeaders        db    "Content-Type: application/x-www-form-urlencoded", 13, 10, 0h
szContent        db    "filename="
MyData            db    "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFao"
                db    "oFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1Z"
                db    "AqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ"
                db    "6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4T"
                db    "FLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHi"
                db    "rr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWL"
                db    "dwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=",0h

.data?
hInstance        HINSTANCE ?

InternetOpenHandle    dd    ?
InternetConnectHandle    dd    ?
HttpOpenRequestHandle    dd    ?

.code

;****************************************************************************************
; Calculate length of text buffer
;
; EDI = Buffer Pointer
; Returns length in EAX
;****************************************************************************************
ReturnBufferLength:
    xor        eax, eax
CountBufferLength:
    cmp        byte ptr[edi], 0h
    je        short EndOfBuffer
    inc        eax
    inc    edi
    jmp        short CountBufferLength
EndOfBuffer:
    ret

start:
    invoke    GetModuleHandle, NULL
    mov        hInstance,eax

    invoke    InitCommonControls
   
    invoke    InternetOpen, addr UserAgent, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0h
    mov        InternetOpenHandle, eax
   
    invoke    InternetConnect, InternetOpenHandle, addr lpszServerName, INTERNET_DEFAULT_HTTPS_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0
    mov        InternetConnectHandle, eax
   
    invoke    HttpOpenRequest, InternetConnectHandle, addr PostRequest, addr PostURL, NULL, NULL, NULL, INTERNET_FLAG_SECURE, 0

    mov        HttpOpenRequestHandle, eax
   
    lea        edi, szContent
    call    ReturnBufferLength
   
    invoke    HttpSendRequest, HttpOpenRequestHandle, addr szHeaders, -1, addr szContent, eax
   
    invoke    InternetCloseHandle, HttpOpenRequestHandle
    invoke    InternetCloseHandle, InternetConnectHandle
    invoke    InternetCloseHandle, InternetOpenHandle
   
Terminate:
   
    invoke    ExitProcess,0h
   
End start

Some points to note, lpszServerName cannot be prefixed with http or https, https is taken care of by 'INTERNET_DEFAULT_HTTPS_PORT' (INTERNET_DEFAULT_HTTP_PORT for non SSL) within the InternetConnect API call and 'INTERNET_FLAG_SECURE' (NULL for non-SSL) within the HttpOpenRequest API call.

Also it did not like being behind an Nginx reverse proxy, I had to change HTTP 301 Moved Permanently to HTTP 307 Temporary Redirect in the reverse proxy config.

My other IIS server automatically redirects from HTTP to HTTPS too which would cause a HTTP 301 to occur internally, this was mitigated by using 'INTERNET_DEFAULT_HTTPS_PORT' and 'INTERNET_FLAG_SECURE' as detailed above which causes the HTTPS version of the site to be called directly.

I've included the web server handler that picks up the POST request, it requires an IIS web server with ASP installed, parent paths should also be enabled to allow the file to be written outside of the web site root.

<%@LANGUAGE="VBScript"%>
<%
Dim fieldValue

fieldValue = Request.Form("filename")

Set fso = Server.CreateObject("Scripting.FileSystemObject")
Set fs = fso.OpenTextFile(Server.MapPath("../testabcd.txt"), 8, true)
fs.Write(fieldValue)
fs.Close
Set fso = Nothing

%>

There is a bit more to do on this yet, error handling and reading back the result of the POST request etc.

Another thing I've learned is that Base64 is not URL safe! I'd have expected that an encoding algorithm that was intented to move binary data accross networks would have been designed for *ALL* network use, including URL's. The + symbol is replaced by space in my environment, apparently the / and = can also be trashed.

Oh, by the way, the base64 data is nothing interesting, it was from a Google search for a pre-base64 encoded image.


jj2007

Compliments, colinr :thup:

Great work, I wonder if it should be moved to the Workshop, as it is far above the n00b level. I'd love to test it, but wouldn't know of a webpage or server that accepts uploads like that. Any ideas?

colinr

Quote from: jj2007 on September 05, 2023, 07:53:27 PMCompliments, colinr :thup:

Great work, I wonder if it should be moved to the Workshop, as it is far above the n00b level. I'd love to test it, but wouldn't know of a webpage or server that accepts uploads like that. Any ideas?

Just spin up Server 2019 / 2022 evaluation in a VM and add the IIS role with Classic ASP enabled. I think you can hit the server by it's local IP and it will serve the default site, which is good enough for local testing (replace 'www.<redacted>.com"' in asm code with the servers local IP) . If you remove the ../ from the path in the vbScript that I posted, it should write to the root of the web-site (not good practice for internet facing servers), that said you also need to grant full permissions to IUSR and IIS_IUSRS for the 'wwwroot' folder otherwise the server will throw error 500 if data is attempted to be written to disk.

Literally takes 5 mins to deploy IIS after Server 20xx is installed.

The sample vbScript code can be saved as 'testabcd.asp' and placed into to the 'wwwroot' folder which is located at 'C:\inetpub\wwwroot' after IIS is installed.

The asm code should also be edited to read '/testabcd.asp' rather than '/testabcd' as I use a URL rewrite rule to strip the .asp extension.

Classic ASP (vbScript) is nearly as ancient as asm, but it's good fun and does not require extra frameworks etc. to be installed, can certainly hold it's own for reasonably complex web-site back ends and if used correctly, is secure.

jj2007

Quote from: colinr on September 05, 2023, 08:26:54 PMJust spin up Server 2019 / 2022 evaluation in a VM and add the IIS role with Classic ASP enabled. I think you can hit the server by it's local IP...

You just confirmed that this is not Campus stuff :biggrin:

colinr

If anyone needs help with this stuff, more than happy to help.

Even though it's not asm related, might be worth having a section on how to set up a test web-server to allow people to develop asm code that can communicate with web-apps.

So very relevant actually.

Vortex

Hi colinr,

No need to setup a Windows Server. Here is a much more practical tool :

Http File Server

https://www.rejetto.com/hfs/