August Feng

Uploading a file with cURL

About

My tcpdump skills are still fresh. Let's go inspect some traffic when we upload a file with cURL.

Experiment

Let's host a TCP server with netcat:

  nc -l 1234

And then let's create a foobar file that contains some new lines to see how cURL will handle it .

  echo hello > foobar
  echo world >> foobar
  echo . >> foobar

The --data family of flags use application/x-www-form-urlencoded as Content-Type.

The --form family of flags use multipart/form-data as the Content-Type.

–data

Let's send the data over to our server using the --data flag.

  curl --data @foobar http://localhost:1234

The client will be waiting from an http response from the server, so just type HTTP/1.1 200 OK in netcat, and submit it entering a newline followed with the EOF character (Control-D on Linux/MacOS).

This is the HTTP request that will be sent to the server:

POST / HTTP/1.1
Host: localhost:1234
User-Agent: curl/8.7.1
Accept: */*
Content-Length: 11
Content-Type: application/x-www-form-urlencoded

helloworld.

The carriage returns and newlines are stripped out because the @ character performs special interpretation.

–data-urlencode

If we wanted to include the new lines, then we will need to use --data-urlencode instead to escape the newlines:

POST / HTTP/1.1
Host: localhost:1234
User-Agent: curl/8.7.1
Accept: */*
Content-Length: 20
Content-Type: application/x-www-form-urlencoded

hello%0Aworld%0A.%0A

There are four more syntaxes for specifying the content. We've already experimented with the @filename syntax.

Let's see name@filname now: curl --data-urlencode foobar@foobar http://localhost:1234.

POST / HTTP/1.1
Host: localhost:1234
User-Agent: curl/8.7.1
Accept: */*
Content-Length: 27
Content-Type: application/x-www-form-urlencoded

foobar=hello%0Aworld%0A.%0A

Let's see content now: curl --data-urlencode helloworld http://localhost:1234.

POST / HTTP/1.1
Host: localhost:1234
User-Agent: curl/8.7.1
Accept: */*
Content-Length: 10
Content-Type: application/x-www-form-urlencoded

helloworld

Let's see =content now: curl --data-urlencode =helloworld http://localhost:1234.

POST / HTTP/1.1
Host: localhost:1234
User-Agent: curl/8.7.1
Accept: */*
Content-Length: 10
Content-Type: application/x-www-form-urlencoded

helloworld

Let's see name=content now: curl --data-urlencode foobar=helloworld http://localhost:1234.

POST / HTTP/1.1
Host: localhost:1234
User-Agent: curl/8.7.1
Accept: */*
Content-Length: 17
Content-Type: application/x-www-form-urlencoded

foobar=helloworld

–form-string

Now let's experiment requests that use multipart/form-data as Content-Type.

The --form-string allows us to send strings as values.

  curl --form-string foobar=helloworld http://localhost:1234
POST / HTTP/1.1
Host: localhost:1234
User-Agent: curl/8.7.1
Accept: */*
Content-Length: 163
Content-Type: multipart/form-data; boundary=------------------------rq1DxrKk0eSy5XPZUjENER

--------------------------rq1DxrKk0eSy5XPZUjENER
Content-Disposition: form-data; name="foobar"

helloworld
--------------------------rq1DxrKk0eSy5XPZUjENER--

–form

The --form flag adds meaning to the @ and < when it prefixes the file.

The @ sign tells cURL to consider the file as a binary and includes the filename in the Content-Type field line.

  curl --form foo=@foobar http://localhost:1234
POST / HTTP/1.1
Host: localhost:1234
User-Agent: curl/8.7.1
Accept: */*
Content-Length: 223
Content-Type: multipart/form-data; boundary=------------------------T6bqePJBQp4FoNvv1De6Lw

--------------------------T6bqePJBQp4FoNvv1De6Lw
Content-Disposition: form-data; name="foo"; filename="foobar"
Content-Type: application/octet-stream

hello
world
.

--------------------------T6bqePJBQp4FoNvv1De6Lw--

The < sign tells curl to consider the file as a text and retrieves its content. You will see that there's no filename in the Content-Type field line.

Hint: Be sure to to quote the <name=content> to escape the < sign from the shell.

  curl --form "foo=<foobar" http://localhost:1234
POST / HTTP/1.1
Host: localhost:1234
User-Agent: curl/8.7.1
Accept: */*
Content-Length: 164
Content-Type: multipart/form-data; boundary=------------------------RxN8nUhV7pI3nC0pLRgjSi

--------------------------RxN8nUhV7pI3nC0pLRgjSi
Content-Disposition: form-data; name="foo"

hello
world
.

--------------------------RxN8nUhV7pI3nC0pLRgjSi--