Home » How does curl protect a password from appearing in ps output?

How does curl protect a password from appearing in ps output?

Solutons:


When the kernel executes a process, it copies the command line arguments to read-write memory belonging to the process (on the stack, at least on Linux). The process can write to that memory like any other memory. When ps displays the argument, it reads back whatever is stored at that particular address in the process’s memory. Most programs keep the original arguments, but it’s possible to change them. The POSIX description of ps states that

It is unspecified whether the string represented is a version of the argument list as it was passed to the command when it started, or is a version of the arguments as they may have been modified by the application. Applications cannot depend on being able to modify their argument list and having that modification be reflected in the output of ps.

The reason this is mentioned is that most unix variants do reflect the change, but POSIX implementations on other types of operating systems may not.

This feature is of limited use because the process can’t make arbitrary changes. At the very least, the total length of the arguments cannot be increased, because the program can’t change the location where ps will fetch the arguments and can’t extend the area beyond its original size. The length can effectively be decreased by putting null bytes at the end, because arguments are C-style null-terminated strings (this is indistinguishable from having a bunch of empty arguments at the end).

If you really want to dig, you can look at the source of an open-source implementation. On Linux, the source of ps isn’t interesting, all you’ll see there is that it reads the command line arguments from the proc filesystem, in /proc/PID/cmdline. The code that generates the content of this file is in the kernel, in proc_pid_cmdline_read in fs/proc/base.c. The part of the process’s memory (accessed with access_remote_vm) goes from the address mm->arg_start to mm->arg_end; these addresses are recorded in the kernel when the process starts and can’t be changed afterwards.

Some daemons use this ability to reflect their status, e.g. they change their argv[1] to a string like starting or available or exiting. Many unix variants have a setproctitle function to do this. Some programs use this ability to hide confidential data. Note that this is of limited use since the command line arguments are visible while the process starts.

Most high-level languages copy the arguments to string objects and don’t give a way to modify the original storage. Here’s a C program that demonstrates this ability by changing argv elements directly.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
    int i;
    system("ps -p $PPID -o args=");
    for (i = 0; i < argc; i++)
    {
        memset(argv[i], '0' + (i % 10), strlen(argv[i]));
    }
    system("ps -p $PPID -o args=");
    return 0;
}

Sample output:

./a.out hello world
0000000 11111 22222

You can see argv modification in the curl source code. Curl defines a function cleanarg in src/tool_paramhlp.c which is used to change an argument to all spaces using memset. In src/tool_getparam.c this function is used a few times, e.g. by redacting the user password. Since the function is called from the parameter parsing, it happens early in a curl invocation, but dumping the command line before this happens will still show any passwords.

Since the arguments are stored in the process’s own memory, they cannot be changed from the outside except by using a debugger.

The other answers answer the question well in a general manner. To specifically answer “How is this effect achieved? Is it somewhere in the source code of curl?”:

In the argument parsing section of the curl source code, the -u option is handled as follows:

    case 'u':
      /* user:password  */
      GetStr(&config->userpwd, nextarg);
      cleanarg(nextarg);
      break;

And the cleanarg() function is defined as follows:

void cleanarg(char *str)
{
#ifdef HAVE_WRITABLE_ARGV
  /* now that GetStr has copied the contents of nextarg, wipe the next
   * argument out so that the username:password isn't displayed in the
   * system process list */
  if(str) {
    size_t len = strlen(str);
    memset(str, ' ', len);
  }
#else
  (void)str;
#endif
}

So we can explicitly see that the username:password argument in argv is overwritten with spaces, as described by the other answers.

A process can not only read its parameters but write them, too.

I am not a developer so I am not familiar with this stuff but it may be possible from the outside with an approach similar to the changing of environment parameters:

https://stackoverflow.com/questions/205064/is-there-a-way-to-change-another-processs-environment-variables

Related Solutions

Joining bash arguments into single string with spaces

[*] I believe that this does what you want. It will put all the arguments in one string, separated by spaces, with single quotes around all: str="'$*'" $* produces all the scripts arguments separated by the first character of $IFS which, by default, is a space....

