CVE-2020-8177: curl overwrite local file with -J
Medium
C
curl
Submitted None
Actions:
Reported by
snsn
Vulnerability Details
Technical details and impact analysis
## Summary:
curl supports the `Content-disposition` header, including the `filename=` option. By design, curl does not allow server-provided local file override by verifying that the `filename=` argument does not exist before opening it.
However, the implementation contains 2 minor logical bugs that allow a server to override an arbitrary local file (without path traversal) when running curl with specific command line args (-OJi)
This bug can trigger a logical RCE when curl is used from the user's home dir (or other specific directories), by overriding specific files (e.g. ".bashrc"), while keeping the user completely uninformed of the side effects.
The 2 bugs are:
1. `curl -iJ` is not supported however `curl -Ji` is available -
2. The standard `Content-disposition` handling flow does not allow opening existing files: https://github.com/curl/curl/blob/master/src/tool_cb_wrt.c#L54, however by using `-OJi` it is possible to reach a flow that overrides a local file with the response headers, without verification: https://github.com/curl/curl/blob/master/src/tool_cb_hdr.c#L196
## Steps To Reproduce:
1. Return the following http response form a server :
```
HTTP/1.1 200 OK
<PAYLOAD>
Content-disposition: attachment; filename=".bashrc"
```
Where `<PAYLOAD>` is the bash payload, e.g. `echo pwn`
2. Run `curl -OJi` from the user's home dir
**Note that curl falsely claims that `.bashrc` was refused to be overwritten.**
## Supporting Material/References:
First bug:
```c
case 'i':
config->show_headers = toggle; /* show the headers as well in the
general output stream */
break;
...
case 'J': /* --remote-header-name */
if(config->show_headers) {
warnf(global,
"--include and --remote-header-name cannot be combined.\n");
return PARAM_BAD_USE;
}
config->content_disposition = toggle;
break;
```
Second bug:
```c
if(filename) {
if(outs->stream) {
int rc;
/* already opened and possibly written to */
if(outs->fopened)
fclose(outs->stream);
outs->stream = NULL;
/* rename the initial file name to the new file name */
rc = rename(outs->filename, filename);
if(rc != 0) {
warnf(per->config->global, "Failed to rename %s -> %s: %s\n",
outs->filename, filename, strerror(errno));
}
if(outs->alloc_filename)
Curl_safefree(outs->filename);
if(rc != 0) {
free(filename);
return failure;
}
}
```
## Impact
Local file override without path traversal, possibly leading to an RCE or loss of data.
Report Details
Additional information and metadata
State
Closed
Substate
Resolved
Bounty
$700.00
Submitted
Weakness
Improper Input Validation