Select Page

In Part 1 of our series of curl usage, we covered the fundamentals: headers, request methods, and basic data submission. Now we’ll look at some techniques that’ll make you look like a curl pro. We’ll be covering cookies, SSL certificate handling, and extracting only the data you need from responses.

These aren’t just “nice to know” features. During assessments, you’ll encounter session management that requires cookies, self-signed certificates that block your requests, and timing differences that reveal vulnerabilities. Let’s dive in.

Working with Cookies and Sessions

Most modern web applications use cookies for session management. Understanding how to capture, store, and replay cookies with curl is essential for testing authenticated endpoints.

Saving and Using Cookies: -c and -b

The -c flag saves cookies to a file, while -b loads them. Let’s see both of them in action. Recall from Part 1 that we can submit POST data with -d.

Log in and save the cookies to a file, cookies.txt:

curl -c cookies.txt \
     -d "username=admin&password=secret" "https://www.example.com/user-login"

We can then use those saved cookies in subsequent requests:

curl -b cookies.txt "https://www.example.com/admin/dashboard"

You can combine both flags to maintain a session across multiple requests. Login and save cookies with both -c and -b:

curl -c cookies.txt -b cookies.txt -d "username=admin&password=secret"  "https://www.example.com/user-login"

Then you can access a protected resource where cookies will be automatically updated:

curl -c cookies.txt -b cookies.txt "https://www.example.com/admin/users"

Using -c and -b together means each request reads existing cookies and updates the cookies.txt file with any new ones. You can manually modify the cookies.txt file as necessary, or switch to a new cookie jar, such as cookies2.txt. Here’s an example of what a cookie file looks like using GitHub as an example:

# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.

#HttpOnly_.github.com   TRUE    /       TRUE    1800544701      logged_in       no
.github.com             TRUE    /       TRUE    1800544701      _octo           GH1.1.19029...
#HttpOnly_github.com    FALSE   /       TRUE    0               _gh_sess        qRzAPbbU8E...

The cookies above have been shortened for readability. For a little more context, here’s a description of each field:

  1. The domain the cookie is valid for. #HttpOnly_ indicates that the HttpOnly flag is set.
  2. Indicates whether the “include subdomains” flag is set or not.
  3. The URL path the cookie is valid for.
  4. Indicates whether the “secure” flag is set or not.
  5. The Unix timestamp for when the cookie expires. A 0 means it’s a session cookie.
  6. The name of the cookie.
  7. The value of the cookie.

Sending Cookies Manually

Sometimes you don’t want a cookie jar. Maybe you’re testing a specific cookie value or you copied a cookie from your browser’s developer tools. Use the -b flag to send a specific cookie value:

curl -b "session_id=abc123def456" "https://www.example.com/admin/dashboard"

Or you can send multiple cookies by separating each one with a semicolon:

curl -b "session_id=abc123; user_role=admin" "https://www.example.com/admin/dashboard"

Handling SSL/TLS Certificates

During pentests, you’ll likely encounter self-signed certificates, expired certificates, and validation errors. Dealing with these certificates can be a pain without knowing the correct flags to set. Let’s start out with one of the most common issues you’ll see:

The Insecure Flag: -k/–insecure

Have you ever tried to use curl to look up a site that had a self-signed certificate?

curl "https://self-signed.local/admin"

If so, you’ve probably seen this message returned:

curl: (60) SSL certificate problem: self-signed certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the webpage mentioned above.

The -k flag skips certificate verification:

curl -k "https://self-signed.local/admin"

Important: This disables SSL/TLS security validation. Use it during testing, but document invalid certificates as they might indicate misconfigurations worth reporting.

Certificate Information: -v

See certificate details with verbose mode:

curl -v "https://www.example.com/" 2>&1 | grep -A 10 "SSL connection"

This shows TLS version, cipher suite, and certificate details. Adjust the 10 to see more or less lines after the match on “SSL connection”.

Since the curl that’s shipped with Windows uses Schannel (Windows’ native SSL/TLS library) instead of OpenSSL, the verbose output format is different. This means that the certificate information isn’t great. It is possible to install a Windows version of curl with OpenSSL, though that’s beyond the scope of this blog.

Extracting Information with -w/–write-out

The -w flag lets you extract specific information and format output exactly how you need it.

Basic Status Code Checking

curl -s -o /dev/null -w "%{http_code}" "https://www.attackd.com/"

This outputs just: 200

Breakdown:

  • -s: Silent mode
  • -o /dev/null: Discard response body
  • -w "%{http_code}": Write only the status code

Useful Variables