AddTransient, AddScoped and AddSingleton Services Differences

TL;DR Transient objects are always different; a new instance is provided to every controller and every service. Scoped objects are the same within a request, but different across different requests. Singleton objects are the same for every object and every...

How to download package not install it with apt-get command?

Use --download-only: sudo apt-get install --download-only pppoe This will download pppoe and any dependencies you need, and place them in /var/cache/apt/archives. That way a subsequent apt-get install pppoe will be able to complete without any extra downloads....

What defines the maximum size for a command single argument?

Answers Definitely not a bug. The parameter which defines the maximum size for one argument is MAX_ARG_STRLEN. There is no documentation for this parameter other than the comments in binfmts.h: /* * These are the maximum length and maximum number of strings...

Bulk rename, change prefix

I'd say the simplest it to just use the rename command which is common on many Linux distributions. There are two common versions of this command so check its man page to find which one you have: ## rename from Perl (common in Debian systems -- Ubuntu, Mint,...

Output from ls has newlines but displays on a single line. Why?

When you pipe the output, ls acts differently. This fact is hidden away in the info documentation: If standard output is a terminal, the output is in columns (sorted vertically) and control characters are output as question marks; otherwise, the output is...

mv: Move file only if destination does not exist

mv -vn file1 file2. This command will do what you want. You can skip -v if you want. -v makes it verbose - mv will tell you that it moved file if it moves it(useful, since there is possibility that file will not be moved) -n moves only if file2 does not exist....

Is it possible to store and query JSON in SQLite?

SQLite 3.9 introduced a new extension (JSON1) that allows you to easily work with JSON data . Also, it introduced support for indexes on expressions, which (in my understanding) should allow you to define indexes on your JSON data as well. PostgreSQL has some...

Combining tail && journalctl

You could use: journalctl -u service-name -f -f, --follow Show only the most recent journal entries, and continuously print new entries as they are appended to the journal. Here I've added "service-name" to distinguish this answer from others; you substitute...

how can shellshock be exploited over SSH?

One example where this can be exploited is on servers with an authorized_keys forced command. When adding an entry to ~/.ssh/authorized_keys, you can prefix the line with command="foo" to force foo to be run any time that ssh public key is used. With this...

Why doesn’t the tilde (~) expand inside double quotes?

The reason, because inside double quotes, tilde ~ has no special meaning, it's treated as literal. POSIX defines Double-Quotes as: Enclosing characters in double-quotes ( "" ) shall preserve the literal value of all characters within the double-quotes, with the...

What is GNU Info for?

GNU Info was designed to offer documentation that was comprehensive, hyperlinked, and possible to output to multiple formats. Man pages were available, and they were great at providing printed output. However, they were designed such that each man page had a...

Set systemd service to execute after fstab mount

a CIFS network location is mounted via /etc/fstab to /mnt/ on boot-up. No, it is not. Get this right, and the rest falls into place naturally. The mount is handled by a (generated) systemd mount unit that will be named something like mnt-wibble.mount. You can...

Merge two video clips into one, placing them next to each other

To be honest, using the accepted answer resulted in a lot of dropped frames for me. However, using the hstack filter_complex produced perfectly fluid output: ffmpeg -i left.mp4 -i right.mp4 -filter_complex hstack output.mp4 ffmpeg -i input1.mp4 -i input2.mp4...

How portable are /dev/stdin, /dev/stdout and /dev/stderr?

It's been available on Linux back into its prehistory. It is not POSIX, although many actual shells (including AT&T ksh and bash) will simulate it if it's not present in the OS; note that this simulation only works at the shell level (i.e. redirection or...

How can I increase the number of inodes in an ext4 filesystem?

It seems that you have a lot more files than normal expectation. I don't know whether there is a solution to change the inode table size dynamically. I'm afraid that you need to back-up your data, and create new filesystem, and restore your data. To create new...

Why doesn’t cp have a progress bar like wget?

The tradition in unix tools is to display messages only if something goes wrong. I think this is both for design and practical reasons. The design is intended to make it obvious when something goes wrong: you get an error message, and it's not drowned in...