Curl has a ton of variables, but here are some useful ones when scripting and dealing with multiple sites.

# HTTP status code
-w "%{http_code}"

# Response time
-w "%{time_total}"

# Size of download
-w "%{size_download}"

# Final URL (after redirects)
-w "%{url_effective}"

# The remote IP address of the most recently used connection
-w "%{remote_ip}"

Custom Output Formats

You can use -w to put multiple variables together

curl -s -o /dev/null -w "Status: %{http_code}\nTime: %{time_total}s\nSize: %{size_download} bytes\n" "https://www.attackd.com/"

or if you’re using PowerShell:

curl.exe -s -o NUL -w "Status: %{http_code}\nTime: %{time_total}s\nSize: %{size_download} bytes\n" "https://www.attackd.com/"

Output:

Status: 200
Time: 0.482761s
Size: 198025 bytes

Bulk Status Checking

Test multiple endpoints:

for url in $(cat urls.txt); do
    curl -s -o /dev/null -w "$url - %{http_code}\n" "$url"
done

Or if you’re using PowerShell:

Get-Content urls.txt | ForEach-Object {
    curl.exe -s -o NUL -w "$_ - %{http_code}`n" "$_"
}

The output will look something like this:

https://www.attackd.com - 200
https://www.example.com - 200

Timing Attacks and Response Time Analysis

Timing differences can reveal vulnerabilities like SQL injection and user enumeration.

Basic Timing Measurement

Let’s take a look at how the time total variable works:

curl -s -w "\nTime: %{time_total}s\n" -o /dev/null -d "username=admin&password=wrong"  "https://www.example.com/user-login"

In Windows:

curl.exe -s -w "\nTime: %{time_total}s\n" -o NUL -d "username=admin&password=wrong" "https://www.example.com/user-login"

The output from these commands will look something like this:

Time: 0.217967s

Detecting Timing-Based SQL Injection

Now that we’ve got a time baseline from a regular request, we’ll take a look at a simple SQL injection attempt in these contrived examples. Since we’re sending a malformed request, we’ll need to use the --data-urlencode option, which will send a POST request by default. To specify a GET request, you’d need to use -G along with it.

Here’s what a POST request would look like in Linux:

curl -s -w "\nTime: %{time_total}s\n" -o /dev/null --data-urlencode "q=test' AND SLEEP(5)--" "https://www.example.com/search"

Here’s an example in Windows but as a GET request:

curl.exe -s -w "\nTime: %{time_total}s\n" -o NUL -G --data-urlencode "q=test' AND SLEEP(5)--" "https://www.example.com/search"

If these requests take ~5 seconds longer, you might have SQL injection.

User Enumeration via Timing

Some applications can take longer to process valid usernames:

Let’s try with an invalid username:

curl -w "Time: %{time_total}s\n" -o /dev/null -s -d "username=invalid_user&password=wrong"  "https://www.example.com/user-login"

And then we’ll use a valid username:

curl -w "Time: %{time_total}s\n" -o /dev/null -s "https://www.example.com/user-login" -d "username=valid_user&password=wrong"

The output might look like this:

Time: 0.110305s

Even a 50-100ms difference can help enumerate users.

Proxy Support

Curl allows you to route traffic through proxies for analysis.

HTTP Proxy

Send traffic through Burp Suite or another proxy:

Basic proxy use:

curl -x http://127.0.0.1:8080 "https://www.example.com/"

Proxy with authentication:

curl -x http://127.0.0.1:8080 -U user:pass "https://www.example.com/"

Environment Variables

Set proxy globally for all curl requests in your session:

export http_proxy="http://127.0.0.1:8080"
export https_proxy="http://127.0.0.1:8080"

Or in Windows:

$env:http_proxy = "http://127.0.0.1:8080"
$env:https_proxy = "http://127.0.0.1:8080"

Now all curl commands will use the proxy!

Quick Reference

New flags from Part 2:

  • -c file : Save cookies to file
  • -b file : Load cookies from file
  • -k : Skip SSL certificate verification
  • -w format : Custom output format
  • --data-urlencode "data=to_be_sent" : Send a POST request with URL-encoded data
  • -G --data-urlencode "data=to_be_sent" : Send a GET request with URL-encoded parameters
  • -x proxy : Use HTTP proxy

What’s Next

In Part 3, we’ll explore curl’s powerful globbing features for automated enumeration.

These techniques – managing sessions, handling certificates, timing analysis, and extracting precise data – are the building blocks of effective web application testing. Master them, and you’ll find yourself reaching for curl more often than you might expect.

Share This

Did you find this helpful?

Share it with your friends!